前言:这本书是我在看完 <C Primer Plus >后打算找本书练习一下,结果发现,这书里面很多问题,真的很坑. 我至今没遇到,甚至不会有那种想法. 好吧,不说其它,把笔记弄上来,一些让我无名郁闷的就留给后人补充吧. 我这文章建议可以一览而过
基本问题(很多问题在<C prime plus>有提及)
1 声明和初始化
1.1 根据需要选择整数的类型,不要把期望给予编译器.一般 int;数值大 :long ;注重空间:short; 避免符号扩展:unsigned
1.2 C99 定义了long long来保证长度至少有64位
1.3 关于定义和声明 全局变量和函数 :可以有多处"声明",但只能只有一个"定义".最好:在某个相关的.c文件里面定义,然后在头文件.h中进行外部声明,
在需要使用的时候,只要包含该.h即可
1.4 extern :作为一种格式上的提示,表明函数上的定义可能在另一个源文件. extern int F() 和 int F() 本质上没有什么区别
1.6 C里面的struct 如何定义一个包含指向自己的指针
struct node {
char*item ;
structnode *next;
};
typedef struct node *NODEPTR
1.7怎样建立和理解非常复杂的声明?
关于从内到外” 解释和理解 C 语言声明
方法1:直接暴力法 ,用typedef 逐步完成声明;
//用 typedef 逐步完成声明:
typedef char *pc;/* 字符指针 */
typedef pc fpc();/* 返回字符指针的函数 */
typedef fpc *pfpc;/* 上面函数的指针 */
typedef pfpcfpfpc(); /* 返回函数指针的函数 */
typedef fpfpc*pfpfpc; /* 上面函数的指针 */
pfpfpc a[N]; /* 上面指针的数组 */
2:用cdecl程序,它可以把英文转为C或者把C转为英文
举例: 定义一个包含 N 个指向返回指向字符的指针的函数的指针的数组
char*(*(*a[])())()
cdecl> declare a as array ofpointer to function returningpointer tofunction returning pointer to char
通过类型转换, cdecl 也可以用于解释复杂的声明, 指出参数应该进入哪一对括号 (如同在上述的复杂函数定义中)
1.8函数调用前必须先声明
3 表达式
3.1 为什么这样的代码: a[i] = i++; 不能工作? i++ 产生副作用,但又在其他地方被引用,a[i]里面的i 不能确定为是新值还是旧值.C标准将其视为无定义
3.2 i++ *i++的问题 跟上面一样,结果取决于编译器,包含多个不确定的副作用的代码的行为总是被认为未定义
3.3 i = i++ ;(同上)
3.5运算符优先级和括弧只能赋予表达是计算部分的顺序
例如: f() + g() *h(),乘法运算在加法之前, 但这并不能说明这三个函数哪个会被首先调用。如果你需要确保子表达式的计算顺序,你可能需要使用明确的临时变量和独立的语句。
3.6 while((c = getchar())
!= EOF &&c != ’\n’)
3.7 序列点:序列点是一个时间点(在整个表达式全部计算完毕之后或在 ||、 &&、 ? : 或逗号运算符处, 或在函数调用之前), 此刻尘埃落定, 所有的副作用都已确保结束 在上一个 和下一个序列点之间, 一个对象所保存的值至多只能被表达式的计算修改一次。而且前一个值只能用于决定将要保存的值,第二句确保在修改之前才访问变量的表达式为合法
3.8 " a[i] = i++; 我们不知道 a[] 的哪一个分量会被改写,但 i的确会增加 1"这个说法是错的(无定义状态是针对整个式子的)
3.12 ? : 操作符, 跟多数操作符一样, 生成一个值, 而不能被赋值
4 指针
4.1 操作指针P本身和操作指针指向的内存的区别
对指针本身赋值,使之指向别处 : P= malloc(10)
使用* : *P = 'H'
4.2"++"或者"--"操作符的优先级比"*"高,所以*P++相当于*(P++) ;自增 p 并返回 p 自增之前所指向的值
4.4 在 C 中, 参数是通过值传递的。被调函数仅仅修改了传入的指针副本。你需要传入
指针的地址 (函数变成接受指针的指针), 或者让函数返回指针。
4.6 int f(int *)引入一个常数
先定义一个临时变量,然后把它的地址传给函数
使用“复合常量: f((int[]){5});
5空指针
5.1空指针 表示 “未分配” 或者 “尚未指向任何地方”的指针
取地址操作符 & 永远也不能得到空指针
与任何对象或函数的指针值都不相等
对 malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针
空指针可以确保不指向任何对象或函数; 而未初始化指针则可能指向任何地方
每种指针类型都有一个空指针, 而不同类型的空指针的内部表示可能不尽相同,编译器必须时刻明确需要那种空指针, 以便在需要的时候加以区分
5.2获得空指针的做法
指针上下文中的常数 0 会在编译时转换为空指针
在初始化、赋值或比较的时候, 如果一边是指针类型的值或表达式, 编译器可以确定另一边的常数 0 为空指针并生成正确的空指针值
if(p) 等价于 if(p != 0)。
而这是一个比较上下文, 因此编译器可以看出 0 实际上是一个空指针常数
5.5 NULL 在机器的定义定义为0
在指针上下文中 NULL 和 0 是完全等价的, 而未加修饰
的 0 也完全可以接受。任何使用 NULL (跟 0 相对) 的地方都应该看作一种温和的提示, 是在使用指针; 程序员 (和编译器都) 不能依靠它来区别指针 0 和整数 0
5.12遵循的规则
1. 当你在源码中需要空指针常数时, 用“0”或 “NULL”。
2. 如果在函数调用中 “0” 或 “NULL”用作参数, 把它转换成被调函数需要的指针类型
6 数组和指针
6.3 C语言中“指针和数组等价:意思是操作等价,定义完全不是同一回事
6.4作为函数形参的数组和指针申明可以互换,这是一种便利做法,传入的永远是复制的值,而不是实际值
6.5指针是可变的左值,但数组不是.所以*P++是合法的,但a++就不行
6.6 数组和指针的区别
数组自动分配空间, 但是不能重分配或改变大小。指针必须明确赋值以指向分配的空间 (可能使用 malloc), 但是可以随意重新赋值 (即, 指向不同的对象), 同时除了表示一个内存块的基址之外, 还有许多其它的用途。由于数组和指针所谓的等价性(参见问题 6.3), 数组和指针经常看起来可以互换, 而事实上指向 malloc 分配的内存块的指针通常被看作一个真正的数组(也可以用 [ ] 引用)。
6.9 数组引用会退化为指针,但arr 和&arr 还是有区别的,区别在于类型.
8 字符和字符串
8.1 strcat()用于连接字符串;比较两个字符串, 一般使用库函数 strcmp():
8.2 C中的字符串用字符的数组表示,而C语言从来不会把数组作为一个整体操作 (赋值, 比较等)
8.3 定义完数组后若不初始化,之后是不能整体赋值的,但可以用可以使用strcpy()代替:
8.4 数字以及它们对应的字符集的相互转化:相互转换时,加上或减去常数’0’,
8.5 C语言中的字符常数是int型,因此sizeof(’a’) 是 sizeof(int),这是另一个与 C++ 不同的地方
9 布尔表达式和变量
C语言里面没有布尔类型,(用int 更快,用char 节省空间)
C 语言中的确任何非零值都都被看作真, 但这仅限于“输入”, 也就是说, 仅限于需要布尔值的地方,所以,把TURE定义为1是可以的,
不用担心某个内置的函数或关系操作符 “返回” 不是 1而是2345...
预处理宏 TRUE 和 FALSE (当然还有 NULL) 只是用于增加代码可读性, 而不是因为其值可能改变
14 浮点运算
精度引起的问题(二进制存储,转为十进制的损失)
例如 float 变量赋值为 3.1,printf输出的值可能为 3.0999999
下溢、误差的累积和其它非常规性是常遇到的麻烦
double下两个数的比较要注意!
if (a == b) /* 错! */
if (fabs(a - b) <= epsilon *fabs(a))
epsilon 被赋为一个选定的值来控制 “接近度”。你也要确定a不会为0.利用一个精确的阈值。这个阈值和作比较的浮点数值大小有关
14.6 怎么取整数
对于整数 (int)(x + 0.5) ;将负数考虑进去,我们可以这样: int)(x < 0 ? x- 0.5 : x + 0.5)
14.7 幂运算
可以用pow(),但是不建议.提供幂运算的处理器很少; 一般用乘法更有效
(ps:乘除尽量考虑2,这样可以用移位来更高效解决问题)
14.8 PI 的定义不再标准,要使用的话,可以自定义或者用4*atan(1.0) 或 acos(-1.0) 计算出来
10 C预处理器
10.4 习惯放在 .h文件里的 :
1宏定义 (预处理 #defines) 2 结构、联合和枚举声明;3 typedef 声明;4 外部函数声明;5 全局变量声明
10.6 #include <> 和 #include "" 有什么区别
<> 语法通常用于标准或系统提供的头文件, 而 "" 通常用于程序自己的头文件。
11 ANSI/ISO 标准C
11.24 不能对void*指针进行运算,因为编译器不知道所指对象的大小,这样是没法移动指针的
11.26 ANSI/ISO 标准声称malloc(0) 可能返回一个空指针或者指向 0 字节的指针; 其行为由实现定义。
11.35 面对未定义行为的时候,包括范围内的实现定义行为和未确定行为,
编译器可以做任何实现,其中也包括你所有期望的结果。但是依靠这个实现却不明智
12 标准输入输出库
12.2 在键盘输入EOF : ctrl +D 或者ctrl+Z
12.6 printf 的格式串中输出一个 ’%' (使用'%%')
12.15 scanf格式串中\n不表示等待换行符,而是读取并放弃所有的空白字符(C Primer 多次提到)