1. 用单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。用双引号引起
的字符串代表的是一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制为零的符'\0'初
始化。------p11
这也就是为什么char *str; str = "hello world";可以运行。
整型数的存储空间可容纳多个字符(一般为8位),因此有的c编译器允许在一个字符常量中包含多个字符。也就是说,
用'yes'代替"yes"不会被编译器检测到,前者含义并没有准确地进行定义,但大多数c编译器理解为:“一个整数值,
有'y'、'e'、's'所代表的整数值按照特定的编辑器实现中定义的方式组合得到”。
----- p12
在VC++中得确可以通过,用一下程序观看存储方式:
int a = 'yes';
char one = char(a);
char two = char(a>>8);
char three = char(a>>16);
char four = char(a>>24);
char five = char(a>>32);
char six = char(a>>40);
char seven = char(a>>48);
char eight = char(a>>56);
printf("%c %c %c %d %c %c %c %d\n",one, two, three, four, five, six, seven, eight);
输出:s e y 0 s e y 0
如果int a = 'hill',输出: l l i 104 l l i 104,这里104是h的ASCII码
如果int a = 'hello',编译错误,报 too many characters in constant。
2. float ((f));
这个声明的含义是:当对其求值时,((f))的类型为浮点类型,由此可以推知:f也是浮点类型。
同样的逻辑也适用于函数和指针类型的声明,例如
float ff();
这个声明的含义是:表达式ff()求值结果一个浮点数,也就是,ff是一个返回值为浮点类型的函数。--p16
float (*h)();
表示h是一个指向返回值为浮点类型的函数的指针,因此:
(float (*)())
表示一个“指向返回值为浮点类型的函数的指针”的类型转换符。 ---p16
因此将常数转型为“指向返回值为void类型的函数的指针”类型,可以这样写:
(void (*)())0
有指针函数指针fp调用函数是 (* fp)();
因此,某种微处理器上,要想当计算机启动时,硬件将调用首地址为0位置的子例程,我们可以(* (void (*)()) 0)();
---p17
3. c语言只有一维数组,而且数组大小必须在编译期就作为一个常数确定下来,然而,数组的元素可以是任何类型的对象,
当然也可以是另一个数组。 --p33
对于一个数组,我们只能够做两件事:确定该数组的大小,以及获得指向该数组下标为0的元素的指针。其他有关数组的
操作,实际上都是通过指针进行的。--p34
int calendar[12][31];
这个语句声明了calendar是一个数组,该数组拥有12个数组类型的元素,其中每个元素都是一个拥有31个整型元素的数
组。如果calendar不是用与sizeof的操作数,而是用于其他的场合,那么calendar总是被换成一个指向calendar数组的起始
元素的指针。 ---p34
int a[3];
a是一个拥有3个整型元素的数组,如果我们在应该出现指针的地方,却采用了数组名来替换,那么数组名就被当作指向该
数组下标为0的元素的指针。因此我们这么写
int *p = a;
就会把数组a下标为0的元素的地址赋值给p。注意,我们并没有写成
int *p = &a;
因为&a是一个指向数组的指针,而p是一个指向整型变量的指针,它们的类型不匹配。 ---p36
int calendar[12][31];
int *p;
如果我们 p = calendar; 这条语句是非法的,因为calendar是一个二维数组,即“数组的数组”,在此处的上下文中使用
calendar名称会将其转换为一个指向数组的指针;而p是一个指向整型的指针,所以是非法的。很显然,我们需要一种声
明指向数组的指针的方法:
int (*ap)[31];
这个语句实际效果是,声明了*ap是一个拥有31个整型数组的数组,因此ap就是一个指向这样的数组的指针。---P37
4. 假定我们有两个这样的字符串s和t,我们希望将这两个字符串连接成单个字符串r:
char *r;
strcpy(r, s);
strcat(r, t);
这段程序并不能如愿以偿,原因在于不能确定r指向何处,而且r所指向的地址还应该有内存空间可容纳字符串。
一种方式是char r[100];但是我们不能确保r足够大。第二种方式 char *r = (char *)malloc(strlen(s) + strlen(t) + 1);
这种方式需注意是否申请空间成功,回收内存。 ---P40
5. C程序员经常错误的假设,在其他情形下也会有数组声明转换为对应的指针声明这种自动转换,
extern char *hello;
这个语句与
extern char hello[];
有着天壤之别。如果一个指针参数并不实际代表一个数组,即使从技术上而言是正确的,采用数组形式的记法经常也会起
到误导作用。(这段没搞懂)---P42
6.
int i, a[10];
for(i = 1; i <= 10; i++){
a[i] = 0;
}
这段代码由于i <= 10;的错误导致了死循环。实际上并不存在a[10]被设置为0,也就是数组a之后的一个字的内存被设置
为0;如果用来编译这段程序的编译器按照内存地址递减的方式来给变量分配内存,那么内存中数组a之后的一个字实际
上是分配给了整型变量 i 。此时,本来循环计数器 i 的值为10,而a[10] = 0将计数器 i 的值设置为0,陷入了一个死循环。
---P46
7. 连接器,即若干个源程序可以在不同的时候单独进行编译,然后在恰当的时候整合到一起。但是,连接器一般是与c编译
器分离的,它不可能了解C语言的诸多细节。尽管连接器并不理解C语言,然而它却能够理解机器语言和内存布局。编译
器的责任就是把C源程序“翻译”成对连接器有意义的形式。 ---P65
8. 每个外部对象都必须在程序某个地方进行定义。因此,如果一个程序中包括了语句
extern int a;
那么,这个程序就必须在别的某个地方包括语句
int a;
这两个语句可以是在同一个源文件中,也可以位于程序的不同源文件之中。
如果一个定义出现在两个或多个不同的源文件中,不同的系统会有不同的处理方式。严格的规则是,每个外部变量只能
够定义一次。 ----P68
9. 许多系统中的标准输入/输出库都允许程序打开一个文件,同时进行写入和读取操作。为了保持与过去同时进行读写操作
的程序的向下兼容性,一个输入操作不能随后直接紧跟一个输出操作,反之亦然。如果要同时进行输入和输出操作。必须
在其中插入fseek函数的调用。 ---P85