c/c++ 面试 (更新中)
1. 对标准头文件中的一些通用结构的理解
1 #ifndef __INCvxWorksh 2 #define __INCvxWorksh 3 #ifdef __cplusplus 4 extern "C" { 5 #endif 6 /*...*/ 7 #ifdef __cplusplus 8 } 9 #endif 10 #endif /*__INCvxWorksh*/
代码第1、2、10行的作用是 防止该头文件被重复应用
代码第3行的作用是 表示当前使用的是C++编译器(C编译器的表示方式是 __STDC__)
代码第4-8行中 extern “C” 是 C++编译器提供的与C连接交换指定的符号,用来解决名字匹配问题
2.头文件引用中<>和" "的区别
<>表示这个文件是一个工程或者是标准头文件,查找过程会首先检查预定义的目录
" "表示该文件是用户提供的头文件,查找文件时会从当前文目录下寻找文件,然后在标准位置寻找文件
3. atexit()函数的使用
atexit()的函数原型:其参数是一个函数指针
int atexit(void (*)(void));
atexit()函数是来注册程序正常终止时需要调用的函数,并且在main()函数结束时,调用这些函数的顺序预注册它们的顺序相反。
4. 前缀自增运算和后缀自增运算的效率
内建数据类型的情况,效率没有区别
自定义的数据类型 前缀自增运算的效率更高,因为前缀(++i)可以返回对象的引用,而后缀(i++)必须返回对象的值,这将产生较大的复制开销,效率降低
5. 三元运算(? :)
三元运算比if-else 效率更高
6.
1 #define STR(s) #s 2 #define CONS(a,b) int(a##e##b)
使用#把宏参数变成一个字符串,用##把两个宏参数连接在一起
7. 指针常量和常量指针
1 #include <stdio.h> 2 3 int main() 4 { 5 const int x = 1 //定义x为常量 6 int b = 10; 7 int c = 20; 8 9 const int* a1 = &b; 10 int* const a2 = &b; 11 const int* const a3 =&b; 12 13 x = 2; 14 15 a1 = &c; 16 *a1 = 1; 17 18 a2 = &c; 19 *a2 = 1; 20 21 a3 = &c; 22 *a3 = 1; 23 24 return 0; 25 26 }
第13行代码 由于x为整型常量,因此不能改变x的值
第15 16行代码 a1为常量指针,即指针所指向的值不能改变,但指针可以改变
第18 19行代码 a2为指针常量,即指针所指向的地址不能发生改变,但是指针所指向的内容可以改变
第21 22行代码 a3为上述两种情况的综合,即指针和其所指向的内容都不能发生改变
综上所述,第13,16,18,21,22 行代码编译时会出现错误。
8. const和宏定义#define的区别
const是一个Run-Time概念,存在于程序的数据段,并在堆栈中分配空间,const常量有数据类型
#define是一个Compile-Time概念,存在于程序的代码段,实际程序中只是一个常量,并没有实际的存在,且没有数据类型
9. const在C++中的作用
用于定义常量:const定义的常量编辑器会对其进行数据静态类型的安全检查
修饰函数形式参数: 当输入参数为用户自定义类型和抽象数据类型时,应该将 值传递 改为 “类型 const & 值” ,这样可以提高效率。
修饰函数的返回值:当给指针传递的函数返回值前加 const ,则返回值不能直接被修改,且返回值必须赋值给const修饰的同类指针
1 const char *getChar(void){}; 2 const char *ch = getChar(); //correct 3 char *ch = getChar(); //error
修饰类的成员函数:任何不会修改数据成员的函数都应该使用const修饰
1 int geiCount(void) const;
10. static在C++中的作用
声明静态变量,使其值保持不变
在模块内,一个被声明的静态变量可以被模块内的所有函数访问,是一个本地的全局变量,但是不能被模块外的其他函数访问。
在模块内,一个被声明的静态函数可以被模块内的其他函数访问,只能在本地使用。
11. static全局变量和普通全局变量的区别,static局部变量和普通局部变量的区别,static函数与普通函数的区别。
静态全局变量和全局变量在存储方式上是一样的,都为静态存储方式,为这两者的区别在于:如果源程序由多个文件组成,在一个文件定义的全局变量可以有同源程序下的不同文件访问,即非静态的全局变量在各个源文件都是有效的;而静态全部变量只有在第一该变量的源文件内有效,且只能被初始化一次。
静态局部变量和非静态局部变量的存储方式不一样,静态局部变量只能被初始化一次,下一次的值依据上一次的结果值。
静态函数和非静态函数的作用域不同,并且在内存中只有一份,普通静态函数在每个被调用的中维持一份复制品。
12. 无论何种类型的指针,在WinNT平台下都是4个字节
13. 字节对齐
对齐的作用与原因:由于各个硬件平台在存储空间上的不同,有些平台只能从特定的地址处开始存取,但有的平台则没有这种情况,若果不按照平台的指定要求对数据进行存取,会带来效率的损失。
对齐的准则:
1) 结构体的首地址能够被其最宽的数据类型成员大小整出。
2)结构体中的每个数据成员相对于结构体首地址的偏移量都是该成员大小的整数倍,如果不是整数倍,补齐,
3)结构体的总大小为结构体中最宽数据类型成员大小的整数倍,如果不是整数倍,补齐,
eg:
1 struct S 2 { 3 char c1; 4 int i; 5 char c2; 6 };
c1的偏移量是0,i的偏移量是1,不是int类型的整数倍,填充3个字节,,所以总的字节加起来为:1+3+4+1 = 9,不是最宽数据成员int的整数倍,在填充3个字节,所以,该结构体的字节总数为12.
14.含有虚函数的类对象的空间大小
1 class A 2 { 3 public: 4 A(int x) : a(x){} 5 virtual void print() 6 { 7 cout<< "A" <<endl; 8 } 9 private: 10 int a; 11 };
1 A a(1) 2 cout << "size of A obj is" << sizeof(a) << endl;
size of A obj is 8
普通函数不占内存,只要有虚函数,就会占用一个指针大小的内存,原因是系统多用了一个指针维护这个类的虚函数表,并且注意这个虚函数无论含有多少项(类中有多少个虚函数)都不会在影响类的大小。
15. 虚拟继承的类对象的空间大小
1 class D:virtual public A 2 { 3 4 }; 5 class E:virtual public A, virtual pubic B 6 { 7 8 }; 9 int main() 10 { 11 cout << "sizeof(D) = " << sizeof(D) <<endl; 12 cout << "sizeof(E) = " << sizeof(E) <<endl; 13 return 0; 14 }
sizeof(D) = 4
sizeof(E) = 8
类D是虚拟继承于A,编译器会为该类安插一个指向父类的指针,指针大小为4,有了该指针将不会再安插一个char给空类。
类E是虚继承A和虚继承B,因此它有指向父类A的指针与父类B的指针,所以总空间大小为8字节。
16. sizeof与strlen 有哪些区别
1. sizeof 是操作符,strlen是函数。
2. sizeof操作符类型结果是size_t,在头文件中的typedef为unsigned类型,该类型能保证能容纳实现所有建立的最大对象的字节大小。
3. sizeof可以用类型做参数,strlen只能用char *做参数,且必须是以“\0”结尾的。
4. 数组做sizeof的参数不能退化,传递给strlen就可以退化为指针。
5. 大部分程序编译时,sizeof就被计算过了,而strlen的结果在运行的时候才能计算出来,
6. sizeof后如果是类型,必须加括弧,如果是变量名,可以不加括号,这是因为sizeof是个操作符,而不是个函数
17. 对于复合数据类型而言,如union、structural、class等,其对齐方式为成员中数据类型最大的成员的整数倍
18. #pragma pack(x)
该函数的作用是修改cpu的对齐方式的,C++固有编译类型的对齐方式取编译器对齐与自身大小较小的一个。例如,指定编译器按2对齐,int类型的大小为4,则对齐方式取2和4中较小的2。
19. 内联函数相比于宏定义的优点
内联函数直接存放在符号表中,使用时直接调用,不产生调用的开销。
内联函数是真正的函数,消除了隐患和局限性
内联函数可以作为成员函数
区别:
内联函数在编译时展开,宏定义在预编译时展开
内联函数在调用的时候直接镶嵌在代码中,而宏定义只是简单的文本替换
内联函数可以完成诸如 类型检测 、判断语法是否正确等
内联函数是函数,而宏定义不是函数
宏定义时小心参数出现二义性(必要的部分用括号括起来),而内联函数则不会出现二义性
内联函数的缺点:
内联函数是牺牲空间换时间,即以代码的复制/膨胀为代价,省去了程序调用函数的开销,以下情况不适于声明为内联函数:
1)代码量较长,使用内敛时内存消耗过大
2)内联函数中有循环,这样执行时间要比调用的时间开销大。
20. 引用
1)引用在声明的时候就要初始化
2)引用只能在声明的时候赋值,以后也不能给该引用赋值
21. c++中struct 和 class 的区别
如果在没有标明成员变量和成员函数的访问权限级别时,struct默认的访问权限级别是public,而class默认的访问权限级别是private
22. c#中struct 和 class的区别
如果在没有标明成员变量和成员函数的访问权限级别时,struct和class的访问权限都是private,区别在于: struct定义的是值类型,值类型的实例在栈上分配内存,而class定义的类型是引用类型,引用类型
的实例内存分配是在堆上分配
23. 引用/指针注意
若 &a = b 如果b的生命周期比a的生命周期短,那么会报错;
对于常量类的变量,其引用也必须是常量类型的,而对于非常量类型的变量,其引用可以是常量类型的,也可以是非常量
eg:
1 int a; 2 const int b; 3 const int &c = a; √ 4 int &d = b; x
24. 指针和引用的区别
1) 引用需要创建的同时初始化,而指针无需创建完就直接初始化,
2) 引用一旦初始化完毕,会一直指向同一个对象,以后将不能修改,而指针可以可以指向其他对象, 总的来说,引用会比较安全
3) 引用不能指向null,而指针可以哦
4) 由于引用不会指向空值,意味着在使用之前不需要测试,而指针经常指向空值,所以使用之前需要测试,因此,使用引用的效率比指针的效率高
注:在语法层面上,引用的用法跟对象一样,在二进制层面,引用一般都是由指针实现的,只不过在编译过程中,编译器帮我们完成了转换
25 解读复杂指针声明
原理--->右手原理:首先从最里面圆括号开始(其实是:从第一个未定义的标识符开始),然后往右看,再往左看,每当遇到圆括号时跳出,循环反复。
eg: int (*(*f)[5]) (int * p)
找到未声明的标识符f,往右看是一个圆括号,往左看是一个*,表明f是一个指针,再往右看是一个【】运算符,表明f是指向数组的指针,再往左看,是一个*,表明数组元素时5个指针类型,在跳出括号,右边又是一个括号,说明这个数组元素是指向函数的指针;
总结一下:f是一个指向数组的指针,数组元素为函数指针,这些指针指向形参为int*类型,返回值为int的函数,
26 域名系统DNS 用来解析 机器域名和IP地址
27 五种基本关系代数运算:并(∪)、差(-)、笛卡尔积(x)、投影(σ)、选择(π)
关系运算符:投影(σ)、选择(π)、连接(▷◁)、除(÷)
集合运算符:并(∪),差(-),交(∩),笛卡尔积(×)
28 垂直型微指令的特点:采用微操作码
29 类中的非静态函数默认是有this指针的,表明为该类的对象所有,静态函数不属于任何类的对象,没有this指针,由类直接调用
30 scanf("%s",a) 或者 gets(a)
浙公网安备 33010602011771号