第八章 数组
1、数组名是一个指针常量,也就是数组第一个元素的地址,而不是指针变量。
2、sizeof 数组名:返回整个数组的长度,而不是指向数组的指针的长度。
sizeof string:返回的是指向字符的指针的长度,而不是数组的长度。
3、不能使用赋值符把一个数组的所有元素赋值到另一个数组。
如:int a[10];
int b[10];
b=a; //错误
4、一个神秘和离题的例子:
2[array] 是合法的。相当于array[2].
5、函数原型中的一维数组形参无需写明它的元素数目,因为函数并不为数组参数分配内存空间。
在多维数组中,只有一维数组才能根据初始化列表缺省地提供,剩余的几维必须显示地写出,这样编译器就能推断出每个子数组维数的长度
6、不完整的初始化,只允许省略最后几个初始值。
如:int vector[4]={1,5};
7、int d[3][6][10];
d是一个3排6列10行的整型三维数组。
多维数组的元素存储顺序按照最右边的下标率先变化的原则,---行主序。
可以把d看成1个1维数组,有3个元素,每个数组元素是又一个数组,含有6个元素,这每个元素又是一个数组,含有 10个元素。
8、int matrix[3][10];
1)、matirx[1][5]
2)、matirx
它的类型是指向包含10个整型元素的数组的指针。指向包含10个整型元素的第一个子数组。
3)、matrix+1
指向包含10个整型元素的第二个子数组。
4)、*(matrix+1)
类型:指向整型的指针。
相当于matrix[1]
5)、*(matrix+1)+5
类型:指向整型值的指针
相当于&matrix[1][5]
6)、*(*(matrix+1)+5)==matrix[1][5];
9、指向数组的指针
int matrix[3][10];
int (*p)[10]=matrix; //p指向matrix的第一行
p是一个指向(拥有10个元素整型元素的数组)的指针。
警告: int (*p)[]=matrix;
p仍然是指向一个整型数组的指针,但是数组长度却不见了。当某个整数与种类型的指针执行指针运算时,
它的值将根据空数组的长度进行调整,也就是说与0相乘。结果可能不是你所设想的。
10、指针数组
int *p[10];
p是一个数组,含有10个元素,每个元素是指向整型值的指针。
11、只要有可能,函数的指针形参都应该声明为const。
12、在有些环境中,使用register关键字提高程序的运行效率。
13、在多维数组的初始值列表中使用完整的多层花括号能提高可读性。
第九章 字符串、字符和字节
1、NULL字节是字符串的终止符,但它本身不是字符串的一部分,所以字符串的长度并不包括NULL字节。
字符串的长度就是它所包含的字符数。
2、size_t strlen(char const *string);
size_t是一个无符号整数类型。
看下面两个表达式:
if(strlen(x)>=strlen(y)) ...
if(strlen(x)-strlen(y)>=0)...
看似相等却不等!
第一条语句会如你想象的那样运行。
但是第二条语句将永远为真:因为无符号数-无符号数,结果永远都是无符号数,无符号数是不可能为负的
3、警告:表达式中如果同时出现了符号数和无符号数,可能会产生奇怪的结果。
如:
if(strlen(x)>=10)...
if(strlen(x)-10>=0)...
如果把strlen的返回值强制转换成int,就可以消除这个问题。
4、strncpy 调用的结果可能不是一个字符串,因此它的结果不会以NULL字节结尾。
5、提示:直接测试或操纵字符会降低程序的可移植性。
例如:
if(ch>='A'&&ch<='Z')
这条语句在使用ASCII字符集的机器上能够运行,但是在使用EBCDIC字符集的机器上将会失败 。
下面这条语句:
if(isupper(ch))
无论机器使用哪个字符集,它都能顺利运行。
6、任何类型的指针都可以转换成void*类型的指针。
第十章、结构和联合
1、
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}y[20],*z;
z=&x; //这是非法的。
上面这两个声明被编译器当做两种截然不同的类型,即使它们的成员列表完全相同。
2、点操作符的结合性是从左到右的。下标引用和点操作符具有相同的优先级,也是自左向右的结合性。它们的优先级均高于间接访问操作符。
3、struct SELF_REF{
int a;
struct SELF_REF b;
int c;
}; //这种类型的自引用是非法的。
因为这样b中又有一个结构SELF_REF,以此类推,无限下去,没有结尾。
struct SELF_REF{
int a;
struct SELF_REF *b;
int c;
}; //这种类型的自引用是合法的。
这个声明和前面那个声明区别在于b现在是一个指针而不是一个结构,编译器在结构的长度确定之前就已经知道指针的长度了。
它实际上是指向同一种类型的不同结构。(这句话是什么意思?)
4、不完整的声明:
struct B;
struct A{
struct B;
};
struct B{
struct A;
};
5、优先级高到低: -> . *
6、结构的存储分配:
编译器按照成员列表的顺序一个接一个地给每个成员分配内存。只有当存储成员时需要满足正确的边界对齐要求时,成员之间才可能出现用于填充的额外内存空间。
struct ALIGN{
char a;
int b;
char c;
};
如果某个机器的整型值是4个字节长度,并且它的起始存储位置必须能够被4整除,那么
aXXXbcXXX
每个结构将占据12字节的空间,但实际上只使用了其中个6个,浪费啊。。。
改成这样:
struct ALIGN{
int b;
char a;
char c;
};
但它值包含了8个字节的存储空间。
两个字符字节可以紧挨着存储,所以只有结构最后面需要跳过的两个字节才被浪费掉。
sizeof操作符能够得出一个结构的整体长度,包括因边界对齐而跳过的那些字节。
7、什么时候你应该向函数传递一个结构而不是一个指向结构的指针呢?很少有这种情况!
8、位段
位段成员必须声明为int、signed int、unsigned int类型。
在成员的后面是一个冒号和一个整数。这个整数指定该位段所占用的位的数目。
位段的声明本质上是不可移植的。
struct CHAR{
unsigned ch : 7;
unsigned font:6;
unsigned size:19;
};
9、联合
联合的所有成员引用的是内存中的相同位置。当你想在不同时刻把不同的东西存储与同一个位置时,就可以使用联合。
如果联合的各个成员具有不同的长度,联合的长度就是它最长的成员的长度。
第十一章 动态内存分配
1、malloc实际分配的内存可能比你请求的稍微多一点。但是,这个行为是由编译器定义的,所以不能指望它肯定会分配比你的请求更多的内
存。 如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针。因此,动态内存分配最常见的错误就是忘记检查所请求的
内存是成功分配。
2、一个void *类型的指针可以转换为其他任何类型的指针。
3、malloc和calloc之间最主要的区别就是后者在返回指向内存指针之前把它初始化为0.使用realloc可以对一块内存扩大或者缩小。
4、释放内存的一部分是不允许的,动态分配的内存必须整块一起释放。但是realloc函数可以缩小一块动态分配的内存,有效地释放它尾部的部分内存。
5、分配内存但在使用完毕后不将其释放将会引起内存泄露(memory leak).
6、如果一个指针不是从早先的malloc、calloc、realloc函数返回的,它是不能作为参数传递给free函数的。(int *p=&a;呢?)
第十二章 使用结构和指针
1、Pascal语言中的指针哲学:
使用锤子可能会伤着你自己,所以我们不给你锤子。
C语言中的指针哲学:给你锤子,实际上你可以使用好几种锤子,祝你好运!
2、语句提炼是简化程序的一种技巧,其方法是消除程序中冗余的句子。