器→工具, 编程语言

C语言学习之标准库

钱魏Way · · 8 次浏览

C语言是一种通用的、面向过程式的计算机程序设计语言。C语言不但提供了丰富的库函数,还允许用户定义自己的函数。每个函数都是一个可以重复使用的模块,通过模块间的相互调用,有条不紊地实现复杂的功能。可以说C程序的全部工作都是由各式各样的函数完成的。

C 标准函数库(C standard library,缩写:libc)是一套内置的功能函数集,它们可以用于执行各种常见的任务。标准库包括了各种功能,例如输入/输出处理,字符串操作,内存管理,数学计算等等。

C 标准函数库是在C语言程序设计中,所有匹配标准的头文件(head file)的集合,以及常用的函数库实现程序(如 I/O 输入输出和字符串控制)。标准函数库通常会随附在编译器上。因为 C 编译器常会提供一些额外的非 ANSI C 函数功能,所以某个随附在特定编译器上的标准函数库,对其他不同的编译器来说,是不兼容的。

C语言标准库有各种不同的实现,比如最著名的glibc, 用于嵌入式Linux的uClibc,还有ARM公司的自己的C语言标准库及精简版的MicroLib等。不同标准库的实现并不相同,而且提供的函数也不完全相同,不过有一个它们都支持的最小子集,这也就是最典型的C语言标准库。

  • ANSI C共包括15个头文件。
  • 1995年,Normative Addendum 1 (NA1)批准了3个头文件(h、wchar.h和wctype.h)增加到C标准函数库中。
  • C99标准增加了6个头文件(h、fenv.h、inttypes.h、stdbool.h、stdint.h和tgmath.h)。
  • C11标准中又新增了5个头文件(h、stdatomic.h、stdnoreturn.h、threads.h和uchar.h)。

至此,C标准函数库共有29个头文件。

ANSI C

头文件文件 简介说明
<stdio.h> 标准I/O库
<ctype.h> 字符的判断和大小写转换
<stdlib.h> 标准工具库函数:定义数值转换函数,伪随机数生成函数,动态内存分配函数,过程控制函数。
<string.h> 字符串处理函数
<assert.h> 包含断言宏,被用来在程序的调试版本中帮助检测逻辑错误以及其他类型的bug。
<limits.h> 定义了整数类型的一些极限值
<stddef.h> 一些标准宏定义
<time.h> 时间相关
<float.h> 浮点运算与限制
<math.h> 数学函数
<errno.h> 用来测试由库函数报的错误代码。
<locale.h> 定义C语言本地化函数
<setjmp.h> 跨函数跳转
<signal.h> 信号
<stdarg.h> 可变参处理

标准I/O <stdio.h>

C语言的标准输入输出库(通常称为stdio,由头文件<stdio.h>提供)提供了一套丰富的函数,用于在控制台上进行输入和输出,或者对文件进行读写操作。

在其中定义了以下一些常用的类型及常量:

Name Comment
FILE 文件指针
EOF End Of File,表示文件的结尾
stderr 标准错误流
stdin 标准输入流
stdout 标准输出流

其中stderr、stdin、stdout为宏定义,是指向FILE类型的指针。<stdio.h>中的函数有很多,大致可分为对标准输入输出流的操作、对文件流的操作、对标准错误流的操作、对字符串的操作这几大类。

标准输入输出流

其实从stdin与stdout的定义中也可以看到,标准输入输出流也就是文件,只是一般情况下已经默认定义为键盘和屏幕。这与Linux中一切皆文件的思想一脉相承。

常用的函数有以下这些:

Name Comment
int printf(const char * format, …) 格式化输出数据至stdout
int scanf(const char * format, …) 由stdin读取格式化输入数据
int putchar(int c) 向stdout输出一个字符
int getchar(void) 由stdin读入一个字符
int puts(const char * s) 向stdout输出一串字符串
char * gets(char * s) 由stdin读入一串字符串

另外,vprintf()函数主要用于需要自己实现一些类似printf()的函数时使用,关于这个函数的用处可参考StackOverflow上的讨论,用于文件流的vfprintf()与用于字符串的vsprintf()的用处也是相似的。

文件流

对文件的操作是<stdio.h>中的核心,其他函数均可视为对特定文件的操作,大部分函数均以f****()命名。

最重要的函数是以下这几个:

Name Comment
FILE * fopen(const char * filename, const char * mode) 打开文件,失败返回NULL
int fclose(FILE * stream) 关闭文件,成功返回0,失败返回EOF
size_t fread(void * ptr, size_t size, size_t nmemb, FILE * stream) 读取文件内容
size_t fwrite(cosnt void * ptr, size_t size, size_t nmemb, FILE * stream) 写入文件内容

 

只使用这4个函数就可以完成基本的文件读写操作了,其它函数可以视为是为了更方便的进行文件读写而引入的。在Linux中,文件不仅仅是指磁盘上的一个file,也有可能是一个设备等,不过都可以以统一的方式进行读写。常用的打开模式有r(读)、w(写)、a(附加)、b(二进制)等。

与标准输入输出流的操作相同,对文件的操作也有以下这些函数:

Name Comment
int fprintf(FILE * stream, const char * format, …) 格式化输出数据至文件
int fscanf(FILE * stream, cosnt char * format, …) 由文件读取格式化输入数据
int putc(int c, FILE * stream) 向文件输出一个字符
int getc(FILE * stream) 由文件读入一个字符
int fputc(int c, FILE * stream) 向文件输出一个字符
int fgetc(FILE * stream) 由文件读入一个字符
int fputs(const char * s, FILE * stream) 向文件输出一串字符串(或比特流)
char * fgets(char * s, int n, FILE * stream) 由文件读入一串字符串(或比特流)

其中putc()与fputc()、getc()与fgetc()的区别在于前者可能是使用宏定义实现的,而后者一定是函数

用于对文件进行修改(如删除文件等)的函数有以下这些:

Name Comment
int remove(const char * filename) 删除文件,成功返回0
int rename(const char * old, const char * new) 更改文件名称或位置,成功返回0
FILE * tmpfile(void) 以wb+形式创建一个临时二进制文件

其中tmpfile()创建的临时文件在调用fclose()关闭时会被自动删除。

对文件流的定位通常使用以下这些函数:

Name Comment
int fseek(FILE * stream, long int offset, int fromwhere) 移动文件流的读写位置,错误返回非0
long int ftell(FILE * stream) 取得文件流的读取位置
void rewind(FILE * stream) 重设读取目录的位置为开头位置
int feof(FILE * stream) 检测文件结束符

whence可设置为SEEK_SET、SEEK_END或SEEK_CUR。

使用这两个函数处理读写文件流操作中的错误:

Name Comment
int ferror(FILE * stream) 检查流是否有错误
void clearerr(FILE * stream) 复位错误标志

与缓冲(Buffer)机制有关的函数常用的有以下这两个:

Name Comment
void setbuf(FILE * stream, char * buf) 把缓冲区与流相联
int fflush(FILE * stream) 更新缓冲区,成功返回0,错误返回EOF

其他流操作

对stderr的操作通过以下函数完成:

Name Comment
void perror(const char * s) 打印出错误原因信息字符串

此函数将上一个函数发生错误的原因输出到stderr,此错误原因依照全局变量errno的值来决定要输出的字符串,errno在<errno.h>中声明。

对字符串也提供了格式化输入输出函数:

Name Comment
int sprintf(char * s, const char * format, …) 格式化字符串复制
int sscanf(const char * s, const char * format, …) 格式化字符串输入

字符处理<ctype.h>

包含字符测试及大小写转换函数。

字符测试

Name Comment
isalpha(c) 是否为字母
isupper(c) 是否为大写字母
islower(c) 是否为小写字母
isdigit(c) 是否为数字
isxdigit(c) 是否为16进制数字(数字 & Af)
isalnum(c) 是否为字母及数字
ispunct(c) 是否为标点符号
isspace(c) 是否为空白字符(空格、\r(CR)、\n(LF)、\t(TAB)、\v(VT)、\f(FF))
iscntrl(c) 是否为控制字符(ASCII 0 ~ 37(0x1F) & 177(0x7F))
isgraph(c) 是否为可显示字符(字母 & 数字 & 标点)
isprint(c) 是否为可打印字符(字母 & 数字 & 标点 & 空白)

大小写转换

Name Comment
tolower(c) 转换为小写
toupper(c) 转换为大写

标准库头文件<stdlib.h>

<stdlib.h> 是C语言中的一个常用的头文件,其全名是“标准库头文件”(Standard Library Header)。该头文件包含了一系列在程序中常用的函数和宏定义。

在其中定义了以下一些常用的类型及常量:

Name Comment
size_t sizeof运算符产生的数据类型,一般是一个无符号整数
wchar_t 一个宽字符的大小
NULL
RANDMAX rand()的最大返回值

下面分类整理一下其中的重要函数。

内存管理函数

最常用的是以下两个函数:

Name Comment
void * malloc(size_t size) 从堆上动态分配内存空间
void free(void * ptr) 释放之前分配的内存空间

还有一些常用的内存控制函数位于<string.h>中。

数学函数

常用函数有:

Name Comment
int abs(int j) int类型数据绝对值
long labs(long j) long类型数据绝对值
int rand(void) 产生一个随机数
void srand(unsigned int seed) 初始化随机数种子

关于rand()与srand()的用法。

字符串转换函数

常用的有以下这3个函数:

Name Comment
int atoi(const char * nptr) 将字符串转换为整数(int)
long atol(const char * nptr) 将字符串转换为长整数(long)
double atof(const char * nptr) 将字符串转换为浮点型数(double)

环境函数

常用的函数有:

Name Comment
int system(const char * string) 执行Shell(或命令行)命令
char * getenv(const char * name) 获取环境变量中的内容
int exit(int stauts) 结束进程

搜索和排序函数

Name Comment
void qsort(void * base, size_t nmemb, size_t size, int (* compar)(const void *, const void *)) 快速排序算法
void * bsearch(const void * key, const void * base, size_t nmemb, size_t size, int (* compar)(const void *, const void *)) 在数组进行二分法查找某一元素,要求数组预先已排好序

在<stdlib.h>中还有一些用于进行多字节字符处理的函数,此处没有列出。

字符串处理<string.h>

<string.h>中除了字符串处理函数,还有一些内存管理函数:

Name Comment
void * memset(void * dest, int c, size_t n) 将一段内存空间填上某值
void * memcpy(void * dest, const void * src, size_t n) 复制一段内存内容
int memcmp(const void * s1, const void * s2, size_t n) 比较两段内存内容
void * memchr(const void * s, int c, size_t n) 在某一段内存范围中查找特定字节

常用的字符串操作函数有:

Name Comment
char * strcat(char * deat, const char * src) 连接两个字符串
char * strcpy(char * dest, const char * src) 复制字符串
int strcmp(const char * s1, const char * s2) 比较两个字符串
size_t strlen(const char * s) 获取一个字符串的长度
char * strtok(char * s1, const char * s2) 分割字符串

以下这些函数用于进行字符串查找:

Name Comment
char * strchr(const char * s, int c) 正向查找一个字符
char * strrchr(const char * s, int c) 反向查找一个字符
char * strstr(const char * s1, const char * s2) 查找一个字符串
char * strpbrk(const char * s1, const char * s2) 查找一个字符集合

断言<assert.h>

在C语言中,断言是一种在程序中检验假设是否成立的有效方法。如果假设的条件为假,程序会立即终止并报告错误。断言是由<assert.h>头文件提供的宏。

此头文件的唯一目的是提供assert(int x)这个宏,如果断言非真,程序会在标准错误流输出错误信息,并调用abort()函数使程序异常终止。

以下是一个断言的例子:

#include <assert.h>

int main() {
    int x = 5;
    assert(x == 5);  // This assertion passes
    assert(x == 10); // This assertion fails, program terminates here
    return 0;
}

类型长度<limits.h>

<limits.h> 是C语言中的一个标准库头文件,它包含了一系列关于数据类型长度的宏定义。

<limits.h> 头文件帮助程序员了解系统中每种数据类型的长度和范围,从而更好地进行编程,避免出现溢出等问题。

常用宏定义有:

Name Comment
CHAR_BIT 一个字节的比特数
SCHAR_MIN 带符号字符最小值
SCHAR_MAX 带符号字符最大值
UCHAR_MAX 无符号字符最大值
CHAR_MIN char的最小值
CHAR_MAX char的最大值
SHRT_MIN 带符号短整型最小值
SHRT_MAX 带符号短整型最大值
USHRT_MAX 无符号短整型最大值
INT_MIN 带符号整形最小值
INT_MAX 带符号整形最大值
UINT_MAX 无符号整形最大值
LONG_MIN 带符号长整形最小值
LONG_MAX 带符号长整形最大值
ULONG_MAX 无符号长整形最大值

标准定义<stddef.h>

<stddef.h> 是C语言中的一个标准库头文件,它主要包含了一些通用类型定义和宏。

定义了一些标准定义,如size_t、wchar_t、NULL等,这些定义也会出现在其他的头文件里。还定义了以下这个宏:

Name Comment
offsetof(type, member) 返回结构体中某一成员相对于结构体起始地址的偏移量

时间操作<time.h>

日期及时间操作。定义了time_t、clock_t及tm这几种类型,常用函数有:

C语言标准库提供了一些处理日期和时间的函数,主要在<time.h>头文件中定义,一些常见的函数包括:

获取时间及相关计算

Name Comment
time_t time(time_t * timer) 获取UNIX时间戳,一般传入NULL
clock_t clock(void) 获取CPU时钟计数
double difftime(time_t time1, time_t time0) 计算时间差,time1 – time0
struct tm * gmtime(const time_t * timer) GMT时间
struct tm * localtime(const time_t * timer) 地方时时间
time_t mktime(struct tm * timeptr) 地方时时间

转换为可阅读的字符串

Name Comment
char * ctime(const time_t * timer) 返回标准时间字符串,地方时时间,等价于asctime(localtime())
char * asctime(const struct tm * timeptr) 返回标准时间字符串
size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *) 返回自定义格式时间字符串

time()

这个函数返回系统的当前日历时间,单位是自1970年1月1日(UNIX纪元)以来的秒数。如果获取时间失败,它会返回-1。

time_t current_time = time(NULL);

ctime()

这个函数将time_t类型的时间转换为本地时间,并以字符串的形式返回。

time_t current_time = time(NULL);
printf("Current time is %s", ctime(&current_time));

difftime()

这个函数计算两个time_t类型时间的差值,单位是秒。

time_t start = time(NULL);
// some time consuming operations...
time_t end = time(NULL);
printf("Elapsed time is %.2f seconds", difftime(end, start));

mktime()

这个函数将结构体struct tm表示的时间转换为time_t类型的时间,结构体struct tm包含了年、月、日、时、分、秒等成员。

struct tm timeinfo = {0};
timeinfo.tm_year = 2020 - 1900;  // years since 1900
timeinfo.tm_mon = 12 - 1;  // months since January [0-11]
timeinfo.tm_mday = 31;  // day of the month [1-31]
timeinfo.tm_hour = 23;  // hours since midnight [0-23]
timeinfo.tm_min = 59;  // minutes after the hour [0-59]
timeinfo.tm_sec = 59;  // seconds after the minute [0-60]
time_t timestamp = mktime(&timeinfo);

浮点数定义<float.h>

<float.h> 是C语言中的一个标准库头文件,它包含了一系列关于浮点数的宏定义,这些宏定义了系统中浮点数的特性和限制。

以下是一些 <float.h> 中常见的宏定义:

  • FLT_RADIX:定义了浮点数的基数,通常为2。
  • FLT_ROUNDS:定义了浮点数的舍入模式。
  • FLT_DIG:定义了float类型的有效数字位数。
  • FLT_EPSILON:定义了00和比1.00大的最小的float类型数之间的差值。
  • FLT_MANT_DIG:定义了float类型的尾数位数。
  • FLT_MAX:定义了float类型的最大值。
  • FLT_MAX_10_EXP:定义了以10为基数的float类型的最大指数。
  • FLT_MAX_EXP:定义了以FLT_RADIX为基数的float类型的最大指数。
  • FLT_MIN:定义了正的float类型的最小值。
  • FLT_MIN_10_EXP:定义了以10为基数的float类型的最小指数。
  • FLT_MIN_EXP:定义了以FLT_RADIX为基数的float类型的最小指数。

类似的,<float.h> 头文件也定义了double类型和long double类型的相关宏。

<float.h> 头文件有助于了解系统中浮点数的特性和限制,从而编写出更加健壮的代码。

数学计算<math.h>

标准数学库,常用函数如下:

三角函数

Name Comment
double sin(double x) 正弦
double cos(double x) 余弦
double tan(double x) 正切
double asin(double x) 反正弦
double acos(double x) 反余弦
double atan(double x) 反正切
double atan2(double y, double x) 计算y/x的反正切

双曲三角函数

Name Comment
double sinh(double x) 双曲正弦
double cosh(double x) 双曲余弦
double tanh(double x) 双曲正切

指数与对数

Name Comment
double exp(double x) e的n次幂
double pow(double x, double y) x的y次幂
double sqrt(double x) 开根号
double log(double x) e为底的对数
double log10(double x) 10为底的对数

取整

Name Comment
double ceil(double x) 向上取整
double floor(double x) 向下取整

其它

Name Comment
double fabs(double x) 计算绝对值

异常处理<errno.h>

C语言没有内建的异常处理机制,但提供了一些函数和变量来处理运行时错误。这些函数和变量在<errno.h>头文件中定义。

  • errno是一个全局变量,当函数调用失败时,系统会设置这个变量的值以表示错误类型。
  • perror()和strerror()函数用于输出错误信息。

以下是一个错误处理的例子:

#include <stdio.h>
#include <errno.h>

int main() {
    FILE *file = fopen("non_existent_file.txt", "r");

    if (file == NULL) {
        perror("Error opening file"); // Output: Error opening file: No such file or directory
        printf("Error code: %d\n", errno);
    }

    return 0;
}

国际化与本地化<locale.h>

<locale.h> 是C语言中的一个标准库头文件,它主要包含了一些与国际化和本地化有关的宏定义、类型和函数。

以下是 <locale.h> 中的一些重要内容:

  • setlocale:这个函数用于设置或查询程序的当前区域设置,这影响了程序中的日期、时间和货币格式,以及字符分类和字符串排序等。
  • localeconv:这个函数返回一个指向结构体的指针,这个结构体包含了当前区域设置中的一些信息,比如小数点字符,货币符号等。
  • lconv:这是一个用于存储区域设置信息的结构体类型。
  • LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME:这些宏用于在调用 setlocale 函数时,指定需要修改或查询的区域设置的类别。

通过使用 <locale.h> 头文件中的函数和宏,程序可以适应不同的语言和地区,这对于开发国际化软件是非常有用的。

非局部跳转<setjmp.h>

在C语言中,非局部跳转,用于从一个深层次嵌套中直接返回至最外层,通过这两个宏完成setjmp()和longjmp()函数实现,这两个函数定义在<setjmp.h>头文件中。

  • setjmp():这个函数通常在主调函数中使用,用于保存程序的当前上下文(包括堆栈和寄存器等状态)到一个类型为jmp_buf的变量中。这个函数在直接调用时返回0。
  • longjmp():这个函数可以在主调函数或被调用的函数中使用,用于恢复由setjmp()保存的上下文。当longjmp()执行后,程序回到最近一次调用setjmp()的地方继续执行,并使得setjmp()返回非零值。

这种机制可以用于处理错误和异常,特别是在嵌套函数调用中处理错误时,可以直接跳转到适当的恢复点,而不必沿着调用链向上逐级返回。

信号<signal.h>

<signal.h> 是C语言中的一个标准库头文件,它包含了一些与信号处理有关的类型、宏定义和函数。

以下是 <signal.h> 中的一些重要内容:

  • signal():这个函数用于改变程序接收到特定信号后的行为。例如,你可以设置当程序接收到SIGINT信号(通常由用户按Ctrl+C产生)时,执行特定的处理函数,或者忽略该信号。
  • raise(int sig):这个函数用于向当前程序发送一个信号,这可能会导致程序的终止,除非对这个信号的处理被改变了。
  • sig_atomic_t:这是一个整数类型,可以在信号处理函数和程序的其他部分之间进行原子操作。
  • SIG_DFL, SIG_IGN, SIG_ERR:这些宏用来指定信号的处理方式,例如默认处理(SIG_DFL),忽略信号(SIG_IGN),或者表示一个错误(SIG_ERR)。
  • SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, 等:这些宏定义了不同的信号代码,代表了不同的事件,比如程序异常终止、浮点错误、非法指令、中断信号、段错误、终止请求等。

使用 <signal.h> 头文件,程序可以捕获并响应各种运行时事件,或者主动发送信号给其他进程。

可变参数<stdarg.h>

在C语言中,可以通过<stdarg.h>头文件提供的宏定义实现可变参数的功能。主要的宏定义有va_start、va_arg、va_end和va_copy。

  • va_start:这个宏初始化一个类型为va_list的变量,使其准备获取可变参数列表。
  • va_arg:这个宏用于获取下一个可变参数的值。
  • va_end:这个宏用于结束可变参数的获取。
  • va_copy:这个宏用于复制可变参数列表。

以下是一个例子,这个函数接受一个整数和一个可变参数列表,整数表示后面参数的数量。

#include <stdarg.h>
#include <stdio.h>

void printNumbers(int count, ...) {
    va_list args;
    va_start(args, count);

    for (int i = 0; i < count; ++i) {
        int value = va_arg(args, int);
        printf("%d ", value);
    }

    va_end(args);
    printf("\n");
}

int main() {
    printNumbers(3, 1, 2, 3);  // 输出:1 2 3
    return 0;
}

这种机制可以用于实现参数数量可变的函数,如printf()和scanf()等函数就使用了这种机制。

NA1

头文件文件 简介说明
<iso646.h> 提供了一种替代C语言中一些操作符的方式,如&&, ||, &, |等,主要用于那些键盘上没有这些字符的情况。
<wchar.h> 为宽字符和宽字符串处理提供支持,包括一些操作宽字符和宽字符串的函数。
<wctype.h> 为宽字符分类和映射提供支持,包括一些函数和宏。

<iso646.h>

<iso646.h> 是C语言中的一个标准库头文件,它主要提供了一些宏定义,用于替代C语言中的一些符号,以便于在不支持ASCII字符集的系统中编写C程序。

以下是 <iso646.h> 中的一些宏定义:

  • and:代表逻辑与操作符 &&。
  • and_eq:代表 &= 操作符。
  • bitand:代表 & 操作符。
  • bitor:代表 | 操作符。
  • compl:代表 ~ 操作符。
  • not:代表 ! 操作符。
  • not_eq:代表 != 操作符。
  • or:代表 || 操作符。
  • or_eq:代表 |= 操作符。
  • xor:代表 ^ 操作符。
  • xor_eq:代表 ^= 操作符。

这些宏使得在不支持ASCII或者某些特殊字符的系统中编写C程序变得更加容易。然而,在现代编程环境中,这个头文件的用途并不大,因为大多数的系统都支持ASCII字符集。

<wchar.h>

<wchar.h> 是C语言的一个标准库头文件,它提供了一系列的宏、类型和函数,用于支持宽字符和宽字符串的操作。这些宽字符和宽字符串是用来表示和处理Unicode字符的,它们比传统的char类型和字符串可以表示更多的字符。

以下是 <wchar.h> 中的一些重要内容:

  • wchar_t:这是宽字符的类型,在各种平台上,它可能是16位或32位的。
  • wint_t:这是宽字符的整数类型。
  • wctrans_t, wctype_t:这些类型用于宽字符的转换和分类。
  • WEOF:这个宏定义了宽字符的文件结束标志。
  • wprintf, wscanf, etc.:这些函数用于宽字符和宽字符串的输入和输出。
  • wctype, iswctype, etc.:这些函数用于宽字符的分类和转换。
  • mbstowcs, wcstombs, etc.:这些函数用于多字节字符串和宽字符串之间的转换。

通过使用 <wchar.h> 头文件,你可以在C程序中使用Unicode字符,这对于国际化软件开发是非常重要的。

<wctype.h>

<wctype.h> 是C语言的一个标准库头文件,它主要提供了一系列用于宽字符分类和转换的函数和宏定义。

以下是 <wctype.h> 中的一些重要内容:

  • wint_t:这是宽字符的整数类型。
  • wctrans_t, wctype_t:这些类型用于宽字符的转换和分类。
  • wctype:这个函数用于返回一个与给定分类名称对应的类型标识符。
  • iswctype:这个函数用于测试一个宽字符是否满足给定的分类。
  • towlower, towupper:这些函数用于将宽字符转换为小写或大写。
  • towctrans:这个函数用于进行宽字符的转换。
  • wctrans:这个函数用于返回一个与给定转换名称对应的转换标识符。

通过使用 <wctype.h> 头文件,你可以在C程序中进行更加细粒度的宽字符分类和转换,这对于处理Unicode字符是非常有用的。

C99

头文件文件 简介说明
<stdint.h> 定义了一套新的整数类型,使得程序员可以在程序中明确指定整数的大小。
<inttypes.h> 包含了一些用于操作整数的函数和宏。
<complex.h> 为复数运算提供支持,包括一些操作复数的函数。
<fenv.h> 为浮点环境访问提供支持,包括一些宏和函数用于处理浮点异常和舍入。
<stdbool.h> 提供了一个宏bool,使C语言能够使用布尔类型(bool)。
<tgmath.h> 这是”type-generic math”的缩写,提供了一种方式可以不区分浮点数的精度(如float,double,long double)来进行数学运算。

<stdint.h>

<stdint.h> 是C语言的一个标准库头文件,它提供了一系列的类型定义和宏,用于表示固定宽度的整数和它们的界限值。

以下是 <stdint.h> 中的一些重要内容:

  • int8_t, int16_t, int32_t, int64_t:这些类型用于表示8位、16位、32位和64位的有符号整数。
  • uint8_t, uint16_t, uint32_t, uint64_t:这些类型用于表示8位、16位、32位和64位的无符号整数。
  • INT8_MIN, INT8_MAX, UINT8_MAX, etc.:这些宏定义了各种整数类型的最小值和最大值。
  • INT8_C, UINT8_C, etc.:这些宏用于构造常量。
  • intptr_t, uintptr_t:这些类型用于存储指针值。

通过使用 <stdint.h> 头文件,你可以在C程序中使用固定宽度的整数类型,这可以使代码的行为更加明确和可预测。

<inttypes.h>

<inttypes.h> 是C语言的一个标准库头文件,它包括 <stdint.h>,并添加了一些额外的宏定义。这些宏定义主要用于处理定宽整数类型的输入和输出。

以下是 <inttypes.h> 中的一些重要内容:

  • PRIx8, PRIu8, PRId8, PRIo8, etc.:这些宏定义了用于 printf 和 scanf 系列函数的格式字符串,用于8位有符号和无符号整数的十六进制、十进制和八进制输出和输入。
  • PRIx16, PRIu16, PRId16, PRIo16, etc.:这些宏适用于16位整数。
  • PRIx32, PRIu32, PRId32, PRIo32, etc.:这些宏适用于32位整数。
  • PRIx64, PRIu64, PRId64, PRIo64, etc.:这些宏适用于64位整数。

imaxabs, imaxdiv, strtoimax, strtoumax, wcstoimax, wcstoumax:这些函数用于处理 intmax_t 和 uintmax_t 类型的数值,这两种类型表示的是最大宽度的有符号和无符号整数。

通过使用 <inttypes.h> 头文件,你可以在C程序中更方便地处理定宽整数类型的输入和输出。

<complex.h>

<complex.h> 是C语言中的一个标准库头文件,它提供了一系列的宏定义和函数,用于复数的操作。

以下是 <complex.h> 中的一些重要内容:

  • complex:这是复数类型,它可以表示一个由实部和虚部组成的复数。
  • I:这是虚数单位,它的平方等于-1。
  • carg, cimag, conj, creal:这些函数用于获取复数的参数、虚部、共轭复数和实部。
  • cabs, cexp, clog, cpow, csqrt:这些函数用于执行复数的各种数学操作,如求绝对值、计算指数、取对数、求幂和求平方根。
  • cacos, casin, catan, ccos, csin, ctan:这些函数用于计算复数的反三角函数和三角函数。
  • cacosh, casinh, catanh, ccosh, csinh, ctanh:这些函数用于计算复数的反双曲函数和双曲函数。

通过使用 <complex.h> 头文件,你可以在C程序中进行复数的各种操作,这对于科学计算和工程计算非常有用。

<fenv.h>

<fenv.h> 是C语言的一个标准库头文件,它提供了一系列的类型定义和函数,用于处理浮点数环境,包括浮点数的舍入模式和异常。

以下是 <fenv.h> 中的一些重要内容:

  • fenv_t:这个类型用于表示整个浮点数环境,包括舍入模式和所有异常的状态。
  • fexcept_t:这个类型用于表示只包含异常状态的部分浮点数环境。
  • FE_DOWNWARD, FE_TONEAREST, FE_TOWARDZERO, FE_UPWARD:这些宏定义了可能的舍入模式。
  • FE_DIVBYZERO, FE_INEXACT, FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW:这些宏定义了可能的浮点数异常。
  • feclearexcept, feraiseexcept, fetestexcept:这些函数用于清除、引发和测试浮点数异常。
  • fegetround, fesetround:这些函数用于获取和设置浮点数的舍入模式。
  • fegetenv, feholdexcept, fesetenv, feupdateenv:这些函数用于操作整个浮点数环境。

通过使用 <fenv.h> 头文件,你可以在C程序中进行更加精细的浮点数操作,这对于科学计算和工程计算非常有用。

<stdbool.h>

<stdbool.h> 是C99和之后版本的C语言的一个标准库头文件,它提供了一个布尔数据类型以及用于表示逻辑真和假的宏。

以下是 <stdbool.h> 中的一些重要内容:

  • bool:这是布尔数据类型。在C语言中,bool是 _Bool 的类型别名。
  • true:这个宏的值是1,用于表示逻辑真。
  • false:这个宏的值是0,用于表示逻辑假。

通过使用 <stdbool.h> 头文件,你可以在C程序中使用布尔数据类型和逻辑值,这可以使代码更加清晰易读。

<tgmath.h>

<tgmath.h> 是C语言的一个标准库头文件,这个头文件包括了 <math.h> 和 <complex.h>,并提供了一些类型通用的数学函数。这意味着,当你使用 <tgmath.h> 中的函数时,这些函数会根据你提供的参数的类型来决定应该执行哪种操作。

以下是 <tgmath.h> 中的一些重要内容:

  • sin, cos, tan, asin, acos, atan, etc.:这些函数可以用于计算实数或复数的三角函数和反三角函数。
  • sinh, cosh, tanh, asinh, acosh, atanh, etc.:这些函数可以用于计算实数或复数的双曲函数和反双曲函数。
  • exp, log, pow, sqrt, etc.:这些函数可以用于计算实数或复数的指数、对数、幂和平方根。

通过使用 <tgmath.h> 头文件,你可以在C程序中使用类型通用的数学函数,这可以使代码更加清晰易读,并可以减少因类型不匹配而导致的错误。

C11

头文件文件 简介说明
<stdalign.h> 用于查询和指定对象的数据结构对齐方式。
<stdatomic.h> 提供了对多线程编程的支持
<stdnoreturn.h> 用于指定非返回函数。
<threads.h> 定义用于管理多个线程以及互斥体和条件变量的函数。
<uchar.h> 用于操作Unicode字符的类型和函数。

对齐<stdalign.h>

C11标准引入了<stdalign.h>头文件,提供了处理对齐问题的宏。其中,alignas和alignof这两个关键字用于设置和获取对齐。

以下是一个对齐的例子:

#include <stdalign.h>
#include <stdio.h>

int main() {
    alignas(double) int a = 10; // 'a' is aligned for a double
    printf("Alignment of 'a': %zu\n", alignof(a)); // Output: Alignment of 'a': 8
    return 0;
}

原子操作<stdatomic.h>

在C11之前,C语言并没有原生支持原子操作。但是在C11标准中,引入了<stdatomic.h>头文件,提供了一些原子操作的函数和宏。下面是一个例子:

#include <stdatomic.h>
#include <stdio.h>

int main() {
    atomic_int atomicCounter = ATOMIC_VAR_INIT(0);

    // 原子增加操作
    atomic_fetch_add(&atomicCounter, 1);
    
    printf("Atomic Counter: %d\n", atomicCounter);  // 输出:1

    return 0;
}

在这个例子中,atomic_fetch_add()函数执行一个原子的加法操作。

请注意,不同的编译器和平台对C11原子操作的支持程度可能不同,且可能需要特定的编译器选项才能使用。

<stdnoreturn.h>

<stdnoreturn.h> 是C11和之后版本的C语言的一个标准库头文件,它定义了一个 noreturn 关键字,该关键字用于声明一个函数不会返回到它的调用者。

以下是 <stdnoreturn.h> 中的主要内容:

  • noreturn:这是一个宏,用于声明一个函数不会返回到它的调用者。如果一个函数被声明为noreturn,那么编译器就可以做出优化,比如跳过调用后面的无用代码,或者删除永远不会执行的代码。

以下是如何在C程序中使用 noreturn 的例子:

#include <stdnoreturn.h>
#include <stdio.h>
#include <stdlib.h>

noreturn void abort_program(void)
{
    printf("Aborting the program...\n");
    exit(1);
}

int main(void)
{
    abort_program();
    return 0; // This code will never be executed.
}

在这个例子中,abort_program 函数被声明为 noreturn,这意味着它不会返回到它的调用者。当编译这段代码时,编译器知道 main 函数中的 return 0; 语句永远不会被执行,因此可能会删除这段代码,或者在其前面插入一条跳转指令,直接跳过这段代码。

<threads.h>

<threads.h> 是C11和之后版本的C语言的一个标准库头文件,它提供了对多线程编程的支持。

以下是 <threads.h> 中的一些重要内容:

  • thrd_t:这个类型用于表示一个线程。
  • mtx_t:这个类型用于表示一个互斥锁,用于在多线程环境中保护共享资源。
  • cnd_t:这个类型用于表示一个条件变量,用于在多线程环境中进行线程间的同步。
  • thrd_create, thrd_join, thrd_detach, thrd_exit, thrd_sleep, thrd_yield:这些函数用于创建、等待、分离、退出、睡眠和让出线程。
  • mtx_init, mtx_lock, mtx_unlock, mtx_destroy:这些函数用于初始化、锁定、解锁和销毁互斥锁。
  • cnd_init, cnd_wait, cnd_signal, cnd_broadcast, cnd_destroy:这些函数用于初始化、等待、发送信号、广播信号和销毁条件变量。

通过使用 <threads.h> 头文件,你可以在C程序中进行多线程编程,这对于实现并发和提高程序效率非常有用。需要注意的是,尽管 <threads.h> 是C11的一部分,但并非所有的编译器都支持它。例如,GCC和Clang在默认情况下不支持 <threads.h>。在这些情况下,你可以考虑使用像pthread这样的线程库。

<uchar.h>

<uchar.h> 是C11和之后版本的C语言的一个标准库头文件,它提供了对Unicode字符的支持,包括UTF-16和UTF-32字符类型,以及与之相关的函数。

以下是 <uchar.h> 中的一些重要内容:

  • char16_t:这个类型用于表示一个UTF-16字符。
  • char32_t:这个类型用于表示一个UTF-32字符。
  • c16rtomb, mbrtoc16:这些函数用于将一个UTF-16字符转换为一个多字节字符,或者将一个多字节字符转换为一个UTF-16字符。
  • c32rtomb, mbrtoc32:这些函数用于将一个UTF-32字符转换为一个多字节字符,或者将一个多字节字符转换为一个UTF-32字符。

通过使用 <uchar.h> 头文件,你可以在C程序中处理UTF-16和UTF-32字符,这对于处理Unicode文本非常有用。需要注意的是,尽管 <uchar.h> 是C11的一部分,但并非所有的编译器都支持它。例如,GCC和Clang在默认情况下不支持 <uchar.h>。在这些情况下,你可以考虑使用其他的库来处理Unicode字符。

标准库使用注意事项

任何包含了标准头的文件都必须遵守两条规则。

  • 该文件不能将头中定义过的宏的名字用于其他目的。例如,如果某个文件包含了<stdio.h>,就不能重新定义NULL了,因为使用这个名字的宏已经在<stdio.h>中定义过了。
  • 具有文件作用域的库名(尤其是typedef名)也不可以在文件层次重定义。因此,一旦文件包含了<stdio.h>,由于<stdio.h>中已经将size_ t定义为typedef名,那么在文件作用域内都不能将size_ t重定义为任何标识符。

上述这些限制是显而易见的,但C语言还有一些其他的限制,可能是你想不到的。

  • 由一个下划线和一个大写字母开头或由两个下划线开头的标识符是为标准库保留的标识符。程序不允许为任何目的使用这种形式的标识符。
  • 由一个下划线开头的标识符被保留用作具有文件作用域的标识符和标记。除非在函数内部声明,否则不应该使用这类标识符。
  • 在标准库中所有具有外部链接的标识符被保留用作具有外部链接的标识符。特别是所有标准库函数的名字都被保留。因此,即使文件没有包含<stdio.h>,也不应该定义名为printf的外部函数,因为在标准库中已经有一个同名的函数了。这些规则对程序的所有文件都起作用,不论文件包含了哪个头。虽然这些规则并不总是强制性的,但不遵守这些规则可能会导致程序不具有可移植性。

上面列出的规则不仅适用于库中现有的名字,也适用于留作未来使用的名字。至于哪些名字是保留的,完整的描述太冗长了,你可以在C标准的 “future library directions” 中找到。例如,C保留了以str和一个小写字母开头的标识符,从而具有这类名字的函数就可以被添加到 <string.h> 头中。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注