目前看到第8章。都是个人的心得,可能有错误或者理解不到位的地方。
1.变量的声明与赋值 写法
int a=1;int a(1);int a={1};int a{1};int a={};int a{};
最后两个语句中a赋值为0。
{}为initialization list,可用于数组的declaration and definition。
2.不同进制的标识
10进制(decimal)第一个bit为0-9;
8进制(octal)第一个bit为0,第二个bit为0-7;
16进制(hexadecimal)前两个bit为0x或0X。
分别以cout<<dec/hex/oct;改变输出进制,对剩余的输出都有效。
3.数字常量的存储
cout<<1000;
默认存为int。加l或L后缀指定为long:
cout<<1000L;
u为unsigned long,同理有ul、ull等。
八进制、十六进制的数按int,u int,l,ul,ll,ull的优先顺序存放。
4.转义字符
\n newline
\t 水平制表符
\v 垂直制表符
\b 回车符
\r carriage return
\a alert
5.扩展字符集 ISO 10646
\u后跟8个十六进制bit,\U后跟16个十六进制bit。
如ö为\u00F6
â为\u00E2
wchar_t用于更复杂的字符集,占2bytes,加L标识,用wcout函数:
wchar_t bob = L’P’;wcout << L"tall" << endl;
C++11引入了char16_t和char32_t,分别占16bits和32bits,加u和U标识。
+5.37E+16
一个科学记数法实例
E/e皆可,e后可为+/-,为指数的符号;为+可省略。
6.setf()函数
C++中cout.setf(ios::left,ios::adjustfield); cout.setf(ios::showpoint,ios::showpint);
cout.setf(ios::scientific,ios::floatfield);
分别表示什么
参考以下:
1.使用控制符控制输出格式
控制符 作用
dec 设置整数的基数为10
hex 设置整数的基数为16
oct 设置整数的基数为8
setbase(n) 设置整数的基数为n(n只能是16,10,8之一)
setfill© 设置填充字符c,c可以是字符常量或字符变量
setprecision(n) 设置实数的精度为n位.在以一般十进制小数形式输出时,n代表有效数字.在以fixed(固定小数位数)形式和scientific(指数)形式输出时,n为小数位数.
setw(n) 设置字段宽度为n位.
setiosflags(ios::fixed) 设置浮点数以固定的小数位数显示.
setiosflags(ios::scientific) 设置浮点数以科学计数法(即指数形式)显示.
setiosflags(ios::left) 输出数据左对齐.
setiosflags(ios::right) 输出数据右对齐.
setiosflags(ios::shipws) 忽略前导的空格.
setiosflags(ios::uppercase) 在以科学计数法输出E和十六进制输出字母X时,以大写表示.
setiosflags(ios::showpos) 输出正数时,给出“+”号.
resetiosflags 终止已设置的输出格式状态,在括号中应指定内容.
2.用流对象的成员控制输出格式
流成员函数 与之作用相同的控制符 作用
precision(n) setprecision(n) 设置实数的精度为n位.
width(n) setw(n) 设置字段宽度为n位.
fill© setfill© 设置填充字符c.
setf( ) setiosflags( ) 设置输出格式状态,括号中应给出格式状态,内容与控制符setiosflags括号中内容相同.
ubsetf( ) resetiosflags( ) 终止已设置的输出格式状态.
cout.width(10);
cout.setf(ios::hex);
3.设置格式状态的格式标志
格式标志 作用
ios::left 输出数据在本域宽范围内左对齐
ios::right 输出数据在本域宽范围内右对齐
ios::internal 数值的符号位在域宽内左对齐,数值右对齐,中间由填充字符填充
ios::dec 设置整数的基数为10
ios::oct 设置整数的基数为8
ios::hex 设置整数的基数为16
ios::showbase 强制输出整数的基数(八进制以0打头,十六进制以0x打头)
ios::showpoint 强制输出浮点数的小点和尾数0
ios::uppercase 在以科学计数法输出E和十六进制输出字母X时,以大写表示
ios::showpos 输出正数时,给出“+”号.
ios::scientific 设置浮点数以科学计数法(即指数形式)显示
ios::fixed 设置浮点数以固定的小数位数显示
ios::unitbuf 每次输出后刷新所有流
ios::stdio 每次输出后清除stdout,stderr
————————————————
版权声明:本文为CSDN博主「荪荪」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/smf0504/article/details/51469300
7.浮点数标识
后缀f/F表示float,l/L表示long double。默认为double。
小的浮点数赋给整数时截取整数部分而非四舍五入
8.类型转换
short a=1;short b=2;short c=a+b;
a+b先转换为int,再换成short。
表达式转换规则:有ld都转换成ld;有d、f同理。然后是int。
强制类型转换:short a;int (a)或(int) a;不会改变a的值,而是创造一个新的值,可用于表达式中。int a=1.2+2.9;中,先转换成double得4.1,再赋值。
auto关键词可自动分配类型。如auto a=1;a将定义为int。
9.static_cast<>和dynamic_cast<>
static_cast a;将a强制转换为int类型。详见后续
10.数组声明
int a[3]={1,2,3};
int a[3]{1,2};(array a中剩余元素初始化为0,可以快速全部初始化为0。11标准中可省略=)
int a[]{1,2,3};(自动识别a大小为3)
int a[3]{};(全部初始化为0。11标准中可用)
initialization list只能在definition时使用,不可如下:
a[3]={1,2,3};
有些老compiler报错可如下写:
static int a[3]{1};
int a[3]={1,2,3.0};不允许narrowing,错误
char a[3]={112,’a’,’b’};允许,112是int但在char范围内
int a=1;int b[2]{a,2};允许
char类型array要注意留出\0的位置。
“s”为string类型,实际上为s和\0,char a=”s”实际上是把s字符串的地址赋给a,错误
11.strlen()返回字符串的长度(不含\0,若字符数组,非实际长度;初始化之前使用,结果不确定)
sizeof()返回实际长度,也不含\0。
char a[10]{“abc”};strlen(a)=3;sizeof(a)=10;
char array用cin输入以whitespace(table,space,newline)结尾。用cin.get()/cin.getline()时以换行符结尾,前者使换行符留在输入流中,后者丢弃换行符。
cin.getline(参数1,参数2),参数1为接收字符串的数组名,参数2为最大接收数,如为20时最多接收19个字符(加\0需一个字符)。参数3后续讨论。
cin.get(参数1,参数2)形式与getline一样。连续两个cin.get()使第二个get()无法接收任何输入。解决办法是在中间再加入一cin.get(),因cin.get(void)可读取任一字符,读取换行符后丢弃。也可写成cin.get(参数1,参数2).get(),因为cin.get(参数1,参数2)返回一个cin对象,可再次调用get()。同理,cin.getline(参数1,参数2).getline(参数1,参数2)也能达到一样的效果。
get()读取empty line(newline)后会设置一个failbit,后续输入将被blocked。可使用cin.clear()。
超出读取限制后,get()和getline()都会将剩余输入留在输入流中,但getline()会设置failbit并关闭输入流。另外,cin读取数字后也会将换行符留在输入流中。由于cin>>int返回cin对象,可使用(cin>>year).get()解决。
12.string对象相比char array有不少优点。
可以直接赋值:str1=str2;str1={“123abc”};
可以相加、增长:str3=str1=str2;str2+=str1;
C中提供了strcpy(str1,str2)、strcat(str2,str1)来对string赋值、附加。str1.size()替代了strlen(),并且初始化str1之前str1.size()为0。为了避免strcpy和cat使用时str1比str2短,可用strncpy()和strncat()并引入第三个代表上限值的参数,如strncpy(a,b,3);但若b比a长,该函数不会自动加\0,后面需要跟a[3]=’\0’;。使用getline(cin,str1)可使string读取space和table。
raw string:加R后,不需转义字符。若输出有”,需加parenthesis。
cout << R"(Jim “King” Tutt uses “\n” instead of endl.)" << ‘\n’;可将括号中的内容输出。
若要输出括号,可使用cout << R"+("(Who wouldn’t?)", she whispered.)+" << endl;其中"+(和)+" 相匹配。R还可与L、U使用来表达wchar_t和char32_t等。R必须大写。
13.结构体
先声明结构体的格式,再声明结构体变量(可外部声明):
struct human
{
double weight;
int age;
char name[20];
};//结构体的格式
human xiaoming;//xiaoming是一个结构体变量,有weight、age、name等成员。
用.运算符操作数据成员,如cin>>xiaoming.name。声明结构体变量时可使用初始化链表(initialization list),可以不初始化完所有成员,可用xiaoming{}的格式使成员全部初始化为0。结构体变量可以做函数的参数或返回值,可以用赋值符号。声明结构体和声明结构体变量可连用如下:
struct human
{
double weight;
int age;
char name[20];
}xiaoming1,xiaoming2
{
60.0,
20
};
还可以省略human(结构体的tag),只声明结构体变量。
C++的结构体变量中的成员还能有函数。
结构体变量较多时,可创造结构体数组。如human xiaoming[100];其可初始化如下:
human xiaoming[100]
{
{60.0,20,”xiaoming”},
{120.0,40,”xiaomingming”},
{130.0}
};
结构体中可使用bit fields来节省空间:
struct human
{
double weight;
int age:8;
char name[20];
};
其中指定age只占8bits,可以表示2^8即-128到127范围,足够表示age了。
但是由于内存对齐原则,位域多用于成员单一的结构体,如:
struct time
{
int year:14,//0-9999年
month:4,//12个月
day:5;//31天
};//此结构体的一个变量大小为4bytes,因为内存对齐
可用sizeof显示结构体变量的大小。
14.union类型
声明和结构体一样,大小为其中最大成员的大小。可用来存放各种值,但一次只能存一个值,实质是不同的基本类型共用一段内存空间:
union a
{
int int_value;
double double_value;
char char_value;
};
a可用于存放int,double或char,长度为8bytes。
union主要目的是节省空间。具体用法可如下:
struct time
{
int year;
int month;
union UNION
{
double day1;
int day2;
}day;
}today;
那么就可以调用today.day.day1。也可以省略union的tag和变量名,直接调用:
struct time
{
int year;
int month;
union
{
double day1;
int day2;
};
}today;
此时调用today.day1
15.枚举变量
格式:enum spectrum {red, orange, yellow, green, blue, violet, indigo, ultraviolet};
定义枚举类型spectrum ,成员有red、orange等。默认red=0,orange=1依次累计。若在中间赋值,如:
enum spectrum {red, orange, yellow=9, green, blue, violet, indigo, ultraviolet};
那么red=0,orange=1,但从blue开始,在yellow的基础上累计,后面blue=10,violet=11等。
spectrum band;
声明一个枚举变量后,它的值只能在枚举的几个数里选。
band=bule可以,band=2就不行(int不会自动转换为enum)。实际上,赋值符号可用于枚举类型,其他符号如++、+不行:
band=blue+red;是不行的,虽然先转换为int后得出结果,但int不能自动转换为enum,需强制类型转换:
band=spectrum(3);
定义枚举类型时tag也可省略。enum可用于switch语句。
enum类型的范围:
enum color{red,blue=100,yellow};
上界:100,比100大又最接近100的2的次方是128,则上界为127;
下界:最小值大于等于0时都为0;小于0时,和上界类似,如red=-6,则找到-8,下界为-7。
上下界主要用于确定存储enum需要的bit数,也和节省空间有关。同时,一个在范围内的数都可赋值给enum变量(需强制类型转换):
color color_of_sky;
color_of_sky=color(102);//此时color_of_sky为120
16.指针
int *a,b;a=&b;//定义int指针a和int类型变量b,并将b取位赋给a
int b;int *a=&b;//定义的同时赋值
指针初始化后,系统在内存中为指针指定一块存储空间,但并未给其指向的值指定空间。如下的语句:
int *a;*a=1;
a指向的位置不确定,强行用1覆盖a指向的内存很可能导致error。
系统通常将指针像int一样处理,但直接将一个int赋给指针是不行的(C99之前可以),需要强制类型转换:
int *a;a=0xB8000000;//错误
int *a;a=(int *)0xB8000000;//可行
在C中可用malloc()的memory allocate函数分配内存,C++中可用new和delete关键词动态管理内存。这样得到的变量没有变量名,只能通过指针访问。
int *p=new int;//得到一个int
int *p=new int[x];//得到一个int数组。其中x可以是表达式、变量、常量。
每new一个指针,必须在使用完后delete之:
delete p;
delete []p;
一个new对用一个delete,一个delete也只能对应一个new。不能将一个地址delete两次,也不能对不是用new定义的指针delete。delete后,指针名可用于下一次new。对空指针delete可以。
int a=1;int *p1=&a;int *p2=p1;delete p2;//p1和p2指向同一个地址,delete一次就够了。过程中p1和p2进行了浅拷贝。
指定动态数组后需要编程者操心使用中有无过界。用sizeof无法得出动态数组的大小。系统知道动态数组的大小,但对编程者不可用。
对动态数组的指针名+1会使其移向下一个元素(不是下一个byte或bit),如:
int p=new int[10];p=p+1;//此时再输出p[0]就是原来的p[1]。在delete之前应将p减1。
可以像使用普通数组名一样使用动态数组的指针名,C++使用数组就是通过指针完成的。
int a[3];int p=a;//和int p=&a[0];等价,p[1]和(p+1)等价,a实际上就是&a[0],数组名就是指针,也可以当作指针使用。但不能像p=p+1一样a=a+1,也不能用sizeof§求a数组的大小。其实a等价于&a[0],而&a是整个数组的指针,所以(a+1)是数组第二个元素,而&a+1是在a[0]的地址上加整个数组的bytes数,本例中为12bytes,跳过了整个数组。
int a[3];int p1=a;int (p2)[5]=&a;
上面的p1是a[0]的地址,即a或&a[0],类型是int ;而p2的类型是int()[5],即含有5个元素的int数组的指针,在这里a有3个元素,显然不匹配。如果不加括号:int p2[5];则定义了一个指针数组,该数组的成员都是指针,每一个成员可指向一个int类型。
两个指针相减得到一个int,即两个元素间相差多少个元素,只有同一数组内的元素地址相减有意义。
char a[10]=”abc”;cout<<a<<”123”;实际上只把a[0]的地址和’1’的地址传给cout,cout在遇到\0后自动停止输出。和int不同,可以有char b=”123”;的语句,因为”123”为字符串常量,在内存中有地址,但由于b存放了常量的地址、常量不能修改,最好在前面加const以防修改。要输出&a[0]可以cout<<(double)a或cout<<(int)a,即转换为非char类型。cout<<&a[0]仍然会输出整个字符串,因为cout遇到一个char类型就会当作string输出。
浅拷贝与深拷贝:前面说到,
Int a;int *p1=&a;int *p2=p1;并不会创造新的地址,p1和p2都指向同一个int,操作其中一个就会改变a的值。要达到深拷贝,需要创造新的地址。比如:
char a[4]=”abc”;char *p=new char[strlen(a)+1];strcpy(p,a);//此处不能直接p=a,而要对a数组中的值一一拷贝。此时指针a和指针p中储存的值相同,但地址不同,可以独立操作。
用指针声明一个结构体后,可以用->操作符访问成员,或者(*).如下:
struct human{int age;double weight;}a;
human *b=&a;//然后可b->age或(*b).age来访问数据成员。
自动分配、静态分配、动态分配:
一个block中声明的变量会自动分配一个内存位置,并在block结束后收回;如果返回该变量的地址给外部,它随时可能被其他值覆盖;一个block就是{}内的部分,比如for循环中定义的局部变量就不能在外面用,一个函数中的变量也不能给其他函数用。实际上,执行时,变量的内存在一个stack中,类似放羽毛球的球筒,按顺序创建,block执行完后按与创建时相反的顺序销毁(栈只能在尾部操作)。在block内部重新定义外部已有的变量,会使得该外部变量“隐形”,只能用内部定义的同名变量。
静态分配的变量,在程序执行时始终存在。可以在所有函数外部声明变量,或者用static关键词:static int a;。有些老编译环境不允许数组和结构体自动分配,这时也要static声明。
通过new动态分配的变量则超出了程序的生命周期,程序结束后仍然存在。所以必须用好delete,不然此区域将持续不可用,因为程序结束后指针丢了。
17.vector和array类的使用
vector<类型名> 对象名(个数)
如vector temp(n);其中n必须是整型变量、常量或表达式,意义为创造了一个有n个成员的整型vector类对象temp。vector可以看成数组的替代,头文件vector。
array类包含在array头文件里,用法:array<int,n> temp;,其中n不能是变量。
C++11后,vector和array类的对象可使用链表来定义并赋值。vector是使用new创建的,内存地址在heap里;array类对象在自动分配区,元素个数相同的array对象可用=赋值。一般的数组、vector类对象、array类对象都不会检查下标是否超出范围,后两者可用at()函数代替下标操作,at()函数会舍弃超出范围的下标。
18.表达式的值
赋值语句的值为左边的变量值,如x=1,整个表达式的值为1;比较表达式的值为bool型。
19.cout.setf(ios::boolalpha);可设置bool型输出true和false。
20.a++:值为后加;++a:值为先加。C++不会保证局部表达式执行完后就立即进行里面的++操作,y=(1+x++)+(2+x++);的值为6(x=1),y=1+x++的值为2。对于内置类型,前置、后置的效率差不多;对于类,前置是用类的方法操作,更快;后置是将值取出、+1再放回原位置。
21.,可在for循环头部使用。i=20,y=120;整个表达式的值为右边的值,即120。,的优先级最低,如i=20,120;会使i=20;i=(20,120);则使i=120。
22.strcmp()比较两个字符串,相同返回0。前者比后者小返回负数,否则返回正数。大小按照ASCII顺序。只要比较的一方是string类对象,即可直接用比较操作符达到相同的效果。string类的对象不用\0来标志结尾,而是其他技术。
23.在程序内延迟时间:
ctime头文件包含了clock()函数和CLOCKS_PER_SEC,前者可返回程序运行到当前经过的系统时间(根据系统不同,可为long、unsigned long或其他类型),为了保证程序可移植性,可用clock_t统一指代clock()函数返回的变量,称之为别名,或者用auto;CLOCKS_PER_SEC为系统中标准的1秒。为了达到延迟一定时间的效果,可如下操作:
int a=10;
clock_t time_delay=a*CLOCKS_PER_SEC;//或者auto time_delay=…
clock_t start=clock();
while(clock()-start<time_delay)
;
这样即可延迟a秒。a也可定义为float、double。
如何自己定义别名:
可以:#define byte char//编译器将把byte替换为char
可以用C++新增的:typedef char byte;//同上
但#define中,如果类型是指针,进行替换后,若有多个变量同时定义,就会出现:
char *a,b;//可以看到,b实际上被定义为了char类型
这种情况只能用typedef。
24.C++11新增的range-based循环:
例1:double a[]{1,2,3};for(double x:a) cout<<x<<endl;
例2:double a[]{1,2,3};for(double &x:a) x=x*0.1;
例3:for(double x:{1,2,3}) cout<<x<<endl;
例1可展示a数组的所有变量;2可通过&操作符对a数组进行操作;3中可在for中定义数组。
25.EOF:
EOF是命令行时代的遗产。cin检测到EOF时,会生成eofbit和failbit并设置值为1。cin.eof()在EOF检测到时返回true;cin.fail()在eofbit或failbit之一设为1时返回true,即到达EOF或读取失败。两者都反映了上一次读入尝试,fail()比eof()在不同的环境更有通用性。在windows命令行环境中ctrl+z可模拟eof输入(然后再次按enter使得输入从缓冲区进入程序)。cin遇到EOF后会设置flag并不再读取输入,cin.clear()可以清除cin的flag并再次读取。
26.cin类提供了函数,能将cin等istream类的对象转换为bool型。所以while(cin)可以检测是否成功输入,替代25中用EOF检测的方法。并且while(cin)还能检测其他导致输入失败的情况,如磁盘错误。由于cin.get()返回的也是cin,所以可改写为while(cin.get(char)),一开始就输入一个char。
ch=cin.get()的写法是可以的,但记住get()返回的是一个cin对象。
cin.get()遇到EOF时返回一个特殊值,一般为-1(没有其他ASCII码为-1)。不需要知道具体值,可以用EOF代表:ch=cin.get();while(ch!=EOF)…但有些环境ch为unsigned char不可能为-1,需要转换ch为int
cout.put()可展示一个字符,接收的参数为char。有些环境中其有3种同名函数,可cout.put(char(int))来指定。
cin.get(ch)和ch=cin.get()的区别:前者返回istream类的对象,正常输入时转换为bool为1,遇到EOF时为0;后者将返回值赋值给ch,ch的类型为int,遇到EOF时ch被赋值EOF。但两者都应该用于字符的输入而非数字。
27.小技巧:写赋值时可以写变量=常量,写条件判断时写常量==变量。这样,若条件判断误写为常量=变量,编译器可立即给出错误。
28.逻辑运算符:
||先算左边再算右边,若左边为真则不执行右边。
&&同样,先左后右,若左边为假则不执行右边。
<的连写是从左至右的。如1<i<3等于(1<i)❤️。1<i可能为0或1,整个式子始终为1。
!的优先级较高,&&和||较低,&&优先级比||高。
对于没有这些符号的编译环境,可用or,and,not替代。
29.C++的cctpye(或ctype.h)头文件中包含了一些处理字符的标准函数。如isalpha(ch)在ch为字母时返回非0值;ispunct(ch)则在ch为标点时返回非0值。同样,isspace()检测是否空格、制表符、换行符,isdigit()检测单个字符是否数字。在有些字符编码不同的环境,字母的顺序中a-z可能不连续,这时需要用isalpha()。下面包含一些cctype中的函数:
Isalnum()检测是否字母或数字;isalpha()检测是否字母;isblank()空格或水平制表符;iscntrl()控制字符;isdigit()数字;ifgraph()可打印字符(除空格);islower()小写字母;isprint()可打印字符(包括空格);ispunct()标点符号;isspace()空格、进纸键、换行、回车、水平制表符、垂直制表符;isupper()大写字母;isxdigit()0-9或A-F或a-f;tolower()返回一个大写字母的小写字母,若非大写字母则返回原值;toupper()同理。
30.三字运算符:
表达式1?表达式2:表达式3
若表达式1为真,整个表达式的值为表达式2;否则为3。
31.switch的label只能为整型常量,通常用int、char、enum。switch总是比if分支结构效率更高。
32.continue对for循环的作用是直接跳到update expression。
33.goto语句:
设置一个标签,比如abc:
然后应用goto abc;就可跳至该标签的地方继续执行。
34.cin对数字的特性:
int n;cin>>n;如果输入一个字符,会发生:
n的值不变,错误的输入留在输入流中;cin对象设置一个error flag,并且call to cin method, if converted to type bool, returns false.
double等也同理。
35.文件读写:
包含fstream头文件;先定义对象;和cin、cout一样。
读取完后要fin.close()、fout.close()(最好在前面加fin.clear()、fout.clear())。
判断是否读取成功:
If(!fin.is_open()) exit(EXIT_FAILURE);
is_open()函数在文件读取失败时返回0;exit()函数包含在cstdlib中,该命令可与操作系统交流、终止程序。若编译器较老,可用good()方法,但它考虑的情况不如is_open()全面。
36.C++函数原型中不想写实参表可以在括号中用…代替。如void func1(…);
C++中参数与函数实参表中不同,编译器可自动转换类型。
argument:实参,被呼叫函数中的参数;parameter:形参,呼叫函数中的参数
只有在函数声明和原型的参数表中,int a[]和int *a才是等价的。
将数组传给被呼叫函数后便不能用sizeof()求数组长度了,若用到应一并传入。或者也可将数组最后一个元素的指针传入,但一般使用最后一个元素再后一个地址的指针。
将指针传给其他函数时,为了保护原数据,可在定义函数时加const,如void func1(const int *a)
37.const的用法:
int A;const int *a=&A;//不能通过a来修改A,但可有A++;,也可改变a的值;int * const a;则不能改变a指向的地址。
const int A;const int *a=&A;可以,但const int A;int *a=&A;不行(为了防止通过指针修改A值)。也不能通过二级指针来绕过这种限制:
const int a;int *p1;const int **pp2;这时不能有pp2=&p1;。
以上总结:可以把常量/非常量数据的地址赋给常量指针(除非这个数据自己就是指针),但不能通过常量指针来修改非常量的值;常量数据的地址只能赋给常量指针。
常量数据传递给函数时在函数声明与定义中也要有const。
38.二维数组的传参:
设想以下语句:
int a[3][4]={…};int total=sum(a,3);
那么函数声明应该这样写:
int sum(int (array)[4],int size)或者int sum(int array[][4],int size)
int total=sum(a,3);中a代表有四个元素的数组,是指向有4个元素的int数组的指针。
这里的sum函数只能用于有四列的二维数组。实际上,二维数组可作为二级指针对待。array[i][j]等于(*(array+i)+j)
39.结构体做参数传递时就是结构体(不是指针),被调用的函数会做出一份copy而不动原数据(就像int、double做参数)。为了提高效率,可以使用结构体指针做实参。也可以使用C++的新方式(下文提到)。
40.用array类做参数时函数原型写:
void func1(array<int,4> *p);
可以用指针。注意array也可以用作结构体或类的数组。
41.C++不允许main()函数自我调用,C允许。
42.函数也有指针。一般可以用来写同名函数。去掉函数后的括号即函数的指针。注意区别:
FUNCTION(func());
FUNCTION(func);
要声明一个函数指针,格式如下:
double func1(int n);
那么double (*pf)(int n);//double *pf(int n);意为函数pf返回一个double *类型
pf指向一种函数,其返回值为double,实参为一个int类型。
还可以声明并初始化函数指针数组:double (*pf[3])(int n)={f1,f2,f3};//f1,f2,f3是三个函数
或者用auto:auto pf=f1;
auto pf2=pf;//这时pf2也成为和pf一样的函数指针数组
但auto不能用于链表初始化:auto pf2[3]={f1,f2,f3};//错误
现在假设一个函数func原型就是:double func(int n);,这时可pf=func;来将func函数的地址传给函数指针。一个调用此种函数的函数原型应该是:
void FUNCTION(int n,double (*p)(int n));
这样,就可以通过FUNCTION函数调用func函数了:
FUNCTION(123,pf);或FUNCTION(123,func);
pf在使用时可以用(*pf)的形式,C++也允许直接用pf:(*pf)(5)等价于pf(5)。
还可以用typedef来定义函数指针:
void func(int);//函数原型
typedef void (*p_to_func)(int);//只需要将函数名替换为类型名
p_to_func p1;//p1就是指向该类型函数的指针
p_to_func p_array[10];//还可以方便地定义函数指针数组
43.内联函数:
普通函数的地址在其他地方,执行时跳到函数地址再跳回原函数,可能有时间损耗;内联函数在编译时在每次call的地方用函数代码替代了call,形成一种嵌套结构,增大了内存消耗但节约了时间。在declaration或definition时前面加inline即可成为内联函数,但编译器不一定会执行,比如递归函数不能是内联函数,或者函数太大。一般把只有一两行的函数做成内联函数。inline double square(double x){return x*x;}