# 1 宏
EXIT_SUCCESS //函数运行成功时的退出状态。
EXIT_FAILURE // 函数运行错误时的退出状态。
RAND_MAX // rand() 函数可以返回的最大值。
MB_CUR_MAX //当前语言环境中,多字节字符占用的最大字节数。
2
3
4
# 2 abs(),labs(),llabs()
这三个函数用于计算整数的绝对值。abs()用于 int 类型,labs()用于 long int 类型,llabs()用于 long long int 类型。
fabs() 定义在 math.h
中。
// 输出 |-2| = 2
printf("|-2| = %d\n", abs(-2));
// 输出 |4| = 4
printf("|4| = %d\n", abs(4));
2
3
4
5
# 3 div,ldiv,lldiv
这三个函数用来计算两个参数的商和余数。div()用于 int 类型的相除,ldiv()用于 long int 类型的相除,lldiv()用于 long long int 类型的相除。
typedef struct {
int quot, rem;
} div_t;
typedef struct {
long int quot, rem;
} ldiv_t;
typedef struct {
long long int quot, rem;
} lldiv_t;
div_t d = div(64, -7);
// 输出 64 / -7 = -9
printf("64 / -7 = %d\n", d.quot);
// 输出 64 % -7 = 1
printf("64 %% -7 = %d\n", d.rem);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 4 字符串转数值
# 4.1 a 系列函数
- atoi():ASCII to int,字符串转成 int 类型。
- atof():字符串转成 double 类型。
- atol():字符串转成 long int 类型。
- atoll():字符串转成 long long int 类型。
int atoi(const char* nptr);
double atof(const char* nptr);
long int atol(const char* nptr);
long long int atoll(const char* nptr);
atoi("3490") // 3490
atof("3.141593") // 3.141593
// 如果参数是数字开头的字符串,atoi()会只转换数字部分,
// 比如atoi("42regular")会返回整数42。
// 如果首字符不是数字,比如“hello world”,则会返回0。
2
3
4
5
6
7
8
9
10
# 4.2 str 系列函数
- strtof():字符串转成 float 类型。
- strtod():字符串转成 double 类型。
- strtold():字符串转成 long double 类型。
float strtof(
const char* restrict nptr,
char** restrict endptr
);
double strtod(
const char* restrict nptr,
char** restrict endptr
);
long double strtold(
const char* restrict nptr,
char** restrict endptr
);
2
3
4
5
6
7
8
9
10
11
12
13
14
它们都接受两个参数,第一个参数是需要转换的字符串,第二个参数是一个指针,指向原始字符串里面无法转换的部分。
- nptr:待转换的字符串(起首的空白字符会被忽略)。
- endprt:一个指针,指向不能转换部分的第一个字符。
如果字符串可以完全转成数值,该指针指向字符串末尾的终止符 \0
。
这个参数如果设为 NULL,就表示不需要处理字符串剩余部分。
它们的返回值是已经转换后的数值。如果字符串无法转换,则返回0。
如果转换结果发生溢出,errno 会被设置为 ERANGE。
如果值太大(无论是正数还是负数),函数返回HUGE_VAL;如果值太小,函数返回零。
char *inp = " 123.4567abdc";
char *badchar;
double val = strtod(inp, &badchar);
printf("%f\n", val); // 123.456700
printf("%s\n", badchar); // abdc
if (*endptr == '\0') {
// 完全转换
} else {
// 存在无法转换的字符
}
2
3
4
5
6
7
8
9
10
11
12
13
如果不关心没有转换的部分,则可以将 endptr 设置为 NULL。
这些函数还可以将字符串转换为特殊值 Infinity 和 NaN。 如果字符串包含 INF 或 INFINITY(大写或小写皆可), 则将转换为 Infinity;如果字符串包含 NAN,则将返回 NaN。
# 4.3 str 系列函数(正数转换)
- strtol():字符串转成 long int 类型。
- strtoll():字符串转成 long long int 类型。
- strtoul():字符串转成 unsigned long int 类型。
- strtoull():字符串转成 unsigned long long int 类型。
long int strtol(
const char* restrict nptr,
char** restrict endptr,
int base
);
long long int strtoll(
const char* restrict nptr,
char** restrict endptr,
int base
);
unsigned long int strtoul(
const char* restrict nptr,
char** restrict endptr,
int base
);
unsigned long long int strtoull(
const char* restrict nptr,
char** restrict endptr, int base
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
它们接受三个参数。
(1)nPtr:待转换的字符串(起首的空白字符会被忽略)。
(2)endPrt:一个指针,指向不能转换部分的第一个字符。如果字符串可以完全转成数值,该指针指向字符串末尾的终止符\0。这个参数如果设为 NULL,就表示不需要处理字符串剩余部分。
(3)base:待转换整数的进制。这个值应该是2到36之间的整数,代表相应的进制,如果是特殊值0,表示让函数根据数值的前缀,自己确定进制,即如果数字有前缀0,则为八进制,如果数字有前缀0x或0X,则为十六进制。
它们的返回值是转换后的数值,如果转换不成功,返回0。
// 十进制转换
char* s = "3490";
unsigned long int x = strtoul(u, NULL, 10);
printf("%lu\n", x); // 3490
// 十六进制转换
char* end;
long value = strtol("0xff", &end, 16);
printf("%ld\n", value); // 255
printf("%s\n", end); // 无内容
value = strtol("0xffxx", &end, 16);
printf("%ld\n", value); // 255
printf("%s\n", end); // xx
// 转换二进制
char* s = "101010";
unsigned long int x = strtoul(s, NULL, 2);
printf("%lu\n", x); // 42
// 自定判断进制
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char* string = "-1234567abc";
char* remainderPtr;
long x = strtol(string, &remainderPtr, 0);
printf("%s\"%s\"\n%s%ld\n%s\"%s\"\n",
"The original string is ",
string,
"The converted value is ",
x,
"The remainder of the original string is ",
remainderPtr
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 5 rand()
rand()函数用来生成 0~RAND_MAX 之间的随机整数。
RAND_MAX是一个定义在stdlib.h里面的宏,通常等于 INT_MAX。
// 原型
int rand(void);
// 示例
int x = rand();
int x = rand() % (M - N + 1) + N;
// 或者浮点数随机值
// 0 到 0.999999 之间的随机数
printf("0 to 0.99999: %f\n", rand() / ((float)RAND_MAX + 1));
// n 到 m 之间的随机数:
// n + m * (rand() / (float)RAND_MAX)
printf("10.5 to 15.7: %f\n", 10.5 + 5.2 * rand() / (float)RAND_MAX);
2
3
4
5
6
7
8
9
10
11
12
13
14
# 6 srand()
rand()是伪随机数函数,为了增加随机性,必须在调用它之前,使用srand()函数重置一下种子值。
srand()函数接受一个无符号整数(unsigned int)作为种子值,没有返回值。
void srand(unsigned int seed);
// 通常使用time(NULL)函数返回当前距离时间纪元的秒数,作为srand()的参数。
2
#include <time.h>
srand((unsigned int) time(NULL));
2
上面代码中,time()的原型定义在头文件time.h里面,返回值的类型是类型别名time_t, 具体的类型与系统有关,所以要强制转换一下类型。time()的参数是一个指针,指向一个具体的 time_t 类型的时间值,这里传入空指针NULL作为参数,由于 NULL 一般是0,所以也可以写成time(0)。
# 7 abort()
abort()用于不正常地终止一个正在执行的程序。使用这个函数的目的,主要是它会触发 SIGABRT 信号,开发者可以在程序中为这个信号设置一个处理函数。
void abort(void);
该函数没有参数。
# 8 exit(),quick_exit(),_Exit()
这三个函数都用来退出当前正在执行的程序。
void exit(int status);
void quick_exit(int status);
void _Exit(int status);
2
3
它们都接受一个整数,表示程序的退出状态,0 是正常退出,非零值表示发生错误,可以使用宏 EXIT_SUCCESS
和EXIT_FAILURE
当作参数。它们本身没有返回值。
它们的区别是,退出时所做的清理工作不同。exit()是正常退出,系统会做完整的清理,比如更新所有文件流,并且删除临时文件。quick_exit()是快速退出,系统的清理工作稍微少一点。_Exit()是立即退出,不做任何清理工作。
下面是一些用法示例。
exit(EXIT_SUCCESS);
quick_exit(EXIT_FAILURE);
_Exit(2);
2
3
# 9 atexit(),at_quick_exit()
atexit()用来登记当前程序退出时(调用exit()或main()正常退出),所要执行的其他函数。
at_quick_exit()则是登记使用quick_exit()方法退出当前程序时,所要执行的其他函数。
exit()只能触发atexit()登记的函数,quick_exit()只能触发at_quick_exit()登记的函数。
int atexit(void (*func)(void));
int at_quick_exit(void (*func)(void));
它们的参数是要执行的函数地址,即函数名。它们的返回值都是调用成功时返回0,调用失败时返回非零值。
下面是一个例子。
void sign_off(void);
void too_bad(void);
int main(void) {
int n;
atexit(sign_off); /* 注册 sign_off()函数 */
puts("Enter an integer:");
if (scanf("%d", &n) != 1) {
puts("That's no integer!");
atexit(too_bad); /* 注册 too_bad()函数 */
exit(EXIT_FAILURE);
}
printf("%d is %s.\n", n, (n % 2 == 0) ? "even" : "odd");
return 0;
}
void sign_off(void) {
puts("sign_off");
}
void too_bad(void) {
puts("too bad");
}
上面示例中,用户输入失败时,会调用sign_off()和too_bad()函数;但是输入成功时只会调用sign_off()。因为只有输入失败时,才会进入if语句登记too_bad()。
另外,如果有多条atexit()语句,函数退出时最先调用的,是最后一个登记的函数。
atexit()登记的函数(如上例的sign_off和too_bad)应该不带任何参数且返回类型为void。通常,这些函数会执行一些清理任务,例如删除临时文件或重置环境变量。
at_quick_exit()也是同样的规则,下面是一个例子。
void exit_handler_1(void) {
printf("1\n");
}
void exit_handler_2(void) {
printf("2\n");
}
int main(void) {
at_quick_exit(exit_handler_1);
at_quick_exit(exit_handler_2);
quick_exit(0);
}
执行上面的示例,命令行会先输出2,再输出1。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 10 getenv()
getenv()用于获取环境变量的值。环境变量是操作系统提供的程序之外的一些环境参数。
char* getenv(const char* name);
它的参数是一个字符串,表示环境变量名。返回值也是一个字符串,表示环境变量的值。如果指定的环境变量不存在,则返回 NULL。
下面是输出环境变量$PATH的值的例子。
printf("PATH is %s\n", getenv("PATH"));
2
3
4
5
6
# 11 system()
system()
system()函数用于执行外部程序。它会把它的参数字符串传递给操作系统,让操作系统的命令处理器来执行。
void system( char const * command );
这个函数的返回值因编译器而异。但是标准规定,如果 NULL 作为参数,表示询问操作系统,是否有可用的命令处理器,如果有的话,返回一个非零值,否则返回零。
下面是执行ls命令的例子。
system("ls -l");
2
3
4
5
6
# 12 内存管理
stdlib.h 提供了一些内存操作函数,下面几个函数详见《内存管理》一章,其余在本节介绍。
- malloc():分配内存区域
- calloc():分配内存区域。
- realloc():调节内存区域大小。
- free():释放内存区域。
aligned_alloc()
很多系统有内存对齐的要求,即内存块的大小必须是某个值(比如64字节)的倍数,这样有利于提高处理速度。aligned_alloc()就用于分配满足内存对齐要求的内存块,它的原型如下。
void* aligned_alloc(size_t alignment, size_t size);
它接受两个参数。
alignment:整数,表示内存对齐的单位大小,一般是2的整数次幂(2、4、8、16……)。
size:整数,表示内存块的大小。
分配成功时,它返回一个无类型指针,指向新分配的内存块。分配失败时,返回 NULL。
char* p = aligned_alloc(64, 256);
上面示例中,aligned_alloc()分配的内存块,单位大小是64字节,要分配的字节数是256字节。
2
3
4
5
6
7
8
9
10
11
12
# 13 qsort()
qsort()用来快速排序一个数组。它对数组成员的类型没有要求,任何类型数组都可以用这个函数排序。
void qsort(
void *base,
size_t nmemb,
size_t size,
int (*compar)(const void *, const void *)
);
该函数接受四个参数。
base:指向要排序的数组开始位置的指针。
nmemb:数组成员的数量。
size:数组每个成员占用的字节长度。
compar:一个函数指针,指向一个比较两个成员的函数。
比较函数compar将指向数组两个成员的指针作为参数,并比较两个成员。如果第一个参数小于第二个参数,该函数应该返回一个负值;如果两个函数相等,返回0;如果第一个参数大于第二个参数,应该返回一个正数。
下面是一个用法示例。
#include <stdio.h>
#include <stdlib.h>
int compar(const void* elem0, const void* elem1) {
const int* x = elem0;
const int* y = elem1;
return *x - *y;
}
int main(void) {
int a[9] = {14, 2, 3, 17, 10, 8, 6, 1, 13};
qsort(a, 9, sizeof(int), compar);
for (int i = 0; i < 9; i++)
printf("%d ", a[i]);
putchar('\n');
}
执行上面示例,会输出排序好的数组“1 2 3 6 8 10 13 14 17”。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 14 bsearch()
bsearch()使用二分法搜索,在数组中搜索一个值。它对数组成员的类型没有要求,任何类型数组都可以用这个函数搜索值。
注意,该方法只对已经排序好的数组有效。
void *bsearch(
const void* key,
const void* base,
size_t nmemb,
size_t size,
int (*compar)(const void *, const void *)
);
这个函数接受5个参数。
key:指向要查找的值的指针。
base:指向数组开始位置的指针,数组必须已经排序。
nmemb:数组成员的数量。
size:数组每个成员占用的字节长度。
compar:指向一个将待查找值与其他值进行比较的函数的指针。
比较函数compar将待查找的值作为第一个参数,将要比较的值作为第二个参数。如果第一个参数小于第二个参数,该函数应该返回一个负值;如果两个参数相等,返回0;如果第一个参数大于第二个参数,返回一个正值。
如果找到待查找的值,bsearch()返回指向该值的指针,如果找不到,返回 NULL。
下面是一个用法示例。
#include <stdio.h>
#include <stdlib.h>
int compar(const void *key, const void *value) {
const int* k = key;
const int* v = value;
return *k - *v;
}
int main(void) {
int a[9] = {2, 6, 9, 12, 13, 18, 20, 32, 47};
int* r;
int key;
key = 12; // 包括在数组中
r = bsearch(&key, a, 9, sizeof(int), compar);
printf("Found %d\n", *r);
key = 30; // 不包括在数组中
r = bsearch(&key, a, 9, sizeof(int), compar);
if (r == NULL)
printf("Didn't find 30\n");
return 0;
}
执行上面的示例,会输出下面的结果。
Found 12
Didn't find 30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 多字节字符函数
stdlib.h 提供了下面的函数,用来操作多字节字符。
- mblen():多字节字符的字节长度。
- mbtowc():将多字节字符转换为宽字符。
- wctomb():将宽字符转换为多字节字符。
- mbstowcs():将多字节字符串转换为宽字符串。
- wcstombs():将宽字符串转换为多字节字符串。