C 语言程序设计 现代方法

C 语言程序设计 现代方法

第十四章 预处理器

14.3 宏定义

# 运算符将宏的一个参数转换为字面串。

1
2
3
#DEFINE PRINT_INT(n) printf(#n "=%d\n", n)

PRINT_INT(i/j);

## 运算符将两个记号(如标识符)“粘合”在一起,成为一个记号。若其中一个参数是宏参数,“粘合”会在形参被相应的实参替换后发生

1
2
3
#define MK_ID(n)i##n

int MK_ID(1),MK_ID(2),MK_ID(3);

预处理后变成

1
int i1,i2,i3;

另一种用法

1
2
3
4
5
6
7
#define GENERIC_MAX(type) \
type type##_max(type x,type y) \
{ \
return x>y?x:y; \
}

GENERIC_MAX(float)
1
2
3
4
#define TEST(condition, ...) ((condition)? \
printf("Passed test: %s\n", #condition): \
printf(__VA_ARGS))
TEST(voltage <=max_voltage, "Vlotage %d exceeds %d\n", vlotage, max_voltage)

__VA_ARGS__ 是一个专用的标识符,出现在具有可变参数个数的宏的替换列表中,代表所有与省略号相对应的参数。

第十六章 结构、联合和枚举

16.4 联合

1
2
3
4
union {
int i;
double d;
} u;

16.5 枚举

1
2
3
4
5
6
enum suit{
CLUBS,
DIAMONDS,
HEARTS,
SPADES
} s1,s2;

第十七章 指针的高级运用

17.3 动态分配数组

malloc 函数

1
void *malloc(size_t size)

calloc 函数

1
void *calloc(size_t nmemb, size_t size)

nmemb 个元素的数组分配空间,每个元素长度都是 size 字节

分配后,会把所有位置 0

realloc 函数

1
void *realloc(void *ptr, size_t size)

17.5 链表

1
2
3
4
5
struct node{
int value;
struct node *next;
};
struct node *first = NULL;

17.6 指向函数的指针

1
double integrate(double (*f)(double), double a, double b);

qsort 函数

1
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
1
2
3
4
int compare_parts(const void *p, const void *q)
{
return ((struct part *)p)->number - ((struct part *) q)->number;
}

第十八章 声明

18.1 声明的语法

声明形式 声明指定符 声明符;

声明指定符分为

  • 存储类型,包括 autostaticexternregister 必须在最前面

  • 类型限定符,包括 constvolatilerestrict_Atomic

  • 类型指定符,包括 voidcharshortintlongfloatdoublesignedunsignedstructunionenum 和用 typedef 创建的类型名

  • 函数指定符,包括 inline_Noreture

18.2 存储类型

C 程序中每个变量都有三个性质

  • 存储期:变量的存储期决定了为变量预留的内存被释放的时间。
    • 具有自动存储期的变量在所属块被执行时获得内存单元,块终止时释放内存单元。
    • 具有静态存储期的变量在程序运行期间占有同一个存储单元,也就允许变量无限期地保留所占有的空间。
  • 作用域:变量的作用域是变量名字的作用范围。
    • 块作用域:变量的名字从声明的地方一直到所在块的末尾都是可见的。
    • 文件作用域:变量的名字从声明的地方一直到所在文件末尾都是可见的。
  • 链接:变量名字的链接属性
    • 通过具有外部链接属性的名字,变量可以被程序中的几个或全部文件共享。
    • 名字具有内部链接属性,变量只能属于单独一个文件,文件中的函数可以共享这个变量。
    • 名字属于无链接的变量属于单独一个函数,不能被共享

默认的性质

1
int i;

有静态存储期,文件作用域,外部链接

1
2
3
4
void f(void)
{
int j;
}

自动存储期,块作用域,无链接

auto 存储类型

对块内部变量默认

static 存储类型

作用在块外部时,static 说明变量名字具有内部链接。

用在块内部时,static 把变量的存储期从自动的变成静态的。

extern 存储类型

1
extern int i;

编译器不会为变量 i 分配存储单元。

上述声明不是变量 i定义,它只是提示编译器需要访问定义在别处的变量(可能稍后在同一文件,更常见的是另一文件)。变量在程序中可以多次声明,只能有一次定义

变量的 extern 声明不是定义,除了一个例外:对变量进行初始化的声明,例如

1
2
3
extern int i=0;

int i=0;

上两条等价。

extern 声明中的变量始终具有静态存储期,若声明在块内部,那么具有块作用域,否则具有文件作用域

extern 型变量在文件中较早的位置(任何函数定义的外部)声明为 static 那么它具有内部链接;否则(通常情况下)具有外部链接。

register 存储类型

要求编译器把变量存储在寄存器而不是内存中

函数的存储类型

函数声明也可以包含存储类型,选项只有 externstatic

extern 说明函数具有外部链接,即允许其他函数调用此函数

static 说明是内部链接,也就是只能在定义函数的内部调用此函数

函数默认具有外部链接

18.4 声明符

  • 始终从内向外读声明符
  • []() 优先于 *

18.6 内联函数

对具有外部链接的内联函数的限制

  • 内联函数不能定义可改变的 static 变量
  • 函数中不能引用具有内部链接的变量

GCC 命令行选项需要添加 -O 才会进行内联

第二十章 底层程序设计

当数据项多于一个字节时,先存储最左边的字节叫大端,相最后存储相反的叫小端

x86 处理器假设数据按小端存储

1
2
3
4
5
6
7
8
union {
struct {
WORD ax, bx, cx, dx;
} word;
struct {
BYTE al, ah, bl, bh, cl, ch, dl, dh;
} byte;
} regs;

第二十五章 国际化特性

多字节字符与宽字符的比较

特性 多字节字符(Multibyte Character) 宽字符(Wide Character)
表示类型 char wchar_t
存储长度 动态长度(1 至多字节,依编码不同) 固定长度(2 或 4 字节,依实现不同)
编码方式 变长编码(如 UTF-8、GBK) 定长编码(如 UTF-16、UTF-32)
函数支持 标准 C 函数(strlen, strcpy 宽字符函数(wcslen, wcscpy
兼容性 与 ASCII 兼容 需要专门支持
优点 存储效率高 处理字符方便
缺点 操作复杂(需处理变长编码) 占用更多存储空间

第二十六章 其他库函数

1
2
3
4
5
6
7
8
9
10
11
12
int max_int(int n, ...){
va_list ap;
int i,cur, mx;
va_start(ap, n);
mx = va_arg(ap, int);
for(i=1;i<n;i++){
cur = va_arg(ap, int);
if(cur>mx) mx=cur;
}
va_end(ap);
return mx;
}

va_copy(va_list dest, va_list src) 宏把 src 复制到 dest 中