C 库函数主要指那些由美国国家标准协会(ANSI)或国际标准化组织(ISO)发布的标准中规定的库函数,按照标准 C 的要求来进行 C 语言编程是很重要的,因为这样你的代码才有可能跨平台使用。
最早的 C89
中有15个标准头文件:
assert.h ctype.h errno.h float.h limits.h
locale.h math.h setjmp.h signal.h stdarg.h
stddef.h stdio.h stdlib.h string.h time.h
随后的 C95
增加了3个标准头文件:
iso646.h wchar.h wctype.h
其次 C99
增加了6个标准头文件:
complex.h fenv.h inttypes.h stdbool.h stdint.h tgmath.h
最新的 C11
又增加了5个标准头文件:
stdalign.h stdatomic.h stdnoreturn.h threads.h uchar.h
所以目前总共有29个标准头文件,但是大部分的编译器并没有支持全部的C标注,所以目前我们在这里讨论的主要还是 C89
中的15个标准头文件。
C89
的15个标准头文件的主要内容如下:
头文件 | 说明 | 头文件 | 说明 | 头文件 | 说明 |
---|---|---|---|---|---|
assert.h | 断言相关 | ctype.h | 字符类型判断 | errno.h | 标准错误机制 |
float.h | 浮点限制 | limits.h | 整形限制 | locale.h | 本地化接口 |
math.h | 数学函数 | setjmp.h | 非本地跳转 | signal.h | 信号相关 |
stdarg.h | 可变参数处理 | stddef.h | 宏和类型定义 | stdio.h | 标准I/O |
stdlib.h | 标准工具库 | string.h | 字符串和内存处理 | time.h | 时间相关 |
有人说,标准库(C89)——也就是ISO C——可以分为3组,根据这3组可以分出三种层次的程序员:
作为一个走在优秀程序员路上的 XX 程序员,这里简单把三个层次分开说一下。
ctype.h 这个头文件主要定义了一批 C 语言字符分类函数,所有的函数都只有一个参数,且参数和返回值均为 int
类型。下面是简单的函数介绍:
函数名 | 说明 | 函数名 | 说明 | 函数名 | 说明 |
---|---|---|---|---|---|
isalpha | 是否为字母 | isdigit | 是否为数字 | isalnum | 是否为数字或字母 |
iscntrl | 是否为控制字符 | isgraph | 是否为图形文字 | isupper | 是否为大写字母 |
islower | 是否为小写字母 | tolower | 转换为小写字母 | toupper | 转换为大写字母 |
isprintf | 是否为可打印字符 | ispunct | 是否为标点符号 | isspace | 是否为空白 |
isxdigit | 是否为十六进制的数字 |
stdio.h 这个头文件应该是大多数人接触 C 语言的时候第一个认识的 C 库函数。这个函数最常用,但是也很复杂。它定义了三种类型,一些宏和很多的输入输出函数。
size_t
是由 sizeof
关键字产生的无符号整类型。FILE
是一个结构体类型,记录了控制流需要的所有信息,包括它的文件定位符、指向相关缓冲的指针、记录是否发生了读/写错误的错误提示符和记录文件是否结束的文件结束符。 fpos_t
包含可以唯一指定文件中的每一个位置所需的所有信息。NULL
空值 _IOFBF
表示完全缓冲 _IOLBF
表示线缓冲 _IONBF
表示无缓存BUFSIZ
是setbuf
函数所使用的缓冲区的大小EOF
是负整数,该表达式由几个函数返回来说明文件的结束,即一个流输入结束了(END OF FILE)FOPEN_MAX
(20)同时打开的文件的最大数量FILENAME_MAX
文件名的最大长度L_tmpnam
整数,最大长度的临时文件名SEEK_CUR
取得目前文件位置 SEEK_END
将读写位置移到文件尾时 SEEK_SET
将读写位置移到文件开头TMP_MAX
表示tmpnam
函数可以生成的单独文件名的最大数目`stderr
标准错误流,默认为屏幕, 可输出到文件 stdin
标准输入流,默认为键盘 stdout
标准输出流,默认为屏幕 int remove(const char *filename);
删除文件 int rename(const char *old, const char *new);
重命名文件 FILE *tmpfile(void);
创建一个临时的二进制文件,并通过模式“wb+”打开。 char *tmpname(char *s);
生成一个字符串,这个字符串是一个有效的文件名。 FILE* fopen(const char *filename, const char *mode);
打开名字为 filename
指向的文件,并且把这个文件和一个流相关联。 FILE* freopen(const char *filename, const char *mode, FILE *stream);
打开名字为 filename
指向的文件,并且把它和 stream
指向的流关联在一起。 int fclose(FILE *stream);
使 stream
指向的流被清空,并且和流相关联的文件被关闭。 int fflush(FILE *stream);
对 stream
指向的流执行清空行为。 void setbuf(FILE *stream, char *buf);
除了没有返回值,等价于函数 setvbuf
。 void setvbuf(FILE *stream, char *buf, int mode, size_t size);
设定 stream
缓冲的方式。 int fprintf(FILE *stream, const char *format, ...);
int printf(const char *format, ...);
int sprintf(char *s, const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int scanf(const char *format, ...);
int sscanf(char *s, const char *format, ...);
EOF
;否则返回赋值的输入项的数目。 int vfprintf(FILE *stream, const char *format, va_list arg);
int vprintf(const char *format, va_list arg);
int vsprintf(char *s, const char *format, va_list arg);
printf
函数,不过可变参数表用 arg
代替。 int fgetc(FILE *stream);
从 stream
指向的输入流中读取下一个字符。 int getc(FILE *stream);
等价于函数 fgetc
。 int getchar(void);
等价于用参数 stdin
调用函数 getc
。 int fputc(int c, FILE *stream);
把字符写到指向的输出流中指定的位置处。 int putc(int c, FILE *stream);
等价于 fputc。
int putchar(int c);
等价于把stdout作为第二个参数调用的 putc。
char *fgets(char *s, int n, FILE *stream);
从 stream
指向的流中读取字符。 int fputs(const char *s, FILE *stream);
把 s
指向的串写入 stream
指向的流中。 char *gets(char *s);
从 stdin
指向的输入流中读取若干个字符,并将其保留到 s
指向的数组中,直到遇到文件结束符或者读取一个换行符。 int puts(const char *s);
把 s
指向的串写到 stdout
指向的流中,并且在输出最后添加一个换行符。 int ungetc(int c, FILE *stream);
把 c
指定的字符(转换为 unsigned char
类型)退回到 stream
指向的输入流中。 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
stream
指向的流中读取最多 nmemb
个元素到 ptr
指向的数组中 。 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
ptr
指向的数组中读取最多 nmemb
个元素并将其写到 stream
指向的流中。 int fgetpos(FILE *stream, fpos_t *pos);
stream
指向的流的文件定位符的当前值存储到 pos
指向的对象中。 int fsetpos(FILE *stream, const fpos_t *pos);
pos
指向的对象的值来设置 stream
指向的流的文件定位符, long int ftell(FILE *stream);
stream
指向的流的文件定位符的当前值。 int fseek(FILE *stream, long int offset, int whence);
stream
指向的流设置文件定位符。 void rewind(FILE *stream);
stream
指向的流的文件定位符设置在文件的开始位置,等价于 (void)fseek(stream, 0L, SEEK_SET);
,只不过流的错误指示符也被清零。void clearerr(FILE *stream);
stream
指向的流的文件结束符和错误指示符。 int feof(FILE *stream);
stream
指向的流的文件结束符。当且仅当 stream
流设置了文件结束符时函数返回一个非0值。 int ferror(FILE *stream);
stream
指向的流的错误指示符。当且仅当 stream
流设置了错误指示符时函数返回一个非0值。 void perror(const char *s);
errno
中的错误编号转换为一条错误消息。是标准工具库。包含了C语言的中最常用的系统函数
NULL
空EXIT_FAILURE
失败状态码EXIT_SUCCESS
成功状态码RAND_MAX rand
的最大返回值MB_CUR_MAX
多字节字符中的最大字节数size_t
是由 sizeof
关键字产生的无符号整类型。wchar_t
是一个整型,标识一个宽字节字符,例如 L’x’
的类型就是 wchar_t
。div_t
是结构体类型 作为 div
函数的返回类型ldiv_t
是一个结构类型,是函数 ldiv
的返回值类型。double atof(const char *nptr);
将字符串转换成浮点型数 int atoi(const char *nptr);
将字符串转换成整型数 long int atol(const char *nptr);
将字符串转换成长整型数 double strtod(const char *nptr, char **endptr);
将字符串转换成浮点数 long int strtol(const char *nptr, char **endptr, int base);
将字符串转换成长整型数 unsigned long int strtoul(const char *nptr, char **endptr, int base);
将字符串转换成无符号长整型数 void *calloc(size_t nmemb, size_t size);
配置内存空间 void free(void *ptr);
释放原先配置的内存 void *malloc(size_t size);
配置内存空间 void *realloc(void *ptr, size_t size);
重新分配主存int atexit(void (*func)(void));
void exit(int status);
void abort(void);
char *getenv(const char *name);
int system(const char *string);
void bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compare)(const void , const void *));
nmemb
个元素的数组,来查找与 key
指向的对象匹配的元素, void qsort(void base, size_t nmemb, size_t size, int (*compare)(const void , const void *));
compare
所指向的比较函数将数组内容排列成升序。int abs(int j);
计算整型数的绝对值 long int labs(long int j);
将两个整数相除, 返回商和余数 div_t div(int number, int denom);
取长整型绝对值 ldiv_t ldiv(long int number, long int denom);
两个长整型数相除, 返回商和余数 int rand(void);
随机数发生器 void srand(unsigned int seed);
设置随机数种子int mblen(const char *s, size_t n);
根据locale的设置确定字符的字节数 int mbtowc(wchar_t *pwc, const char *s, size_t n);
把多字节字符转换为宽字符 int wctomb(char *s, wchar_t wchar);
把宽字符转换为多字节字符 size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n);
把多字节字符串转换为宽字符串 size_t wcstombs(char *s, const wchar_t *pwcs, size_t n);
把宽字符串转换为多字节字符串包含了C语言的最常用的字符串操作函数
NULL
空size_t
void *memmove(void *s1, const void *s2, size_t n);
void *memcpy(void *s1, const void *s2, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
void *memchr(const void *s, int c, size_t n);
void *memset(void *s, int c, size_t n);
char *strcpy(char *s1, const char *s2);
char *strcat(char *s1, const char *s2);
int strcmp(const char *s1, const char *s2);
char *strchr(const char *s, int c);
char *strrchr(cosnt char *s, int c);
char *strstr(const char *s1, const char *s2);
size_t strspn(const char *s1, const char *s2);
size_t strcspn(const char *s1, const char *s2);
char *strpbrk(const char *s1, const char *s2);
char *strtok(char *s1, const char *s2);
char *strncpy(char *s1, const char *s2, size_t n);
char *strncat(char *s1, const char *s2, size_t n);
int strncmp(const char *s1, const char *s2, size_t n);
size_t strlen(const char *s);
char *strerror(int errnum);
int strcoll(const char *s1, const char *s2);
size_t strxfrm(char *s1, const char *s2, size_t n);
这个头文件只定义了一个宏:assert
如果条件为假,assert将输出一些信息并调用abort函数退出程序。
一个可能的实现:
C++
#define NDEBUG
#include
int main(int argc, char* argv[]) {
int a = 12;
int b = 24;
assert( a > b );
printf("a is larger than b!");
return 0;
}
上面的程序会发现程序中止,printf
并未执行,且有这样的输出:main: Assertion 'a > b' failed.
原因就是因为 a
其实小于 b
,导致断言失败,assert
输出错误信息,并调用 abort()
中止了程序执行。
头文件定义了整型变量的一些极限值和设置。
CHAR_BIT
一个ASCII字符长度 SCHAR_MIN
字符型最小值
SCHAR_MAX
字符型最大值 UCHAR_MAX
无符号字符型最大值
CHAR_MIN
/CHAR_MAX
char字符的最大最小值,如果char字符正被表示有符号整数。它们的值就跟有符号整数一样。否则char字符的最小值就是0,最大值就是无符号char字符的最大值。
MB_LEN_MAX
一个字符所占最大字节数SHRT_MIN
最小短整型
SHRT_MAX
最大短整形 USHRT_MAX
最大无符号短整型
INT_MIN
最小整型 INT_MAX
最大整形
UINT_MAX
最大无符号整型 LONG_MIN
最小长整型
LONG_MAX
最大长整型 ULONG_MAX
无符号长整型
此头文件定义了3个类型和2个宏,其中一些在其他头文件中也有定义。
ptrdiff_t
两个指针相减的结果的类型,有符号整型,一般来说是int或long的typedef。 size_t
是sizeof操作符的结果的类型,无符号整型。 wchar_t
是一个整型,标识一个宽字节字符,例如L’x’的类型就是wchar_t。NULL
空指针常量。 offsetof(type, member-designator)
展开为一个size_t类型的整值常量表达式。作时间的函数。
定义了两个宏
NULL
CLOCKS_PER_SEC
使 clock
函数的返回值 CLOCKS_PER_SEC
的单位是秒定义了四个类型
声明的类型有size_t、clock_t、time_t和struct tm。
size_t
在 stddef.h
中已介绍过。 clock_t
是 clock
函数的返回值类型time_t
是 time
函数的返回值类型,struct tm
保存了一个日历时间的各组成部分,比如年月日时分秒等。定义了两类函数
time_t time(time_t *timer);
clock_t clock(void);
struct tm *gmtime(const time_t *timer);
把日期和时间转换为(GMT)时间 struct tm *localtime(const time_t *timer);
取得当地目前时间和日期 char *ctime(const time_t *timer);
把日期和时间转换为字符串 time_t mktime(struct tm *timeptr);
将时间结构数据转换成经过的秒数 char *asctime(const struct tm *timeptr);
将时间和日期以字符串格式表示 size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr);
将时间格式化double difftime(time_t time1, time_t time0);
time1-time0,
返回以秒为单位的差值。这个函数似乎简单到没有存在的必要,直接执行减法不就得了。float 头文件类似 limit 头文件,主要是浮点型数值的相关定义。
在所有实例里FLT
指的是float
,DBL
是double
,LDBL
指的是long double
.
变量 | 定义 |
---|---|
FLT_ROUNDS | 定义浮点型数值四舍五入的方式,-1是不确定,0是向0,1是向最近,2是向正无穷大,3是负无穷大 |
FLT_RADIX 2 | 定义指数的基本表示(比如base-2是二进制,base-10是十进制表示法,16是十六进制) |
FLT_MANT_DIG,DBL_MANT_DIG,LDBL_MANT_DIG | 定义数值里数字的个数 |
FLT_DIG 6,DBL_DIG 10,LDBL_DIG 10 | 在四舍五入之后能不更改表示的最大小数位 |
FLT_MIN_EXP,DBL_MIN_EXP,LDBL_MIN_EXP | FLT_RADIX 的指数的最小负整数值 |
FLT_MIN_10_EXP -37,DBL_MIN_10_EXP -37,LDBL_MIN_10_EXP -37 | 10进制表示法的的指数的最小负整数值 |
FLT_MAX_EXP ,DBL_MAX_EXP ,LDBL_MAX_EXP | FLT_RADIX 的指数的最大整数值 |
FLT_MAX_10_EXP +37 ,DBL_MAX_10_EXP ,LDBL_MAX_10_EXP +37 +37 | 10进制表示法的的指数的最大整数值 |
FLT_MAX 1E+37,DBL_MAX 1E+37,LDBL_MAX 1E+37 | 浮点型的最大限 |
FLT_EPSILON 1E-5,DBL_EPSILON 1E-9,LDBL_EPSILON 1E-9 | 能表示的最小有符号数 |
math.h是 C 语言内的数学函数库。
函数 | 含义 | 函数 | 含义 |
---|---|---|---|
三角函数 | |||
double sin(double x); | 正弦 | double cos(double x); | 余弦 |
double tan(double x); | 正切 | *cot三角函数,可以使用tan(PI/2-x)来实现。 | |
反三角函数 | |||
double asin(double x); | 结果介于[-PI/2, PI/2] | double acos(double x); | 结果介于[0, PI] |
double atan(double x); | 反正切(主值), 结果介于[-PI/2, PI/2] |
double atan2(double y,double); | 反正切(整圆值), 结果介于[-PI, PI] |
双曲三角函数 | |||
double sinh(double x); | 计算双曲正弦 | double cosh(double x); | 计算双曲余弦 |
double tanh(double x); | 计算双曲正切 | ||
指数与对数 | |||
double exp(double x); | 求取自然数e的幂 | double sqrt(double x); | 开平方 |
double log(double x); | 以e为底的对数 | double log10(double x); | 以10为底的对数 |
double pow(double x, double y); | 计算以x为底数的y次幂 | float powf(float x, float y); | 与pow一致,输入与输出皆为浮点数 |
取整 | |||
double ceil(double); | 取上整 | double floor(double); | 取下整 |
标准化浮点数 | |||
double frexp(double f, int *p); | 标准化浮点数, f = x * 2^p, 已知f求x, p ( x介于[0.5, 1] ) |
double ldexp(double x, int p); | 与frexp相反, 已知x, p求f |
取整与取余 | |||
double modf(double, double*); | 将参数的整数部分通过指针回传, 返回小数部分 | double fmod(double, double); | 返回两参数相除的余数 |
error定义了通过错误码来返回错误信息的宏:
errno
宏定义为一个 int
型态的左值, 包含任何函数使用 errno
功能所产生的上一个错误码。
一些表示错误码,定义为整数值的宏:
EDOM
源自于函数的参数超出范围,例如 sqrt(-1)
ERANGE
源自于函数的结果超出范围,例如 strtol("0xfffffffff",NULL,0)
EILSEQ
源自于不合法的字符顺序,例如 wcstombs(str, L"\xffff", 2)
local 定义了区域设置相关的函数和相关的宏以及类型定义。
定义了一个类型 struct lconv;
其中几个比较重要的变量是
char *decimal_point;
格式化非货币量中的小数点字符
char *thousands_sep;
用来对格式化的非货币量中小数点前面的数字进行分组的字符
char *grouping;
用来说明格式化的非货币量中每一组数字的数目
C
typedef struct {
char *decimal_point;
char *thousands_sep;
char *grouping;
char *int_curr_symbol;
char *currency_symbol;
char *mon_decimal_point;
char *mon_thousands_sep;
char *mon_grouping;
char *positive_sign;
char *negative_sign;
char int_frac_digits;
char frac_digits;
char p_cs_precedes;
char p_sep_by_space;
char n_cs_precedes;
char n_sep_by_space;
char p_sign_posn;
char n_sign_posn;
} lconv
定义了两个函数
struct lconv *localeconv(void);
用于返回当前区域中的数字和货币信息
char *setlocale(int category, const char *locale);
用于设置或返回当前的区域特性
定义了几个宏
参数 | 说明 |
---|---|
LC_ALL | 设置所有信息 |
LC_COLLATE | 影响strcoll和strxfrm函数 |
LC_CTYPE | 影响所有字符函数 |
LC_MONETARY | 影响由 localeconv 函数提供的货币信息 |
LC_NUMERIC | 影响十进制小数格式和 localeconv 函数提供的信息 |
LC_TIME | 影响 strftime 函数 |
setjmp 定义了一种特别的函数调用和函数返回顺序的方式。它允许程序流程从一个深层嵌套的函数中返回。
typedef int jmp_buf[16];
int setjmp(jmp_buf);
void longjmp(jmp_buf, int);
非本地跳转的原理非常简单:
setjmp(j)
设置 jump 点,用正确的程序上下文填充 jmp_buf
对象 j
。这个上下文包括程序存放位置,栈和框架指针,其它重要的寄存器和内存数据。当初始化完 jump
的上下文,setjmp()
返回0值。 longjmp(j,r)
的效果就是一个非局部的 goto
或 长跳转 到由 j
描述的上下文处(也就是原来调用 setjmp(j)
处)。当作为长跳转的目标而被调用时,setjmp()
返回 r
或 1
(如果 r
设为0
的话)。(记住,setjmp()
不能在这种情况时返回0) setjmp()
让你知道它正在被怎么使用。当设置 j
时,setjmp()
如你期望地执行;但当作为长跳转的目标时,setjmp()
就从外面 唤醒 它的上下文。你可以用 longjmp()
来终止异常,用 setjmp()
标记相应的异常处理程序。一个简单的例子如下:
````C++
#include
#include
static jmp_buf buf;
void second(void) {
printf("second\n");
longjmp(buf,1);
}
void first(void) {
second();
printf("first\n");
}
int main() {
if ( ! setjmp(buf) ) {
first();
} else {
printf("main\n");
}
return 0;
}
````
程序首先执行了 setjmp
,初始化了 buf
,函数返回了0,进入 first
函数,执行了 second
函数,打印”second”,随后执行 longjmp
函数跳转到 setjmp
的地方,此时setjmp
的返回值为 1,所以直接进入 else
打印 “main” ,函数结束。
signal提供了一些定义和函数用来处理执行过程中产生的信号。
定义了相关的宏
SIG_
开头的宏用于定义信号处理函数: SIG_DFL
默认信号处理函数。SIG_ERR
表示一个错误信号,当signal函数调用失败时的返回值。SIG_IGN
信号处理函数,表示忽略该信号。 SIG
开头的宏是用来在下列情况下,用来表示一个信号代码:SIGABRT
异常终止(abort
函数产生)。SIGFPE
浮点错误(0作为除数产生的错误,非法的操作)。SIGILL
非法操作(指令)。SIGINT
交互式操作产生的信号(如CTRL - C)。SIGSEGV
无效访问存储(片段的非法访问,内存非法访问)。SIGTERM
终止请求。定义了一个变量
typedef sig_atomic_t
sig_atomic_t
类型是int类型,用于接收 signal
函数的返回值。
定义了两个函数
void(*signal(int sig,void (*func)(int)))(int);
sig
表示一个信号代码(相当于暗号类别),即是上面所定义的 SIG
开头的宏。当有信号出现(即当收到暗号)的时候,参数 func
所定义的函数就会被调用。int raise(int sig);
sig
。信号参数为 SIG
开头的宏。stdarg定义了一些宏,当函数参数未知时去获取函数的参数
变量:typedef va_list
通过 stdarg
宏定义来访问一个函数的参数表,参数列表的末尾会用省略号省略
宏:
void va_start(va_list ap, last_arg);
用va_arg和va_end宏初始化参数ap,last_arg是传给函数的固定参数的最后一个,省略号之前的那个参数注意va_start必须在使用va_arg和va_end之前调用
type va_arg(va_list ap, type);
用type类型扩展到参数表的下个参数
注意ap必须用va_start初始化,如果没有下一个参数,结果会是undefined
void va_end(va_list ap);
允许一个有参数表(使用va_start宏)的函数返回,如果返回之前没有调用va_end,结果会是undefined。参数变量列表可能不再使用(在没调用va_start的情况下调用va_end)