C专家编程
C99 LIST FOR NEW FEATURES
C11 LIST FOR NEW FEATURES
NUL代表
\0
用于结束一个ACSII字符串 NULL用于表示空指针
编译限制, 对函数参数数量, 表达式嵌套的括号, 一行最多字符等的限制
#pragmas
向编译器传递信息
判断是否是C++程序, 或者判断是否已包含头文件 使用宏判断
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
HEADER FILE MACRO FOR CHECKING
math.h _MATH_H
stdio.h _STDIO_H
assert.h _ASSERT_H
ctype.h _CTYPE_H
signal.h _SIGNAL_H
stdarg.h _STDARG_H
stdlib.h _STDLIB_H
,
不会发出错误信息早用lint程序, 勤用lint程序. lint程序是软件的道德准则. 当你做错事时, 它会告诉你哪里不 对. 应该始终使用lint程序, 按照它的道德准则办事.
![符号重载]((https://github.com/LiMingFei56/picturebed/raw/main/c-prefessional-program/c-symbol-overload.png)
![运算符优先级问题]((https://github.com/LiMingFei56/picturebed/raw/main/c-prefessional-program/c-algorithm-priority.png)
优先级和结合性规则告诉你哪些符号组成一个意群的同时, 这些意群内部如何进行计算的次序 始终是未定义的. 大部分编程语言并未明确规定操作数计算的顺序. 在不确定优先级时使用
()
.
x = f() + g() * h();
可以知道乘法是在加法之前执行. 但是不知道f(), g(), h()是什么时间执行, 执行顺序有
可能是任意的.
结合性, 所以操作符都有左结合性或右结合性. 当操作符优先级一样时就需要考虑结合性.
类型模型(type model)
char * const *(*next) ();
void (*signal(int sig, void(*func)(int)))(int);
你不可以像下面那样做:
理解C语言声明的优先级规则: A 声明从它的名字开始读取,然后按照优先级顺序依次读取。 B 优先级从高到低依次是: B. 1 声明中波括号括起来的那部分 B. 2后缀操作符: 括号(;表示这是一个函数,而 方括号口表示这是一个数组 B. 3前缀操作符:星号*表示“指向…的指针” C 如果const 和(或)volatile 关键宇的后面紧跟类型说明符(如int,long 等),那么 它作用于类型说明符。在其他情况下,const 和(或)volatile 关键字作用于它左边 紧邻的指针星云
int *p[3];
*p[i];
const int * grape; // 指向的对象是只读的
int const * grape; // 指向的对象是只读的
int * const grape_jelly; // 指针是只读的
const int * const grape_jam;
int const * const grape_jam;
跟变量声明完全一样, 就是多了个关键字
区分定义和声明 只要记住下面的内容即可分清定义和声明: 声明相当于普通的声明:它所说明的并非自身,而是描述其他地方的创建的对象 定义相当于特殊的声明:它为对象分配内存。
右值(地址的内容): 运行时可知
数组中可以直接取字符; 指针需要先取地址, 再根据地址取字符
// 可以 extern char p[]; char *p;
// 不可以, 少了一步取地址 extern char *p; char p[10];
初始化指针时所创建的字符串常量被定义为只读. 修改这个字符串是未定义的
数组与指针相等的情况:
表达式中数组和指针可以互换, 因为它们在编译器里的最终形式都是指针, 并且都可以 进行取下标操作. 像加法一样, 取下标操作符的操作数是可以交换的, 所以a[6] 等效 6[a].
数组和指针可交换性的总结:
数组的数组与多维数组的主要区别,就在于数组的数组各维之间的内在关系是一种鲜明的层级关系。 上一维把下一维看作下一级数组,也就是数组嵌套。数组引用时需要层层解析,直到最后一维。 在C语言的多维数组中, 最右边的下标是最先变化的, 这个约定被称为”行主序”.
int squash[i][j] 会声明几种形式, 解释为 *(*(squash + i) + j)
int squash[23][12]
int *squash[23]
int **squash
int (*squash)[12]
取值也包含数组和指针的区别
int main(int argc, char *argv[])
a.out文件
a.out的其他内容
BSS段所需大小
数据段
文本段
载入器一般使用(mmap)取每个段的映像.
段 segments, 段就是一片连续的虚拟地址, 所以相邻的段被接合. A BSS段(Block Started by Symbol): 保存没有值的变量 B 数据段: 初始化后的全局和静态变量 C 文本段: 可执行文件的指令 D 堆栈段: 保存局部变量, 临时数据, 传递到函数中的参数等. 函数调用时存储堆栈结构 (stack frame, 也叫过程活动记录precedure activation recored). 用作暂时存储区(alloca函数). E 堆 : malloc() 动态分配的内存
引起段错误的地方:
指针释放引起的错误
每个线程有自己的堆栈, 大小为1MB(当需要时增长).
setjmp
和longjmp
函数可以操作过程活动记录 这被称作展开堆栈
(unwinding stack)
工具
段寄存器内存地址形成经过是: 取得段寄存器的值, 左移4位 加上16位偏移地址. 也就是 有多种位模式不同的地址, 最后结果是指向同一个地址.
Undefined behavior
Undefined Behavior in C and C++
What are the most useful new features in C99