目录
- 1. 预处理的使用
- 2. #define
- 3. #define 宏定义的使用
- 4. 宏参数的连接
- 7. const的使用
- 8. const与#define的特点与区别
- 9. C++中const有什么作用
- 10. static有什么作用
- 11. static全局变量与普通全局变量有什么区别
- 12. C++中类的静态成员
- 13. sizeof计算普通变量所占空间大小
- 14. sizeof计算 类对象 / 结构体变量 所占空间大小(字节对齐)
- 15. sizeof计算含有虚函数的类对象的空间大小
- 16. sizeof计算"虚继承"的类对象的空间大小
- 17. sizeof与strlen的区别
- 18. sizeof有哪些用途
- 19. 使用strlen()函数代替sizeof计算字符串长度
- 20. sizeof计算联合体的大小
- 21. #pragma pack的作用
- 22. 为什么要引入内联函数(inline)
- 23. 为什么inline能很好的取代宏函数
- 24. 内联函数的使用场合
- 25. 为什么不把所有的函数都定义为内联函数
- 26. 内联函数和宏定义的区别
《C和C++程序员面试秘笈》-董珊山海编著
1. 预处理的使用
#ifdef XXX
#else
#endif
2. #define
1、三目运算符( ? : ),能产生比if-else更优化的代码,并且书写上更加简洁明了
2、用#define定义的宏,如果是替换为数值类型,需要把参数括起来;如果只是简单的文本替换,如果不注意,使用时很容易引起歧义
3. #define 宏定义的使用
宏定义展开是在预处理时期,也就是在编译之前
如果参数需要加括号,加括号时,要加齐全
4. 宏参数的连接
1、使用#把宏参数变为一个字符串
#define STR(s) #s
STR(s)定义的是一个参数s表示的字符串
当调用时,如:STR(vck),实际表示就是字符串“vck”
2、使用##把两个宏参数贴合在一起
#define CONS(a,b) (int)(a##e##b)
CONS(a,b)定义的是一个将参数a b按aeb连接起来的一个整型值
当调用时,如:CONS(2,3),实际表示的就是整型值2e3,也就是十进制数2000
5.略
6.略
7. const的使用
常量指针 const修饰的是指针指向的内容,指针指向的内容为常量,不能改,指针指向可以改
const int* PointerToInt
指针常量 const修饰的是指针,指针指向不能更改,指针指向的内容可以改
int* const PointerToInt
8. const与#define的特点与区别
#define 只是用来做文本替换的
const常量 有数据类型,编译器可以对其进行类型安全检查
9. C++中const有什么作用
1、const定义常量,编译器可以对const常量进行数据静态类型安全检查
2、const修饰函数形参,参数为用户自定义类型或者抽象数据类型时,“值传递”改为“const & 传递”,可以提高效率
3、const修饰函数的返回值
4、const修饰类的成员函数(函数的()后加const),任何不会修改数据成员的成员函数都应用const修饰
10. static有什么作用
1、全局的static变量(静态变量),可以被本文件内所有函数访问,不能被其他文件访问,被限制为一个本地的全局变量
2、局部的static变量(函数体内),只能被本函数调用
11. static全局变量与普通全局变量有什么区别
1、
static全局变量:只初始化一次;只能在定义该变量的源文件内使用
普通全局变量:可被一个程序的各个源文件使用
2、
static局部变量:只初始化一次,下一次依据上一次的结果值
3、
static函数:在内存中只存在一份
普通函数:在每个被调用中维持一份复制品
12. C++中类的静态成员
类的静态成员、静态方法不属于类的实例,而是属于类本身并在类的实例间共享;不占用类实例的空间(其中静态成员变量存在于静态存储区)。
在调用它们时应该用"类名::静态方法()"
13. sizeof计算普通变量所占空间大小
数组和指针的sizeof运算有细微的区别
对“数组变量(数组名)”sizeof运算得到的是数组占内存的总大小
如果“数组变量(数组名)”被传入函数中做sizeof运算,则和指针的sizeof运算没有区别
对于指针,无论何种类型的指针,其大小都是固定的;在32位WinNT平台下都是4
14. sizeof计算 类对象 / 结构体变量 所占空间大小(字节对齐)
C++的内存对齐方式取决于
1、取 编译器默认对齐大小 与 对象中最大成员 两者中较小的一个
2、现阶段,编译器默认对齐大小为8
一般而言满足3个准则
1、结构体变量的首地址,能被其成员中最宽基本类型大小所整除
2、结构体中每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍;
如有需要,编译器会在成员之间加上填充字节(internal adding)
3、结构体的总大小为结构体成员中最宽基本类型大小的整数倍;
如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)
15. sizeof计算含有虚函数的类对象的空间大小
普通函数不占用类对象的内存
类中只要有虚函数,就会占用一个指针大小的内存
原因是:系统多用了一个指针维护这个类的虚函数表
并且注意,类中无论含有几个虚函数,都不会影响类的大小(因为只有一个虚函数指针,这个虚函数指针用来维护虚函数表)
16. sizeof计算"虚继承"的类对象的空间大小
虚继承时,编译器会为子类安插一个指向父类的指针
一个虚继承,子类里有一个指向父类的指针
多个虚继承,子类里有多个指向父类的指针(一一对应)
17. sizeof与strlen的区别
sizeof
是操作符--计算所占内存空间大小 "\0"计算在内
结果类型为size_t,即unsigned int类型
类型做参数时,必须加“()”;变量名做参数时,可以不加“()”
数组名直接传递给sizeof不退化为指针
sizeof()可以用来定义数组维数
sizeof计算字符串数组时,结果为字符串数组实际占用的字节数,"\0"计算在内
strlen
是函数--计算字符串的长度 "\0"不计算在内
只能用“char *”做参数,且必须以“\0”结尾
数组名直接传递给strlen退化为指针
strlen计算字符串数组时,结果为字符串实际占用的字节数,"\0"不计算在内
如果要计算指针指向的字符串的长度,则一定要使用strlen
char * ss = "0123456789";
int a = sizeof(ss);//4
int b = strlen(ss);//10
18. sizeof有哪些用途
1、查看某个类型的对象在内存中所占的字节数
2、动态分配对象的内存时,让系统知道要分配多少字节
3、涉及操作数的字节大小时,用sizeof代替常量计算
19. 使用strlen()函数代替sizeof计算字符串长度
1、sizeof()不能计算字符串长度
2、strlen()函数用于计算字符串长度
20. sizeof计算联合体的大小
union、struct、class内存对齐方式相同
union的大小
1、成员中占用空间最大的一个成员的大小
2、如果需要,进行内存对齐
#pragma pack(x)可以改变编译器的默认对齐大小
C++的内存对齐方式取决于
1、取 编译器默认对齐大小 与 对象中最大成员 两者中较小的一个
2、现阶段,编译器默认对齐大小为8
21. #pragma pack的作用
#pragma pack(x)可以改变编译器的默认对齐大小
22. 为什么要引入内联函数(inline)
宏函数
仅仅只是做预处理器符号表中的简单替换
不能进行参数有效性的检测
不能享受C++编译器严格类型检查的好处
返回值不能被强制转换为合适类型
因此,它的使用就存在着一系列的隐患和局限性
C++中引入了类及类的访问控制
涉及类的保护成员或私有成员时,就无法使用这种宏定义来实现(因为无法将this指针放在合适的位置)
inline推出的目的
正是为了取代宏函数
消除了它的缺点,同时又很好的继承了它的优点
23. 为什么inline能很好的取代宏函数
inline定义的内联函数
1、函数的代码被放入符号表中,在使用时直接进行替换(像宏一样展开),没有了调用的开销,效率也很高
2、是一个真正的函数,编译器在调用一个内联函数时,首先会检查参数的类型,然后进行一些列的相关检查
3、可以作为某个类的成员函数,这样就可以使用类的保护成员及私有成员
24. 内联函数的使用场合
1、inline函数可以完全替代宏函数
2、类中的数据成员,一般被定义为私有的或者保护的
把读写数据成员的成员函数定义为inline函数,将会获得比较好的效率
3、类中
成员函数的定义被放在类声明中-->自动建议编译器把它编译成inline函数
定义被放在类声明之外,那么要加上inline关键字,才会建议编译器把它编译为成内联函数
25. 为什么不把所有的函数都定义为内联函数
内联函数是以代码膨胀(复制)为代价的,仅仅省去了函数调用的开销;使程序的总代码量增大,消耗更多的内存空间
如果执行函数体内代码的时间相比于函数调用的开销较大,那么效率的收获会很少
以下情况不建议使用内联函数
1、函数体代码较长,使用内联函数将导致内存消耗代价较高
2、函数体内出现循环,执行函数体内代码的时间要比函数调用的开销大
3、类的构造函数和析构函数容易让人误解成使用内联更有效。
要当心构造函数和析构函数可能会隐藏一些行为,比如“偷偷地”执行了基类或成员对象的构造函数和析构函数
所以不要随便地将构造函数和析构函数的定义体放在类声明中!
一个好的编译器将根据函数的定义体,自动取消不值得的内联(这说明了inline不应该出现在函数的声明中)
26. 内联函数和宏定义的区别
1、内联函数在编译时展开,宏在预编译时展开
2、在编译时,内联函数可以直接被镶嵌到目标代码中,而宏只是一个简单的文本替换
3、内联函数可以完成诸如类型检测、语句是否正确等编译功能,宏函数就不具备这样的功能
3、宏函数不是函数,inline函数是函数
4、宏函数在定义时要小心处理宏参数(一般情况是把参数用括号括起来),否则容易出现二义性。而内联函数定义时不会出现二义性