C 语言拾遗

作为第一门语言是 C++ 的程序员,一直觉得对 C 语言的一些重要特性不清晰,对一些 C 程序惯用法也不了解。故借阅读 《C 程序设计语言》 一书的机会,将相关曾经遗漏的知识整理如下。

1. extern 声明

extern 关键字用于声明外部变量,注意声明与定义的不同:

  • 定义(define):表示创建变量或分配存储单元
  • 声明(declaration):说明变量的性质,但并不分配存储单元

当外部变量的定义出现在使用它的函数之前时,可以省略 extern 声明;反之,必须有 extern 声明(如:涉及多个源文件)。

Example:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int n = 1;

int main(void)
{
extern int n; # extern declaration
printf("%d\n", n);
return 0;
}

static 关键字修饰外部变量或函数,可以将其后声明的对象的作用域限定为被编译文件的剩余部分,其他文件不能访问该对象。

static 也可用于声明内部变量:该变量仅能在某函数内使用但一直占据存储空间。

2. 宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 取消宏定义
#undef getchar


// 调试打印宏
#define dprint(expr) printf(#expr " = %g\n", expr)

dprint(x/y);

// 宏将展开为:
printf("x/y" " = %g\n", x/y);


// 预处理器运算符 ##
#define paste(front, end) front ## end

// 宏调用 paste(name, 1) 的结果将创建符号 name1

条件包含

example 1:

1
2
3
4
5
6
#if !defined(HDR)
#define HDR

/* hdr.h 文件内容 */

#endif

等价于:

1
2
3
4
5
6
#ifndef HDR
#define HDR

/* code here */

#endif

example 2:

1
2
3
4
5
6
7
8
9
10
#if SYSTEM == SYSV
#define HDR "sysv.h"
#elif SYSTEM == BSD:
#define HDR "bsd.h"
#elif SYSTEM == MSDOS:
#define HDR "msdos.h"
#else:
#define HDR "default.h"
#endif
#include HDR

3. union

union:在不同时刻保存不同类型和长度的对象的变量。

example:

1
2
3
4
5
union u_tag {
int ival;
float fval;
char *sval;
} u;

union 实际上就是 struct,它的所有成员相对与基地址的偏移量都为 0,此 struct 空间要大到足够容纳最宽的成员,并且,其对齐方式要适合于 union 中所有类型的成员。

4. 位字段 bit-field

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义位字段,':' 后数字表示字段的宽度
// 字段作用与小整数类似,可以出现在算术表达式中
struct {
unsigned int is_keyword : 1;
unsigned int is_extern : 1;
unsigned int is_static : 1;
} flags;

// 设置字段值
flags.is_extern = flags.is_static = 1;
flags.is_extern = flags.is_static = 0;

// 位测试
if (flags.is_extern == 0 && flags.is_static ==0)
...

字段不是数组,且没有地址,不能对其使用 & 运算符。

5. 文件访问

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
FILE *fp = fopen(name, mode);	// 以指定 mode 打开文件;失败返回 NULL
fclose(fp); // 关闭文件

// 从文件中返回下一个字符;若达到文件结尾或出错,返回 EOF
int getc(FILE *fp);

// 将字符 c 写入到 fp 指向的文件中,并返回写入的字符
int putc(int c, FILE *fp);

// 对文件的格式化输入输出
int fscanf(FILE *fp, char *format, ...);
int fprintf(FILE *fp, char *format, ...);

// 错误判断
int ferror(FILE *fp); // 如果流 fp 中出现错误,则返回一个非 0 值
int feof(FILE *fp); // 如果文件到达文件结尾,返回一个非 0 值

// 行输入和行输出
// 从 fp 指向的文件中读取下一个输入行(包括换行符),并将它存放在 line 中。最多读取 maxline-1 个字符。正常结束返回 line;出错返回 NULL
char *fgets(char *line, int maxline, FILE *fp);
// 将 line 写入 fp 指向的文件中。出错返回 EOF;否则返回一个非负值。
char *fputs(char *line, FILE *fp);

C 语言拾遗
https://arcsin2.cloud/2023/05/01/C-语言拾遗/
作者
arcsin2
发布于
2023年5月1日
许可协议