一、C中printf计算参数时是从右到左压栈的。参见p34.
二、p35 类型转化(int&)a (其中 float a=1.0f)
其实,(int&)a就是*(int*)(&a)
- 首先对float型变量取地址
- 强制类型转换为整型变量的地址(地址的值并没有变)
- 将该地址指向的变量输出(但是由于整型和浮点型数据存储方式的不同,输出结果是不同的)
书上的解释是: (int&)a 相当于将该浮点数地址开始的sizeof(int)个字节当成int型的数据输出,因此取决于float型数据在内存中的存储格式,而不是经过(int&)a显示转换的结果(1)。
三、p40
unsigned char a=0xA5;
unsigned char b=~a>>4+1;
printf("b=%d",b);//250
由于~的优先级高于“>>”,“+”,同时“+”优先级高于">>",所以直接移位5位。~a操作时,会对a进行整型提升,a是无符号的。提升是左边补0,取反后左边为1,右移,再按照无符号读取,从而会有250.
四、在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明? p43
答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。
五、头文件中ifdef/define/endif是干什么用的?
答:头文件中~是条件编译的一种,除了头文件被防止重复引用,还可以防止重复定义(变量、宏或者结构)。
六、const
如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。
const int *a =&b;//情况1 这种情况可以先不进行初始化,因为虽然指针内容是常量,但指针本身不是常量。
int const * a =&b;//情况2(情况1、2 相同)
int *const a= &b;//情况3 定义时必须同时初始化
const 成员函数:常常称为常成员函数,可以理解为一个“只读”函数,它既不能更改数据成员的值,也不能调用那些能引起数据成员值变化的成员函数。
七、
-
mutable关键字
在C++中,mutable是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,甚至结构体变量或者类对象为const,其mutable成员也可以被修改。
mutable在类中只能够修饰非静态数据成员。mutable 数据成员的使用看上去像是骗术,因为它能够使const函数修改对象的数据成员。然而,明智地使用 mutable 关键字可以提高代码质量,因为它能够让你向用 户隐藏实现细节,而无须使用不确定的东西。我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。
这里出现了令人纠结的3个问题:
1、为什么要保护类的成员变量不被修改?
2、为什么用const保护了成员变量,还要再定义一个mutable关键字来突破const的封锁线?
3、到底有没有必要使用const 和 mutable这两个关键字?
保护类的成员变量不在成员函数中被修改,是为了保证模型的逻辑正确,通过用const关键字来避免在函数中错误的修改了类对象的状态。并且在所有使用该成员函数的地方都可以更准确的预测到使用该成员函数的带来的影响。而mutable则是为了能突破const的封锁线,让类的一些次要的或者是辅助性的成员变量随时可以被更改。没有使用const和mutable关键字当然没有错,const和mutable关键字只是给了建模工具更多的设计约束和设计灵活性,而且程序员也可以把更多的逻辑检查问题交给编译器和建模工具去做,从而减轻程序员的负担。
-
volatile
像const一样,volatile是一个类型修饰符。volatile修饰的数据,编译器不可对其进行执行期寄存于寄存器的优化。这种特性,是为了满足多线程同步、中断、硬件编程等特殊需要。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的直接访问。
volatile原意是“易变的”,但这种解释简直有点误导人,应该解释为“直接存取原始内存地址”比较合适。“易变”是相对与普通变量而言其值存在编译器(优化功能)未知的改变情况(即不是通过执行代码赋值改变其值的情况),而是因外在因素引起的,如多线程,中断等。编译器进行优化时,它有时会取一些值的时候,直接从寄存器里进行存取,而不是从内存中获取,这种优化在单线程的程序中没有问题,但到了多线程程序中,由于多个线程是并发运行的,就有可能一个线程把某个公共的变量已经改变了,这时其余线程中寄存器的值已经过时,但这个线程本身还不知道,以为没有改变,仍从寄存器里获取,就导致程序运行会出现未定义的行为。并不是因为用volatile修饰了的变量就是“易变”了,假如没有外因,即使用volatile定义,它也不会变化。而加了volatile修饰的变量,编译器将不对其相关代码执行优化,而是生成对应代码直接存取原始内存地址。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;
volatile int i=10; int a = i; ... //其他代码,并未明确告诉编译器,对i进行过操作 int b = i;
volatile 指出 i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据(即10)放在b中,而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的直接访问。
八、内联函数inline
关键字incline必须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前面不起任何作用,所以说inline是一种“用于实现的关键字”,而不是“用于声明的关键字”。