C/C++ 查漏补缺

自己在学习与使用C/C++的过程中,遇到的一些问题。

c++:

  1. 域操作符“::”可用于直接操作全局变量。
  2. 对于类对象a,前缀式(++a)返回对象的引用,是一个左值;后缀式(a++)返回对象的值,是一个右值,所以需要对对象进行拷贝,产生较大的开销,效率较低。
  3. #pragma pack(a) 的作用:将编译器的对齐方式设置为a。每个固有类型的对齐方式取自身大小和编译器对齐方式中较小的一个。编译器默认对齐方式为8。
  4. 有哪些情况只能使用初始化列表而不能在构造函数中进行赋值?
    (1)const类型和引用类型成员变量。
    (2)需要使用基类的非默认构造函数对基类部分进行初始化。
  5. 若类定义了任何一种构造函数,则编译器不会合成一个默认构造函数。若类没有定义复制构造函数,即使类还定义了其他的构造函数,编译器仍然有可能合成一个复制构造函数。
  6. 若函数以值传递的形式返回一个类类型。分三种情况:
    (1)不使用返回值:则使用拷贝构造函数生成一个临时对象,之后被简单丢弃。
    (2)返回值用来赋值:则使用拷贝构造函数生成一个临时对象,之后再使用赋值函数为被赋值的对象赋值。
    (3)返回值用来初始化:则直接使用拷贝构造函数对新的对象进行初始化。
  7. 若参数以值传递的方式,生成临时对象使用的构造函数根据传入的实参的不同而不同,可以是拷贝构造函数,也可以是其他带参数的构造函数。
  8. (理论上)编译器隐式地将在类内定义的成员函数当作内联函数。
  9. 强制类型转换运算符”(type)”与其他一元运算符具有相同的优先级,且它们的结合性是从右至左。”()”比”(type)”的优先级要高,它们是不一样的!

c:

  1. signed和unsigned:
    (1)当表达式中存在有符号和无符号类型时,所有的操作数都自动转换成无符号类型。
    (2)有符号类型是符号扩展(高位补符号位),无符号类型是逻辑扩展(高位补0)。
    (3)有符号类型的移位操作取决于编译器(gcc下是算术移位),无符号类型是逻辑移位。
  2. sizeof是一个操作符,在编译时即可确定结果值的大小。
    char str[20] = “01234567890”; // sizeof(str)的值为20,是整个字符数组分配的空间大小
  3. int a[5]; // a == &a[0]
  4. char *p = “abc”; //“abc”是字符串常量,存储在数据段,对它进行修改会造成运行时错误(segmentation fault)。
  5. const char *p 与 char const *p 等价,”const”都在”*“的左侧,表示指针指向的内容是常量。如果”const”在”*“的右侧,表示指针本身是常量。
  6. struct结构体变量可以直接赋值。
  7. char是signed还是unsigned取决于编译器的实现。
  8. strcat(char *dest, char *src); // 注意dest指向的字符串的结束位置,即’\0’出现的位置。
  9. printf和scanf中的格式转化说明与压入参数的大小,printf中”%f”对应的float类型参数会自动转化为double类型参数。
  10. 操作数的自动转换:
    (1)当某个算数运算符有一个无符号类型操作数和一个有符号类型操作数时,则在开始运算之前有符号类型操作数会自动转换为无符号类型。
    (2)当某个算数运算符有一个浮点型操作数和一个整型操作数时,则在开始运算之前整型操作数会自动转换为浮点型。
  11. 关于volatile:
    (1)首先强调一点,volatile和cache没有关系。因为cache对于编译器或者程序员来说是透明的,至于如何维护cache的一致性(多线程),或者使cpu的某些物理地址空间是不可缓存的(I/O),这都是cpu的事情,与编译器和volatile无关。
    (2)指令集提供给编译器和程序员的接口中,编译器所能操纵的资源只有统一的物理地址空间和“单核”cpu的寄存器。volatile的作用是提醒编译器不要根据它所修饰的变量作出相应优化,原因是可能有“别的地方”会对这个变量进行读写。这里的别的地方包括——多线程、信号处理函数、内存映射I/O等等。所以说编译器需要在满足这个条件的前提下进行编译。于是编译器通常会像下面这样做:
    *不把这些变量放进寄存器,否则多线程会访问到不同cpu的寄存器,内存映射I/O则完全无法对cpu的寄存器进行读写。
    *不将某些循环随便优化成“只判断一次变量而进入死循环的形式”,而是“在每次进入循环体后都会去判断这个变量”。