前言:标准C语言,有一个C标准库(C standard library / ISO C library / libc)。标准库的意义,在于提供语言标准之外的一些既有实现,来帮助程序员更好的编写程序。使用标准库中的类型、宏或函数,就类似于平时我们说话时会引用一些历史典故和约定俗语更好的表达自己。同时,相比于其他语言,C的标准库是非常小的,这使得提供C的运行环境或移植C程序更加容易。最早的C语言和标准库是C89版由1989年发布,历届版本:有C89 / C90 / C95 / C99 / C11 / C18。
一、assert.h
assert.h 常用于防御式编程,防御式编程是提高软件质量技术的有益辅助手段。防御式编程的主要思想:子程序应该不因传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据。这种思想是将可能出现的错误造成的影响控制在有限的范围内。断言 assert 就是声明某种东西为真,该函数多用于调试。
assert.h中只有一个assert宏,用于为程序增加诊断功能,表达式如下:
void assert (int expression);
由此说明aserrt 是一个宏而非函数,实际并不具有函数原型。当它被执行时,判断 expression 的真假,如果为假,它就向标准错误打印一条诊断信息并终止程序。当消除所有预期错误后,可以通过 #define NDEBUG 消除所有断言。
例如:
#include <stdio.h>
#include <assert.h>
//#define NDEBUG
void dialog(int *pr){
assert( pr != NULL );
printf("%d", *pr);
}
int main(){
int i = 20;
int *pp = NULL, *bb = NULL;
pp = &i;
dialog(pp);
dialog(bb);
return 0;
}
debug后控制台会输出:
> Assertion failed: pr != NULL, file c:\\...\\....\\...\TTC.c, line 9
注意:
assert 只有在 Debug 调试中才有效,如果编译为 Release 版本则被忽略。
频繁的调用会极大的影响程序的性能,增加额外的开销。
每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败,
在调试结束后,可以通过在包含 #include 的语句之前插入 #define NDEBUG 来禁用 assert 调用。
二、ctype.h
ctype.h定义了一批 C 语言字符分类函数,用于测试字符是否属于特定的字符类别,如字母字符、控制字符等等,和大小写转换函数。既支持单字节字符,也支持宽字符。
(1)isalnum
表达式:int isalnum ( int c )
功能: 判断字符c是否为字母或数字
解析: 当 c 为‘0’ ~ ‘9’ 或‘a’ ~ ‘z’或‘A’ ~ ‘Z’时,返回非零值,否则返回零。
(2)isalpha
表达式:int isalnum ( int c )
功能: 判断字符 c 是否为英文字母
解析: 当 c 为英文字母 ‘a’ ~ ‘z’ 或 ‘A’ ~ ‘Z’ 时,返回非零值,否则返回零。
(3)isblank
表达式:int isblank ( int c )
功能: 判断字符 c 是否为 ‘\t’ 或 ’ ’
解析: 当 c 为 ‘\t’ 或 ’ ’ 时,返回非零值,否则返回零。
(4)iscntrl
表达式:int iscntrl ( int c )
功能: 判断字符 c 是否为控制字符
解析: 当字符 c 在 0x00(NULL) ~ 0x1F(US) 之间或等于 0x7F(DEL) 时,返回非零值,否则返回零。
(4)iscntrl
表达式:int iscntrl ( int c )
功能: 判断字符 c 是否为控制字符
解析: 当字符 c 在 0x00(NULL) ~ 0x1F(US) 之间或等于 0x7F(DEL) 时,返回非零值,否则返回零。
(5)isdigit
表达式:int isdigit ( int c )
功能: 判断字符 c 是否为数字
解析: 当c为数字 ‘0’ ~ ‘9’ 时,返回非零值,否则返回零。
(6)isgraph
表达式:int isgraph ( int c )
功能: 检查 ch 是否为可显示字符( ASCII 值 > 0x1f, 但不包括 0x7f(DEL) ),不包括空格。
解析: 当 c 满足上述条件,返回非零值,否则返回零。
(7)islower
表达式:int islower ( int c )
功能: 判断字符 c 是否为小写英文字母
解析: 当 c 为小写英文字母( ‘a’ ~ ‘z’ )时,返回非零值,否则返回零。
(8)isprint
表达式:int isprint ( int c )
功能: 检查 c 是否为可打印字符( ASCII 值 > 0x1f, 但不包括 0x7f(DEL) )
解析: 当 c 满足上述条件,返回非零值,否则返回零。
(9)ispunct
表达式:int ispunct ( int c )
功能: 检查 c 是否属于 isgraph 范围(但不包括 isalnum 范围)
解析: 当 c 满足上述条件,返回非零值,否则返回零。
(10)isspace
表达式:int isspace ( int c )
功能: 判断字符 c 是否为控制字符
解析: 当 c 满足上述条件,返回非零值,否则返回零。
(11)isupper
表达式:int isupper ( int c )
功能: 判断字符 c 是否为 ’ ', ‘\t’, ‘\n’, ‘\v’, ‘\f’, ‘\r’
解析: 当c为大写英文字母(A-Z)时,返回非零值,否则返回零
(12)isxdigit
表达式:int isxdigit ( int c )
功能: 检查 ch 是否为一个 16 进制的数学字符(即 ‘0’ ~ ‘9’ 或 ‘A’ ~ ‘F’ 或 ‘a’ - ‘f’)
解析: 当 c 满足上述条件,返回非零值,否则返回零。
(13)tolower
表达式:int tolower ( int c )
功能: 将字符 c 转换为小写英文字母
解析: 如果 c 为大写英文字母,则返回对应的小写字母;否则返回原来的值。
(14)toupper
表达式:int toupper ( int c )
功能: 将字符 c 转换为大写英文字母
解析: 如果 c 为小写英文字母,则返回对应的大写字母;否则返回原来的值。
三、errno.h
errno.h是为了提供了一种错误报告机制。比如,一个函数调用fopen()发生了错误,它可能就会去修改errno的值,这样外部的代码可以通过判断errno的值来区分fopen()内部执行时是否发生错误,并根据errno值的不同来确定具体的错误类型。通过系统调用设置的,在错误事件中的某些库函数表明了什么发生了错误。该宏扩展类型为 int 的可更改的左值,因此它可以被一个程序读取和修改。
errno.h中的库宏:
序号 | 库宏 | 说明 |
1 | extern int errno | 通过系统调用设置的宏,在错误事件中的某些库函数表明了什么发生了错误 |
2 | EDOM Domain Error | 表示一个域错误,它在输入参数超出数学函数定义的域时发生,errno 被设置为 EDOM |
3 | ERANGE Range Error | 表示一个范围错误,它在输入参数超出数学函数定义的范围时发生,errno 被设置为 ERANGE |
extern int errno:
通过系统调用设置的,在错误事件中的某些库函数表明了什么发生了错误。
例如:
#include <string.h>
#include <stdio.h>
#include <errno.h>
extern int errno ;
int main ()
{
FILE *fb;
fb = fopen("TEST.txt", "r");
if( fb == NULL )
{
fprintf(stderr, "value of errno: %d\n", errno);
fprintf(stderr, "error opening file: %s\n", strerror(errno));
}
else
{
fclose(fb);
}
return 0;
}
TEST.txt文件不存在,编译运行后输出:
value of errno: 2
error opening file: No such file or directory
EDOM Domain Error:
表示一个域错误,它在输入参数超出数学函数定义的域时发生,errno 被设置为 EDOM。
例如:
#include <math.h>
#include <stdio.h>
#include <errno.h>
int main()
{
double value;
errno = 0;
value = sqrt(-20);
if(errno == EDOM)
{
printf("invalid value \n");
}
else
{
printf("valid value\n");
}
errno = 0;
value = sqrt(20);
if(errno == EDOM)
{
printf("Invalid value\n");
}
else
{
printf("Valid value\n");
}
return 0;
}
编译运行后输出:
invalid value
valid value
ERANGE Range Error:
表示一个范围错误,它在输入参数超出数学函数定义的范围时发生,errno 被设置为 ERANGE。
例如:
#include <math.h>
#include <stdio.h>
#include <errno.h>
int main()
{
double value;
double as;
as = 2.000000;
value = log(as);
if( errno == ERANGE )
{
printf("Log(%f) is out of range\n", as);
}
else
{
printf("Log(%f) = %f\n", as, value);
}
as = 1.000000;
value = log(as);
if( errno == ERANGE )
{
printf("Log(%f) is out of range\n", as);
}
else
{
printf("Log(%f) = %f\n", as, value);
}
as = 0.000000;
value = log(as);
if( errno == ERANGE )
{
printf("Log(%f) is out of range\n", as);
}
else
{
printf("Log(%f) = %f\n", as, value);
}
return 0;
}
编译运行后输出:
Log(2.000000) = 0.693147
Log(1.000000) = 0.000000
Log(0.000000) = -inf
四、float.h
float.h用以描述浮点数并提供了浮点算术的基本信息,这些信息大多是通过宏的形式定义的。
一个浮点数可以表示为:
x=sbek=1∑pfkb−k,emin<e≤emax
s为符号,取值为± 1 \pm1±1;b是大于1的整数,表示基数;e为指数;p为精度;fk表示有效数字,M=∑k=1pfkb−k
简化式为:
x=s×M×be,emin<e≤emax
一个浮点数的组成也就是:浮点数=(符号)精度*基数^指数
float.h的define的值中的宏,绝大多数可分为三类,分别对应float、double和long double类型:
float | double | long double |
FLT_MANT_DIG | DBL_MANT_DIG | LDBL_MANT_DIG |
FLT_DIG | DBL_DIG | LDBL_DIG |
FLT_MIN_EXP | DBL_MIN_EXP | LDBL_MIN_EXP |
FLT_MAX_EXP | DBL_MAX_EXP | LDBL_MAX_EXP |
FLT_MIN_10_EXP | DBL_MIN_10_EXP | LDBL_MIN_10_EXP |
FLT_MAX_10_EXP | DBL_MAX_10_EXP | LDBL_MAX_10_EXP |
FLT_MAX | DBL_MAX | LDBL_MAX |
FLT_MIN | DBL_MIN | LDBL_MIN |
FLT_EPSILON | DBL_EPSILON | LDBL_EPSILON |
float.h的宏解析:
宏 | 数值 | 说明 |
FLT_ROUNDS | -1或0或1或2或3 | 定义浮点加法的舍入模式 -1是无法确定 0是趋向于零 1是去最近的值 2是趋向于正无穷 3是趋向于负无穷 |
FLT_RADIX | 2或10或16 | 这个宏定义了指数表示的基数。 2是二进制 10是十进制 16是十六进制 |
FLT_MANT_DIG | 24 | float类型在FLT_RADIX基数中的位数 |
FLT_DIG | 6 | float类型舍入后不会改变表示的十进制数字的最大值 |
FLT_MIN_EXP | -125 | float类型在FLT_RADIX基数中的指数的最小负整数值 |
FLT_MIN_10_EXP | -37 | float类型在10基数中的指数的最小负整数值 |
FLT_MAX_EXP | 128 | float类型在FLT_RADIX基数中的指数的最大整数值 |
FLT_MAX_10_EXP | 38 | float类型在10基数中的指数的最大整数值 |
FLT_MAX | 3.4e+38 | float类型最大的浮点值 |
FLT_EPSILON | 1.19e-7 | float类型可表示的最小有效数字 |
FLT_MIN | -3.4e+38 | float类型最小的浮点值 |
DBL_MANT_DIG | 53 | double类型在FLT_RADIX基数中的位数 |
DBL_DIG | 15 | double类型舍入后不会改变表示的十进制数字的最大值 |
DBL_MIN_EXP | -1021 | double类型在FLT_RADIX基数中的指数的最小负整数值 |
DBL_MIN_10_EXP | -307 | double类型在10基数中的指数的最小负整数值 |
DBL_MAX_EXP | 1024 | double类型在FLT_RADIX基数中的指数的最大整数值 |
DBL_MAX_10_EXP | 308 | double类型在10基数中的指数的最大整数值 |
DBL_MAX | 1.8e+308 | double类型最大的浮点值 |
DBL_EPSILON | 2.22e-16 | double类型可表示的最小有效数字 |
DBL_MIN | -1.8e+308 | double类型最小的浮点值 |
LDBL_MANT_DIG | 64 | long double类型在FLT_RADIX基数中的位数 |
LDBL_DIG | 18 | long double类型舍入后不会改变表示的十进制数字的最大值 |
LDBL_MIN_EXP | -16381 | long double类型在FLT_RADIX基数中的指数的最小负整数值 |
LDBL_MIN_10_EXP | -4931 | long double类型在10基数中的指数的最小负整数值 |
LDBL_MAX_EXP | 16384 | long double类型在FLT_RADIX基数中的指数的最大整数值 |
LDBL_MAX_10_EXP | 4932 | long double类型在10基数中的指数的最大整数值 |
LDBL_MAX | 1.2e+4932 | long double类型最大的浮点值 |
LDBL_EPSILON | 1.08e-19 | long double类型可表示的最小有效数字 |
LDBL_MIN | -1.2e+4932 | long double类型最小的浮点值 |
五、limits.h
limits.h 决定了各种变量类型的各种属性,定义在该头文件中的宏限制了各种变量类型(比如 char、int 和 long)的值,这些限制指定了变量不能存储任何超出这些限制的值。
limits.h库宏定义:
宏 | 数值 | 说明 |
CHAR_BIT | 8 | char类型字节的比特数 |
CHAR_MIN | 0 | char类型的最小值,如果char表示负值,则它的值等于SCHAR_MIN,否则等于0 |
CHAR_MAX | 127 | char类型的最大值,如果char表示负值,则它的值等于 SCHAR_MAX,否则等于UCHAR_MAX |
SCHAR_MIN | -128 | signed char类型的最小值 |
SCHAR_MAX | 127 | signed char类型的最大值 |
UCHAR_MAX | 255 | unsigned char类型的最大值 |
MB_LEN_MAX | 1 | char类型中多字节的最大字节数 |
SHRT_BIT | 16 | short类型字节的比特数 |
SHRT_MIN | -32768 | short类型的最小值 |
SHRT_MAX | 32767 | short类型的最大值 |
USHRT_MAX | 65535 | unsigned short类型的最大值 |
INT_BIT | 32 | int类型字节的比特数 |
INT_MIN | -2147483648 | int类型的最小值 |
INT_MAX | 2147483647 | int类型的最大值 |
UINT_MAX | 4294967296 | unsigned int类型的最大值 |
LONG_BIT | 64 | long类型字节的比特数 |
LONG_MIN | -2147483648 | long类型的最小值 |
LONG_MAX | 2147483647 | long类型的最大值 |
ULONG_MAX | 4294967296 | unsigned long类型的最大值 |
LONG_BIT | 128 | long类型字节的比特数 |
LLONG_MIN | -9223372036854775808 | long long类型的最小值 |
LLONG_MAX | 9223372036854775807 | long long类型的最大值 |
ULLONG_MAX | 18446744073709551615 | unsigned long long类型的最大值 |
六、locale.h
locale.h定义了一些函数和宏,用于设置和处理程序的地域设置(locale)。地域设置可以影响程序中一些函数的行为,例如日期、时间、货币格式等。通过使用setlocale函数,可以设置程序的地域设置,而localeconv函数可以获取当前地域设置的相关信息。
locale.h的库宏:
序号 | 宏 | 说明 |
1 | LC_ALL | 设置下面的所有选项 |
2 | LC_COLLATE | 影响 strcoll 和 strxfrm 函数 |
3 | LC_CTYPE | 影响所有字符函数 |
4 | LC_MONETARY | 影响 localeconv 函数提供的货币信息 |
5 | LC_NUMERIC | 影响 localeconv 函数提供的小数点格式化和信息 |
6 | LC_TIME | 影响 strftime 函数 |
locale.h的库函数:
序号 | 库函数 | 说明 |
1 | char *setlocale(int category, const char *locale) | 设置或读取地域化信息 |
2 | struct lconv *localeconv(void) | 设置或读取地域化信息 |
locale.h的库结构体:
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
序号 | 变量 | 说明 |
1 | decimal_point | 用于非货币值的小数点字符 |
2 | thousands_sep | 用于非货币值的千位分隔符 |
3 | grouping | 一个表示非货币量中每组数字大小的字符串。每个字符代表一个整数值,每个整数指定当前组的位数。值为 0 意味着前一个值将应用于剩余的分组 |
4 | int_curr_symbol | 国际货币符号使用的字符串。前三个字符是由 ISO 4217:1987 指定的,第四个字符用于分隔货币符号和货币量 |
5 | currency_symbol | 用于货币的本地符号 |
6 | mon_decimal_point | 用于货币值的小数点字符 |
7 | mon_thousands_sep | 用于货币值的千位分隔符 |
8 | mon_grouping | 一个表示货币值中每组数字大小的字符串。每个字符代表一个整数值,每个整数指定当前组的位数。值为 0 意味着前一个值将应用于剩余的分组 |
9 | positive_sign | 用于正货币值的字符 |
10 | negative_sign | 用于负货币值的字符 |
11 | int_frac_digits | 国际货币值中小数点后要显示的位数 |
12 | frac_digits | 货币值中小数点后要显示的位数 |
13 | p_cs_precedes | 如果等于 1,则 currency_symbol 出现在正货币值之前。如果等于 0,则 currency_symbol 出现在正货币值之后 |
14 | p_sep_by_space | 如果等于 1,则 currency_symbol 和正货币值之间使用空格分隔。如果等于 0,则 currency_symbol 和正货币值之间不使用空格分隔 |
15 | n_cs_precedes | 如果等于 1,则 currency_symbol 出现在负货币值之前。如果等于 0,则 currency_symbol 出现在负货币值之后 |
16 | n_sep_by_space | 如果等于 1,则 currency_symbol 和负货币值之间使用空格分隔。如果等于 0,则 currency_symbol 和负货币值之间不使用空格分隔n_sep_by_space |
17 | p_sign_posn | 表示正货币值中正号的位置 |
18 | n_sign_posn | 表示负货币值中负号的位置 |
库调用事项:
LC_ALL 包括下面的所有选项。
LC_COLLATE 字符串比较。参见 strcoll()。
LC_CTYPE 字符分类和转换。例如 strtoupper()。
LC_MONETARY 货币格式,针对 localeconv()。
LC_NUMERIC 小数点分隔符,针对 localeconv()。
LC_TIME 日期和时间格式,针对 strftime()。
LC_MESSAGES 系统响应。
char *setlocale(int category, const char *locale):
如果成功调用 setlocale(),则返回一个对应于区域设置的不透明的字符串。如果请求无效,则返回值是 NULL。
例如:
#include <stdio.h>
#include <time.h>
#include <locale.h>
int main ()
{
time_t currtime;
struct tm *timer;
char buffer[80];
time( &currtime );
timer = localtime( &currtime );
printf("Locale is: %s\n", setlocale(LC_ALL, "en_GB"));
strftime(buffer,80,"%c", timer );
printf("Date is: %s\n", buffer);
printf("Locale is: %s\n", setlocale(LC_ALL, "de_DE"));
strftime(buffer,80,"%c", timer );
printf("Date is: %s\n", buffer);
return 0;
}
编译并运行输出:
Locale is: en_GB
Date is: Thu 23 Aug 2012 06:39:32 MST
Locale is: de_DE
Date is: Do 23 Aug 2012 06:39:32 MST
struct lconv *localeconv(void):
会返回一个 lconv 结构类型的对象。
例如:
#include <stdio.h>
#include <locale.h>
int main ()
{
struct lconv * lc;
setlocale(LC_MONETARY, "it_IT");
lc = localeconv();
printf("Local Currency Symbol: %s\n",lc->currency_symbol);
printf("International Currency Symbol: %s\n",lc->int_curr_symbol);
setlocale(LC_MONETARY, "en_US");
lc = localeconv();
printf("Local Currency Symbol: %s\n",lc->currency_symbol);
printf("International Currency Symbol: %s\n",lc->int_curr_symbol);
setlocale(LC_MONETARY, "en_GB");
lc = localeconv();
printf ("Local Currency Symbol: %s\n",lc->currency_symbol);
printf ("International Currency Symbol: %s\n",lc->int_curr_symbol);
printf("Decimal Point = %s\n", lc->decimal_point);
return 0;
}
编译并运行输出:
Local Currency Symbol: EUR
International Currency Symbol: EUR
Local Currency Symbol: $
International Currency Symbol: USD
Local Currency Symbol: £
International Currency Symbol: GBP
Decimal Point = .
七、math.h
math.h是C语言中的一个数学函数库,它提供了许多常用的数学函数,如三角函数、指数函数、对数函数、幂函数、取整函数等等。使用math.h库可以方便地进行各种数学计算。
math.h中的库函数:
序号 | 库函数 | 说明 |
1 | sin(x) | 计算正弦函数的值,x为弧度制的角度 |
2 | cos(x) | 计算余弦函数的值,x为弧度制的角度 |
3 | tan(x) | 计算正切函数的值,x为弧度制的角度 |
4 | asin(x) | 反正弦函数 |
5 | acos(x) | 反余弦函数 |
6 | atan(x) | 反正切函数 |
7 | atan2(y,x) | 带两个参数的反正切函数 |
8 | sinh(x) | x的双曲正弦值 |
9 | cosh(x) | x的双曲余弦值 |
10 | tanh(x) | x的双曲正切值 |
11 | exp(x) | 指数函数,以 e 为底数 |
12 | log(x) | x的自然对数 (Natural logarithm of x) |
14 | log10(x) | 常用对数,以10为底 ( Common logarithm of x ) |
15 | pow(x,y) | 幂函数 The result of raising base to the power exponent |
16 | sqrt(x) | x的平方根,其中x>=0 |
17 | ceil(x) | 不小于x的最小整型数,其中x的类型为double |
18 | floor(x) | 不大于x的最小整型数,其中x的类型为double |
19 | fabs(x) | x的绝对值|x| |
20 | ldexp(x,n) | 计算x乘以2的n次幂的值 |
21 | frexp(x,int *exp) | 把 x 分成一个在[1/2, 1]区间内的真分数和一个 2 的幂数。结果将返回真分数部分,并将幂数保存在*exp 中。如果 x 为 0,则这两部分均为 0 |
22 | modf(x,double *ip) | 把 x 分成整数和小数两部分,两部分的正负号均与 x 相同。该函数返回小数部分,整数部分保存在*ip 中 |
23 | fmod(x,y) | 求 x除y 的浮点余数,符号与 x 相同。如果 y 为 0,则结果与具体的实现相关 |
例如:
#include <math.h>
#include <stdio.h>
int main(){
double pr = 3.141592654;
printf("%f\n",sin(pr/2.0));
printf("%f\n",cos(pr));
printf("%f\n",tan(pr/4.0));
printf("%f\n",asin(0.5));
printf("%f\n",acos(0));
printf("%f\n",atan(1));
printf("%f\n",atan2(1,2));
printf("%f\n",sinh(1.14));
printf("%f\n",cosh(5.14));
printf("%f\n",tanh(1));
printf("%f\n",exp(1));
printf("%f\n",log(2));
printf("%f\n",log10(100));
printf("%f\n",pow(2,3));
printf("%f\n",sqrt(1024));
printf("%f\n",ceil(114.514));
printf("%f\n",floor(1919.810));
printf("%f\n",fabs(-114));
printf("%f\n",ldexp(1.1,2));
}
编译运行后输出:
1.000000
-1.000000
1.000000
0.523599
1.570796
0.785398
0.463648
1.403475
85.360813
0.761594
2.718282
0.693147
2.000000
8.000000
32.000000
115.000000
1919.000000
114.000000
4.400000
函数frexp(x,int *exp):
例如:
#include <math.h>
#include <stdio.h>
int main ()
{
double s = 1024, fraction;
int a;
fraction = frexp(s, &a);
printf("s = %.2lf = %.2lf * 2^%d\n", s, fraction, a);
return(0);
}
编译运行后输出:
s = 1024.00 = 0.50 * 2^11
函数fmod(x,y):
例如:
#include <math.h>
#include <stdio.h>
int main ()
{
float aa, bb;
int cc;
aa = 19.19;
bb = 5.14;
cc = 1.14;
printf("%f / %d 的余数是 %lf\n", aa, cc, fmod(aa,cc));
printf("%f / %f 的余数是 %lf\n", aa, bb, fmod(aa,bb));
return 0;
}
编译运行后输出:
19.190001 / 1 的余数是 0.190001
19.190001 / 5.140000 的余数是 3.770001
函数modf(x,double *ip):
例如:
#include<math.h>
#include<stdio.h>
int main ()
{
double pr, fractpart, intpart;
pr = 114514.1919810;
fractpart = modf(pr, &intpart);
printf("整数 = %lf\n", intpart);
printf("小数 = %lf \n", fractpart);
return 0;
}
编译运行后输出:
整数 = 114514.000000
小数 = 0.191981
八、setjmp.h
setjmp.h提供了一种不同于通常的函数调用和返回顺序的方式,它允许立即从一个深层嵌套的函数调用中返回。
setjmp.h中有一个变量jmp_buf,一个宏setjmp(),一个函数longjmp()。
setjmp()宏的表达式为:int setjmp(jum_buf env)
setjmp宏将状态信息保存到env中,供longjmp使用。如果直接调用setjmp,则返回值为0;如果是在 longjmp 中调用 setjmp,则返回值为非 0。setjmp 只能用于某些上下文中,如用于 if 语句、switch 语句、循环语句的条件测试中以及一些简单的关系表达式中。
例如:
if (setjmp(env) == 0)
printf("correct\n");
else
printf("error\n");
函数longjmp()的表达式为:void longjmp(jmp_buf env, int val)
通过最近一次调用 setjmp 时保存到 env 中的信息恢复状态,同时,程序重新恢复执行,其状态等同于 setjmp 宏调用刚刚执行完并返回非 0 值。包含 setjmp 宏调用的函数的执行必须还没有终止。除下列情况外,可访问对象的值同调用 longjmp 时的值相同:在调用 setjmp 宏后,如果调用 setjmp 宏的函数中的非 volatile 自动变量改变了,则它们将变成未定义状态。
例如:
#include <stdio.h>
#include <setjmp.h>
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("world\n");
}
return 0;
}
编译运行后输出:
second
world
九、signal.h
signal.h提供了一些处理程序运行期间引发的各种异常条件的功能,比如来源于外部的中断信号或程序执行错误引起的中断信号。
signal.h中的库函数:
序号 | 库函数 | 说明 |
1 | void (*signal(int sig, void (*handler)(int)))(int) | 决定了如何处理后续的信号 |
2 | int raise(int sig) | raise向程序发送信号sig。如果发送不成功,则返回一个非0值 |
函数void (*signal(int sig, void (*handler)(int)))(int):
如果 handler 的值是 SIG_DFL,则采用由实现定义的默认行为;如果 handler 的值是 SIG_IGN,则忽略该信号;否则,调用 handler 指向的函数(有效信号作为参数)。
有效信号有:
序号 | 信号 | 说明 |
1 | SIGABRT | 异常终止,例如由abort引起的终止 |
2 | SIGFPE | 算术运算出错,如除数为0或溢出 |
3 | SIGILL | 非法函数映像,如非法指令 |
4 | SIGINT | 用于交互式目的的信号,如中断 |
5 | SIGSEGV | 非法存储器访问,如访问不存在的内存单元 |
6 | SIGTERM | 发送给程序的终止请求 |
对于特定的信号,signal 将返回 handler 的前一个值;如果出现错误,则返回值 SIG_ERR。
例如:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void sighandler(int);
int main()
{
signal(SIGINT, sighandler);
while(1)
{
printf("开始运行...\n");
sleep(2);
}
return 0;
}
void sighandler(int signum)
{
printf("捕获信号 %d,跳出...\n", signum);
exit(1);
}
编译运行后输出:
开始运行...
开始运行...
开始运行...
开始运行...
开始运行...
开始运行...
函数int raise(int sig):
raise向程序发送信号sig。如果发送不成功,则返回一个非0值。
例如:
#include <stdio.h>
#include <signal.h>
void signal_catch(int);
int main()
{
int ret;
ret = signal(SIGINT, signal_catch);
if( ret == SIG_ERR)
{
printf("不能设置信号处理程序\n");
exit(0);
}
printf("生成一个信号\n");
ret = raise(SIGINT);
if( ret !=0 )
{
printf("不能生成 SIGINT 信号\n");
exit(0);
}
printf("退出...\n");
return 0;
}
void signal_catch(int signal)
{
printf("!! 信号捕获成功 !!\n");
}
编译运行后输出:
生成一个信号
!! 信号捕获成功 !!
退出...
十、stdarg.h
stdarg.h是由standard(标准) arguments(参数)简化而来,主要目的为让函数能够接收可变参数。
定义宏以访问未命名参数列表的各个参数,调用的函数不知道这些参数的数量和类型。函数可以接受不同数量的附加参数,而不需要相应的参数声明,方法是在其常规命名参数之后包括一个逗号和三个点(, …);
定义了一个变量类型 va_list 和三个宏,这三个宏可用于在参数个数未知(即参数个数可变)时获取函数中的参数.可变参数的函数通在参数列表的末尾是使用省略号(,…)定义的。
返回type函数_name(参数声明,…);
要访问这些附加参数,可以使用在此标题中声明的宏va_start、va_arg和va_end:
stdarg.h中的宏定义:
序号 | 库宏 | 说明 |
1 | void va_start(va_list ap, last_arg) | ap – 这是一个 va_list 类型的对象,它用来存储通过 va_arg 获取额外参数时所必需的信息。 |
2 | type va_arg(va_list ap, type) | 获取函数参数列表中类型为 type 的下一个参数 |
3 | void va_end(va_list ap) | 允许使用了 va_start 宏的带有可变参数的函数返回。如果在从函数返回之前没有调用 va_end,则结果为未定义。 |
首先,va_start将变量参数列表初始化为va_list。
随后执行的va_arg将按照传递给函数的相同顺序产生附加参数的值。
最后,应在函数返回之前执行va_end。
例如:
#include <stdarg.h>
#include <stdio.h>
void Print(int n, ...)
{
int i ,value;
printf ("printing:");
va_list pr;
va_start(pr,n);
for (i=0;i<n;i++)
{
value=va_arg(pr,int);
printf ("%d\t",value);
}
va_end(pr);
printf ("\n");
}
int main ()
{
print(3,1,3,5);
return 0;
}
编译运行后输出:
printing:1 3 5
十一、stddef.h
stddef.h定义了一些常用的类型和宏,用于处理指针和数据类型的偏移量。在这个头文件中定义了三个数据类型:size_t、wchar_t和ptrdiff_t。
stddef.h的库变量:
序号 | 变量 | 说明 |
1 | size_t | size_t是sizeof的结果,一般是long unsigned int |
2 | wchar_t | wchar_t用于描述宽字符,一般是int |
3 | ptrdiff_t | ptrdiff_t是指针相减的数据类型 |
ptrdiff_t指针相减例如:
#include <stdio.h>
#include <stddef.h>
int main(){
char x[10];
ptrdiff_t nx = &x[6]-&x[0];
printf("%d\n", nx);
long int y[20];
ptrdiff_t ny = &y[6]-&y[1];
printf("%d\n", ny);
return 0;
}
十二、stdio.h
stdio.h定义了三个变量类型、一些宏和各种函数来执行输入和输出。
stdio.h中的变量:
序号 | 变量 | 说明 |
1 | size_t | 无符号整数类型,它是 sizeof 关键字的结果 |
2 | FILE | 这是一个适合存储文件流信息的对象类型 |
3 | fpos_t | 这是一个适合存储文件中任何位置的对象类型 |
stdio.h中的宏:
序号 | 宏 | 说明 |
1 | NULL | 一个空指针常量的值 |
2 | _IOFBF、_IOLBF 和 _IONBF | 扩展了带有特定值的整型常量表达式,并适用于 setvbuf 函数的第三个参数 |
3 | BUFSIZ | 一个整数,该整数代表了 setbuf 函数使用的缓冲区大小 |
4 | EOF | 一个表示已经到达文件结束的负整数 |
5 | FOPEN_MAX | 一个整数,该整数代表了系统可以同时打开的文件数量 |
6 | FILENAME_MAX | 一个整数,该整数代表了字符数组可以存储的文件名的最大长度。如果实现没有任何限制,则该值应为推荐的最大值 |
7 | L_tmpnam | 一个整数,该整数代表了字符数组可以存储的由 tmpnam 函数创建的临时文件名的最大长度 |
8 | SEEK_CUR、SEEK_END 和 SEEK_SET | 在 fseek 函数中使用,用于在一个文件中定位不同的位置 |
9 | TMP_MAX | tmpnam 函数可生成的独特文件名的最大数量 |
10 | stderr、stdin 和 stdout | 指向 FILE 类型的指针,分别对应于标准错误、标准输入和标准输出流 |
stdio.h中的库函数:
序号 | 函数&描述 |
1 | int fclose(FILE *stream) 关闭流 stream。刷新所有的缓冲区 |
2 | void clearerr(FILE *stream) 清除给定流 stream 的文件结束和错误标识符 |
3 | int feof(FILE *stream) 测试给定流 stream 的文件结束标识符 |
4 | int ferror(FILE *stream) 测试给定流 stream 的错误标识符 |
5 | int fflush(FILE *stream) 刷新流 stream 的输出缓冲区 |
6 | int fgetpos(FILE *stream, fpos_t *pos) 获取流 stream 的当前文件位置,并把它写入到 pos |
7 | FILE *fopen(const char *filename, const char *mode) 使用给定的模式 mode 打开 filename 所指向的文件 |
8 | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 从给定流 stream 读取数据到 ptr 所指向的数组中 |
9 | int fseek(FILE *stream, long int offset, int whence) 设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数 |
10 | FILE *freopen(const char *filename, const char *mode, FILE *stream) 把一个新的文件名 filename 与给定的打开的流 stream 关联,同时关闭流中的旧文件 |
11 | int fsetpos(FILE *stream, const fpos_t *pos) 设置给定流 stream 的文件位置为给定的位置。参数 pos 是由函数 fgetpos 给定的位置 |
12 | long int ftell(FILE *stream) 返回给定流 stream 的当前文件位置 |
13 | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 把 ptr 所指向的数组中的数据写入到给定流 stream 中 |
14 | int remove(const char *filename) 删除给定的文件名 filename,以便它不再被访问 |
15 | int rename(const char *old_filename, const char *new_filename) 把 old_filename 所指向的文件名改为 new_filename |
16 | void rewind(FILE *stream) 设置文件位置为给定流 stream 的文件的开头 |
17 | void setbuf(FILE *stream, char *buffer) 定义流 stream 应如何缓冲 |
18 | int setvbuf(FILE *stream, char *buffer, int mode, size_t size) 另一个定义流 stream 应如何缓冲的函数 |
19 | FILE *tmpfile(void) 以二进制更新模式(wb+)创建临时文件 |
20 | char *tmpnam(char *str) 生成并返回一个有效的临时文件名,该文件名之前是不存在的 |
21 | int fprintf(FILE *stream, const char *format, ...) 发送格式化输出到流 stream 中 |
22 | int printf(const char *format, ...) 发送格式化输出到标准输出 stdout |
23 | int sprintf(char *str, const char *format, ...) 发送格式化输出到字符串 |
24 | int vfprintf(FILE *stream, const char *format, va_list arg) 使用参数列表发送格式化输出到流 stream 中 |
25 | int vprintf(const char *format, va_list arg) 使用参数列表发送格式化输出到标准输出 stdout |
26 | int vsprintf(char *str, const char *format, va_list arg) 使用参数列表发送格式化输出到字符串 |
27 | int fscanf(FILE *stream, const char *format, ...) 从流 stream 读取格式化输入 |
28 | int scanf(const char *format, ...) 从标准输入 stdin 读取格式化输入 |
29 | int sscanf(const char *str, const char *format, ...) 从字符串读取格式化输入 |
30 | int fgetc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动 |
31 | char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定 |
32 | int fputc(int char, FILE *stream) 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动 |
33 | int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符 |
34 | int getc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动 |
35 | int getchar(void) 从标准输入 stdin 获取一个字符(一个无符号字符) |
36 | char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定 |
37 | int putc(int char, FILE *stream) 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动 |
38 | int putchar(int char) 把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中 |
39 | int puts(const char *str) 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中 |
40 | int ungetc(int char, FILE *stream) 把字符 char(一个无符号字符)推入到指定的流 stream 中,以便它是下一个被读取到的字符 |
41 | void perror(const char *str) 把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 str,后跟一个冒号,然后是一个空格 |
42 | int snprintf(char *str, size_t size, const char *format, ...) 格式字符串到 str 中 |
stdio.h中定义了多个函数和输入输出流(FILE)类型,其中一些最常用的函数如下:
printf:格式化输出数据到标准输出设备(通常是显示器)。
scanf:从标准输入设备(通常是键盘)读取格式化输入数据。
fgets:从指定文件中读取一行字符串。
fputs:向指定文件中输出一行字符串。
fopen/fclose:打开和关闭文件。
fread/fwrite:从指定文件中读取/写入数据。
这些函数在C语言中广泛使用,可以方便地进行文件和字符串的输入输出操作,也是很多库函数和系统函数的基础。
十三、stdlib.h
stdlib.h定义了一些常用的类型和函数。其中包括size_t、wchar_t、div_t、ldiv_t和lldiv_t等五种类型。stdlib.h主要封装了一些内存分配、类型转换、随机数生成、系统调用等函数,如malloc()、calloc()、realloc()、free()、system()、atoi()、atol()、rand()、srand()、exit()等。
stdlib.h中的变量:
序号 | 变量 | 说明 |
1 | size_t | 无符号整数类型,它是 sizeof 关键字的结果 |
2 | wchar_t | 一个宽字符常量大小的整数类型 |
3 | div_t | div 函数返回的结构 |
4 | ldiv_t | ldiv 函数返回的结构 |
5 | lldiv_t | lldiv_t函数返回的结构 |
stdlib.h中的宏:
序号 | 宏 | 说明 |
1 | NULL | 一个空指针常量的值 |
2 | EXIT_FAILURE | exit 函数失败时要返回的值 |
3 | EXIT_SUCCESS | exit 函数成功时要返回的值 |
4 | RAND_MAX | rand 函数返回的最大值 |
5 | MB_CUR_MAX | 表示在多字节字符集中的最大字符数,不能大于 MB_LEN_MAX |
stdlib.h中的库函数:
序号 | 函数&描述 |
1 | void * calloc(unsigned n,unsigned size) 分配n个数据项的内存连续空间,每个数据项的大小为size |
2 | void free(void* p) 释放p所指的内存区 |
3 | void * malloc(unsigned size) 分配size字节的存储区 |
4 | void * realloc(void * p,unsigned size) 将p所指出的已分配内存区的大小改为size,size可以比原来分配的空间大或小 |
5 | int rand(void) 产生0到32767间的随机整数(0到0x7fff之间) |
6 | void abort(void) 异常终止一个进程 |
7 | void exit(int state) 程序中止执行,返回调用过程 |
8 | char* getenv(const char *name) 返回一个指向环境变量的指针 |
9 | int putenv(const char *name) 将字符串name增加到DOS环境变量中 |
10 | long labs(long num) 求长整型参数的绝对值 |
11 | double atof(char *str) 将字符串转换成一个双精度数值 |
12 | int atoi(char *str) 将字符串转换成一个整数值 |
13 | long atol(char *str) 将字符串转换成一个长整数 |
14 | char *ecvt(double value,int ndigit,int *dec,int *sign) 将浮点数转换为字符串 |
15 | char *fcvt(double value,int ndigit,int *dec,int *sign) 将浮点数变成一个字符串 |
下面是stdlib.h中一些常用函数的例子:
1.malloc和free用于动态内存分配和释放。malloc函数用于在堆(heap)中分配指定大小的内存,返回一个指向该内存的指针。free函数用于释放之前分配的内存,将内存归还给系统。
例如:
#include <stdlib.h>
#include <stdio.h>
int main() {
int *p = (int*)malloc(sizeof(int));
if (p == NULL) {
printf("Failed to allocate memory!\n");
return 1;
}
*p = 20;
printf("%d\n", *p);
free(p);
return 0;
}
编译后并运行输出:
20
2.atoi和atof用于将字符串转换为整数或浮点数;atoi函数用于将字符串转换为整数,atof函数用于将字符串转换为浮点数。
例如:
#include <stdlib.h>
#include <stdio.h>
int main() {
char ptr1[] = "268";
char ptr2[] = "3.14";
int num1 = atoi(ptr1);
printf("%d\n", num1);
double num2 = atof(ptr2);
printf("%f\n", num2);
return 0;
}
编译后并运行输出:
268
3.140000
3.rand和srand用于生成随机数和设置随机数种子;rand函数用于生成一个伪随机数,srand函数用于设置随机数种子。
例如:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int main() {
srand(time(NULL));
for (int i = 0; i < 10; i++) {
printf("%d ", rand() % 100);
}
printf("\n");
return 0;
}
编译后并运行输出:
46 78 62 92 83 24 60 32 36 72
4.system用于执行系统命令;system函数用于执行指定的系统命令,例如清屏、打开文件等。
例如:
#include <stdlib.h>
#include <stdio.h>
int main() {
system("clear");
return 0;
}
5.exit用于退出程序;exit函数用于正常退出程序,并返回一个指定的退出码(通常用0表示正常退出,非0表示异常退出),函数下面的语句不再执行。
例如:
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("Hello, world!\n");
exit(0);
return 0;
}
6.qsort用于快速排序;qsort函数用于对数组进行快速排序,需要指定数组的起始地址、元素个数、每个元素的大小和一个比较函数。
例如:
#include <stdio.h>
#include <stdlib.h>
int compare(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
int main() {
int arr[] = {3, 2, 4, 1, 4, 8, 2, 6, 5, 3, 6};
int len = sizeof(arr) / sizeof(int);
qsort(arr, len, sizeof(int), compare);
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
编译后并运行输出:
1 2 2 3 3 4 4 5 6 6 8
7.abs函数用于取整数的绝对值。
例如:
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = -26;
int b = 32;
printf("%d\n", abs(a));
printf("%d\n", abs(b));
return 0;
}
编译后并运行输出:
26
32
8.labs函数用于取长整数的绝对值。
例如:
#include <stdlib.h>
#include <stdio.h>
int main() {
long a = -132463268;
long b = 628371535;
printf("%ld\n", labs(a));
printf("%ld\n", labs(b));
return 0;
}
编译后并运行输出:
132463268
628371535
十四、string.h
string.h提供了一系列字符串操作的函数。
string.h中的库函数:
void *memchr(const void *_s, int _c, size_t _n);
int memcmp(const void *_s1, const void *_s2, size_t _n);
void *memcpy(void *_s1, const void *_s2, size_t _n);
void *memmove(void *_s1, const void *_s2, size_t _n);
void *memset(void *_s, int _c, size_t _n);
char *strcat(char *_s1, const char *_s2);
char *strchr(const char *_s, int _c);
int strncmp(const char *_s1, const char *_s2, size_t _n);
int strcmp(const char *_s1, const char *_s2);
int strcoll(const char *_s1, const char *_s2);
char *strcpy(char *_s1, const char *_s2);
size_t strcspn(const char *_s1, const char *_s2);
char *strerror(int _errnum);
size_t strlen(const char *_s);
char *strncat(char *_s1, const char *_s2, size_t _n);
char *strncpy(char *_s1, const char *_s2, size_t _n);
char *strpbrk(const char *_s1, const char *_s2);
char *strrchr(const char *_s, int _c);
size_t strspn(const char *_s1, const char *_s2);
char *strstr(const char *_s1, const char *_s2);
char *strtok(char *_s1, const char *_s2);
size_t strxfrm(char *_s1, const char *_s2, size_t _n);
1.void *memcpy(void *dest, const void *src, size_t n)
从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中;void *作为形参表示可以传入任意类型的指针;void *作为返回值,则表示可以转成任意类型的指针。
例如:
#include <stdio.h>
#include <string.h>
int main ()
{
const char ptr[] = "happy everyday";
const char ch = 'p';
char *val;
val = (char *)memchr(ptr, ch, strlen(ptr));
printf("result is %s\n", val);
printf("result is %c\n", *val);
return 0;
}
编译后并运行输出:
result is ppy everyday
result is p
2.void memmove( void dest, const void* src, size_t count )
由src所指内存区域复制count个字节到dest所指内存区域。
void * memmove(void *s1, const void *s2, register size_t n)
{
register char *p1 = s1;
register const char *p2 = s2;
if (n>0) {
if (p2 <= p1 && p2 + n > p1) {
/* overlap, copy backwards */
p1 += n;
p2 += n;
n++;
while (--n > 0) {
*--p1 = *--p2;
}
} else {
n++;
while (--n > 0) {
*p1++ = *p2++;
}
}
}
return s1;
}
3.void *memchr(const void *buf, int ch, size_t count)
从buf所指内存区域的前count个字节查找字符ch;当第一次遇到字符ch时停止查找。如果成功,返回指向字符ch的指针;否则返回NULL。
void *memchr(const void *s, register int c, register size_t n)
{
register const unsigned char *s1 = s;
c = (unsigned char) c;
if (n) {
n++;
while (--n > 0) {
if (*s1++ != c) continue;
return (void *) --s1;
}
}
return NULL;
}
4.int memcmp(const void *buf1, const void *buf2, unsigned int count);
比较内存区域buf1和buf2的前count个字节;当buf1<buf2时,返回值<0,
当buf1=buf2时,返回值=0,当buf1>buf2时,返回值>0。
int memcmp(const void *s1, const void *s2, size_t n)
{
register const unsigned char *p1 = s1, *p2 = s2;
if (n) {
n++;
while (--n > 0) {
if (*p1++ == *p2++) continue;
return *--p1 - *--p2;
}
}
return 0;
}
5.void *memset(void *s, int ch, size_t n);
将s中前n个字节用 ch 替换并返回 s;在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。
void *memset(void *s, register int c, register size_t n)
{
register char *s1 = s;
if (n>0) {
n++;
while (--n > 0) {
*s1++ = c;
}
}
return s;
}
6.char *strcat(char *dest,char *src);
把src所指字符串添加到dest结尾处(覆盖dest结尾处的’\0’)并添加’\0’。
char *strcat(char *ret, register const char *s2)
{
register char *s1 = ret;
while (*s1++ != '\0')
s1--;
while (*s1++ = *s2++);
return ret;
}
7.char *strncat(char * s1,const char * s2.size_t n);
把src所指字符串的前n个字符添加接功能到dest结尾处(覆盖dest结尾处接功能的’\0’)并添加’\0’。
char *strncat(char *ret, register const char *s2, size_t n)
{
register char *s1 = ret;
if (n > 0) {
while (*s1++)
s1--;
while (*s1++ = *s2++) {
if (--n > 0) continue;
*s1 = '\0';
break;
}
return ret;
}
else return s1;
}
8.char *strchr(const char *s,char c);
查找字符串s中首次出现字符c的位置。
char *strchr(register const char *s, register int c)
{
c = (char) c;
while (c != *s) {
if (*s++ == '\0') return NULL;
}
return (char *)s;
}
9.char *strrchr(const char *s, int c)
查找一个字符c在另一个字符串str中末次出现的位置(也就是从str的右侧开始查找字符c首次出现的位置),并返回从字符串中的这个位置起,一直到字符串结束的所有字符。如果未能找到指定字符,那么函数将返回NULL。
char *strrchr(register const char *s, int c)
{
register const char *result = NULL;
c = (char) c;
do {
if (c == *s)
result = s;
} while (*s++ != '\0');
return (char *)result;
}
10.int strcmp(const char *s1,const char * s2)
比较两个字符串。设这两个字符串为str1,str2,若str1=str2,则返回零;若str1>str2,则返回正数;若str1<str2,则返回负数。
int strcmp(register const char *s1, register const char *s2)
{
while (*s1 == *s2++) {
if (*s1++ == '\0') {
return 0;
}
}
if (*s1 == '\0') return -1;
if (*--s2 == '\0') return 1;
return (unsigned char) *s1 - (unsigned char) *s2;
}
11.int strncmp(char *str1, char *str2, int maxlen)
这个函数用来比较s1和s2字符串,这个函数将返回一个值, 它的符号与第一对不同的字符的比较结果相关。如果两个字符串相等的话,strncmp将返回0。
如果s1是s2的一个子串的话,s1小于s2。
int strncmp(register const char *s1, register const char *s2, register size_t n)
{
if (n) {
do {
if (*s1 != *s2++)
break;
if (*s1++ == '\0')
return 0;
} while (--n > 0);
if (n > 0) {
if (*s1 == '\0') return -1;
if (*--s2 == '\0') return 1;
return (unsigned char) *s1 - (unsigned char) *s2;
}
}
return 0;
}
12.int strcoll(const char *s1,const char * s2)
根据LC_COLLATE比较字符串s1和s2。
int strcoll(register const char *s1, register const char *s2)
{
while (*s1 == *s2++) {
if (*s1++ == '\0') {
return 0;
}
}
return *s1 - *--s2;
}
13.char strcpy(char dest, const char *src)
把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
char *strcpy(char *ret, register const char *s2)
{
register char *s1 = ret;
while (*s1++ = *s2++)
return ret;
}
14.char *strncpy(char *_s1, const char *_s2, size_t _n)
复制字符串src中的内容(字符,数字、汉字…)到字符串dest中,复制多少由size_t的值决定,返回指向dest的指针。如果遇到空字符(’\0’),则空字符后面全部为空(字符)。
char *strncpy(char *ret, register const char *s2, register size_t n)
{
register char *s1 = ret;
if (n>0) {
while((*s1++ = *s2++) && --n > 0)
if ((*--s2 == '\0') && --n > 0) {
do {
*s1++ = '\0';
} while(--n > 0);
}
}
return ret;
}
15.char *strerror(int errnum)
通过标准错误的标号,获得错误的描述字符串。
char *strerror(register int errnum)
{
extern const char *_sys_errlist[];
extern const int _sys_nerr;
if (errnum < 0 || errnum >= _sys_nerr)
return "unknown error";
return (char *)_sys_errlist[errnum];
}
16.size_t strlen(const char *org)
计算字符串s的(unsigned int型)长度,不包括’\0’在内,返回s的长度,不包括结束符NULL。
size_t strlen(const char *org)
{
register const char *s = org;
while (*s++);
return --s - org;
}
17.size_t strspn (const char *s,const char * accept);
strspn()从参数s 字符串的开头计算连续的字符,而这些字符都完全是accept所指字符串中的字符。若strspn()返回的数值为n,则代表字符串s 开头连续有n 个字符都是属于字符串accept内的字符。
size_t strspn(const char *string, const char *in)
{
register const char *s1, *s2;
for (s1 = string; *s1; s1++) {
for (s2 = in; *s2 && *s2 != *s1; s2++)
if (*s2 == '\0')
break;
}
return s1 - string;
}
18.size_t strcspn(const char *string, const char *notin)
顺序在字符串s1中搜寻与s2中字符的第一个相同字符,返回这个字符在S1中第一次出现的位置。
size_t strcspn(const char *string, const char *notin)
{
register const char *s1, *s2;
for (s1 = string; *s1; s1++) {
for(s2 = notin; *s2 != *s1 && *s2; s2++)
if (*s2)
break;
}
return s1 - string;
}
19.char *strpbrk(const char *s1, const char *s2)
依次检验字符串s1中的字符,当被检验字符在字符串s2中也包含时,则停止检验,并返回该字符位置,空字符NULL不包括在内。
char *strpbrk(register const char *string, register const char *brk)
{
register const char *s1;
while (*string) {
for (s1 = brk; *s1 && *s1 != *string; s1++)
if (*s1)
return (char *)string;
string++;
}
return (char *)NULL;
}
20.char *strstr(const char *str1, const char *str2)
返回str2第一次在str1中的位置,如果没有找到,返回NULL。
char *strstr(register const char *s, register const char *wanted)
{
register const size_t len = strlen(wanted);
if (len == 0) return (char *)s;
while (*s != *wanted || strncmp(s, wanted, len))
if (*s++ == '\0')
return (char *)NULL;
return (char *)s;
}
21.char *strtok(char *string, const char *separators)
分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。
char *strtok(register char *string, const char *separators)
{
register char *s1, *s2;
static char *savestring;
if (string == NULL) {
string = savestring;
if (string == NULL) return (char *)NULL;
}
s1 = string + strspn(string, separators);
if (*s1 == '\0') {
savestring = NULL;
return (char *)NULL;
}
s2 = strpbrk(s1, separators);
if (s2 != NULL)
*s2++ = '\0';
savestring = s2;
return s1;
}
22.size_t strxfrm(char *s1, const char *save, size_t n)
根据num长度拷贝前num个字符到目的字符串中,并返回源字符串长度。该函数还可以自作为返回字符串长度,这时num=0,destination允许传入NULL。
size_t strxfrm(register char *s1, register const char *save, register size_t n)
{
register const char *s2 = save;
while (*s2) {
if (n > 1) {
n--;
*s1++ = *s2++;
} else
s2++;
}
if (n > 0)
*s1++ = '\0';
return s2 - save;
}
十五、time.h
time.h包含获得和使用日期和时间信息的函数的定义。
time.h中的宏:
序号 | 宏 | 说明 |
1 | NULL | 一个空指针常量的值 |
2 | CLOCKS_PER_SEC | 表示每秒的处理器时钟个数 |
time.h中的变量:
序号 | 变量 | 说明 |
1 | size_t | 无符号整数类型(unsigned int),它是 sizeof 关键字的结果 |
2 | clock_t | 一个适合存储处理器时间的类型 |
3 | time_t is | 一个适合存储日历时间类型 |
4 | struct tm | 一个用来保存时间和日期的结构 |
tm 结构的定义如下:
struct tm {
int tm_sec; /* 秒,范围从 0 到 59 */
int tm_min; /* 分,范围从 0 到 59 */
int tm_hour; /* 小时,范围从 0 到 23 */
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月,范围从 0 到 11 */
int tm_year; /* 自 1900 年起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时 */
}
time.h中的库函数:
1.char *asctime(const struct tm *timeptr)
返回一个指向字符串的指针,timeptr 是指向 tm 结构的指针,包含了分解为如下各部分的日历时间。
例如:
#include <stdio.h>
#include <string.h>
#include <time.h>
int main()
{
struct tm t;
t.tm_sec = 28;
t.tm_min = 32;
t.tm_hour = 11;
t.tm_mday = 12;
t.tm_mon = 10;
t.tm_year = 117;
t.tm_wday = 5;
printf("%s",asctime(&t));
return 0;
}
编译后并运行输出:
Fri Nov 12 11:32:28 2017
2.clock_t clock(void)
返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。为了获取 CPU 所使用的秒数,您需要除以 CLOCKS_PER_SEC。
在 32 位系统中,CLOCKS_PER_SEC 等于 1000000 该函数大约每 72 分钟会返回相同的值,这个宏在Windows平台下是1000,而到了Linux平台下就是 1000000。
例如:
#include <time.h>
#include <stdio.h>
int main()
{
clock_t start_t, end_t, total_t;
int i;
start_t = clock();
printf("启动,start_t = %ld\n", start_t);
int j = 0;
printf("运行一个循环,start_t = %ld\n", start_t);
for(i=0; i< 30000; i++)
{
for(j=0; j< 10000; j++)
{
}
}
end_t = clock();
printf("循环结束,end_t = %ld\n", end_t);
printf("CLOCKS_PER_SEC = %d\n", CLOCKS_PER_SEC);
total_t = end_t - start_t;
printf("total_t = %ld\n", total_t);
printf("CPU 占用的时间:%lf\n", (total_t*0.1*10)/CLOCKS_PER_SEC);
printf("停止运行...\n");
return 0;
}
编译后并运行输出:
启动,start_t = 0
运行一个循环,start_t = 0
循环结束,end_t = 661
CLOCKS_PER_SEC = 1000
total_t = 661
CPU 占用的时间:0.661000
停止运行...
3.char *ctime(const time_t *timer)
返回一个表示当地时间的字符串,当地时间是基于参数 timer
timer -- 这是指向 time_t 对象的指针,该对象包含了一个日历时间
返回的字符串格式如下:
Www Mmm dd hh:mm:ss yyyy 其中,Www 表示星期几,Mmm 是以字母表示的月份,dd 表示一月中的第几天,hh:mm:ss 表示时间,yyyy 表示年份。
该函数返回一个 C 字符串,该字符串包含了可读格式的日期和时间信息。
例如:
#include <stdio.h>
#include <time.h>
int main ()
{
time_t curtime;
time(&curtime);
printf("当前时间 = %s", ctime(&curtime));
return 0;
}
编译后并运行输出:
当前时间 = Fri Oct 25 12:48:32 2021
4.double difftime(time_t time1, time_t time2)
返回 time1 和 time2 之间相差的秒数 (time1 - time2),这两个时间是在日历时间中指定的,表示了自纪元 Epoch(协调世界时 UTC:1970-01-01 00:00:00)起经过的时间;time1 -- 这是表示结束时间的 time_t 对象;time2 -- 这是表示开始时间的 time_t 对象;该函数返回以双精度浮点型 double 值表示的两个时间之间相差的秒数 (time2 - time1)。
例如:
#include <time.h>
#include <stdio.h>
#include <unistd.h>
int main ()
{
time_t start_t, end_t;
double diff_t;
printf("启动...\n");
time(&start_t);
printf("休眠 6 秒...\n");
sleep(6);
time(&end_t);
diff_t = difftime(end_t, start_t);
printf("相差时间 = %f\n", diff_t);
printf("退出...\n");
return 0;
}
编译后并运行输出:
启动...
休眠 6 秒...
相差时间 = 6.000000
退出...
5.struct tm *gmtime(const time_t *timer)
使用timer的值来填充tm结构并用协调世界时UTC也被称为格林尼治标准时间GMT表示;timeptr -- 这是指向表示日历时间的 time_t 值的指针;tm 是time.h 定义的结构体。
例如:
#include <stdio.h>
#include <time.h>
#define BST (+1)
#define CCT (+8)
int main ()
{
time_t rawtime;
struct tm *info;
time(&rawtime);
info = gmtime(&rawtime );
printf("当前的世界时钟:\n");
printf("伦敦:%2d:%02d\n", (info->tm_hour+BST)%24, info->tm_min);
printf("中国:%2d:%02d\n", (info->tm_hour+CCT)%24, info->tm_min);
return 0;
}
编译后并运行输出:
当前的世界时钟:
伦敦: 3:08
中国:10:08
6.struct tm *localtime(const time_t *timer)
使用timer的值来填充tm结构。timer的值被分解为tm结构并用本地时区表示。timer -- 这是指向表示日历时间的 time_t 值的指针。
例如:
#include <time.h>
#include <stdio.h>
int main ()
{
time_t rawtime;
struct tm *info;
char buffer[80];
time( &rawtime );
info = localtime( &rawtime );
printf("当前本地时间和日期:%s", asctime(info));
return 0;
}
编译后并运行输出:
当前本地时间和日期:Fri Oct 21 12:09:31 2016
7.strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
根据 format 中定义的格式化规则,格式化结构 timeptr 表示的时间,并把它存储在 str 中;str -- 这是指向目标数组的指针,用来复制产生的 C 字符串。
maxsize -- 这是被复制到 str 的最大字符数。format -- 这是 C 字符串,包含了普通字符和特殊格式说明符的任何组合。这些格式说明符由函数替换为表示 tm 中所指定时间的相对应值。格式说明符是:
timeptr -- 这是指向 tm 结构的指针,该结构包含了一个呗分解为以下各部分的日历时间;如果产生的 C 字符串小于 size 个字符(包括空结束字符),则会返回复制到 str 中的字符总数(不包括空结束字符),否则返回零。
%a 缩写的星期几名称 Sun
%A 完整的星期几名称 Sunday
%b 缩写的月份名称 Mar
%B 完整的月份名称 March
%c 日期和时间表示法 Sun Aug 19 02:56:02 2012
%d 一月中的第几天(01-31) 19
%H 24 小时格式的小时(00-23) 14
%I 12 小时格式的小时(01-12) 05
%j 一年中的第几天(001-366) 231
%m 十进制数表示的月份(01-12) 08
%M 分(00-59) 55
%p AM 或 PM 名称 PM
%S 秒(00-61) 02
%U 一年中的第几周,以第一个星期日作为第一周的第一天(00-53) 33
%w 十进制数表示的星期几,星期日表示为 0(0-6) 4
%W 一年中的第几周,以第一个星期一作为第一周的第一天(00-53) 34
%x 日期表示法 08/19/12
%X 时间表示法 02:50:06
%y 年份,最后两个数字(00-99) 01
%Y 年份 2012
%Z 时区的名称或缩写 CDT
%% 一个 % 符号 %
例如:
#include <time.h>
#include <stdio.h>
int main ()
{
time_t rawtime;
struct tm *info;
char buffer[80];
time( &rawtime );
info = localtime( &rawtime );
strftime(buffer,80,"%x - %I:%M%p", info);
printf("格式化的日期 & 时间 : |%s|\n", buffer );
return 0;
}
编译后并运行输出:
格式化的日期 & 时间 : |10/21/16 - 12:20PM|
8.time_t mktime(struct tm *timeptr)
把 timeptr 所指向的结构转换为一个依据本地时区的 time_t 值;该函数返回一个 time_t 值,该值对应于以参数传递的日历时间。如果发生错误,则返回 -1 值。
例如:
#include <stdio.h>
#include <time.h>
int main ()
{
int ret;
struct tm info;
char buffer[80];
info.tm_year = 116;
info.tm_mon = 9;
info.tm_mday = 21;
info.tm_hour = 12;
info.tm_min = 10;
info.tm_sec = 55;
info.tm_isdst = -1;
ret = mktime(&info);
if( ret == -1 )
{
printf("错误:不能使用 mktime 转换时间。\n");
}
else
{
printf("ret = %d\n",ret);
strftime(buffer, sizeof(buffer), "%c", &info );
printf(buffer);
}
return 0;
}
编译后并运行输出:
ret = 1477023055
10/21/16 12:10:55
9.time_t time(time_t *seconds)
返回自纪元 Epoch(1970-01-01 00:00:00 UTC)起经过的时间,以秒为单位。
如果seconds不为空,则返回值也存储在变量seconds中time_t time(time_t *t)。
例如:
#include <stdio.h>
#include <time.h>
int main ()
{
time_t seconds;
seconds = time(NULL);
printf("自 1970-01-01 起的小时数 = %ld\n", seconds/3600);
return 0;
}
编译后并运行输出:
自 1970-01-01 起的小时数 = 410284