您的位置:首页 > 编程语言 > C语言/C++

<你必须知道的495个C语言问题>学习笔记 (一)

2014-04-16 22:12 288 查看
前言:这本书是我在看完 <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
; /* 上面指针的数组 */


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 多次提到)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: