C和指针_第16章_标准数函数库_学习笔记
1.整型函数
这组函数返回整型数值。分为算术、随机数和字符串转换
1.1算术<stdlib.h>
int abs( int value );
long int labs( long int value );
div_t div( int numerator, int denominator );
ldiv_t ldiv(long int numer, long int denom );
abs和labs返回参数的绝对值。div和ldiv把它的第二个参数(分母)除以第1个参数(分子),产生商和余数,用一个div_t(ldiv_t)结构返回。包含quot(商)和rem(余数)两个字段。如果不能整除,商将是所有小于代数商的整数中最靠近它的那个整数。如果操作数有一个为负而不能整除时,商是所有小于代数商的整数中最靠近它的那个整数还是所有大于代数商的整数中最靠近它的那个整数,取决于编译器。
1.2随机数<stdlib.h>
int rand( void );
void srand( unsigned int seed );
rand返回一个范围在0到RAND_MAX(至少为32,767)之间的伪随机数。当它重复调用时,函数返回这个范围内的其他数。为了避免程序每次运行时获取相同的随机数序列,我们可以调用srand函数,通过它的参数值对随机数发生器进行初始化。
1.3字符串转换<stdlib.h>
atoi和atol执行基数为10的转换。strtol和strtoul运行转换是指定基数,同时允许访问字符串的剩余部分。
int atoi( char const *string );
long int atol( char const *string );
long int strtol( char const *string, char **unused, int base );
unsigned long int strtoul( char const *string, char **unused, int base );
strtol和strtoul保存一个指向转换值后面第1个字符的指针。如果函数的第2个参数并非NULL,这个指针便保存在第2个参数所指向的位置。这个指针运行字符串的剩余部分进行处理而无需推测转换在字符串的哪个位置终止。函数的第3个参数是转换所执行的基数(0或在2到36范围之间)。如果基数是0,任何在程序中用于书写整数字面值的形式都将被接受,包括制定数字基数的形式。
如果函数的string参数中并不包含一个合法的数值,函数就返回0.如果被转换的值无法表示,函数就在errno中存储ERANGE这个值,并返回下表的一个值。
函数 | 返回值 |
---|---|
strtol | 如果值太大且为负数,返回LONG_MIN。如果值太大为正数,放回LONG_MAX |
strtoul | 如果值太大,返回ULONG_MAX |
2.浮点型函数
头文件math.h包含了函数库中剩余的数学函数的声明。这些函数的返回值以及绝大多数参数都是double类型。
如果一个函数的参数不在该函数的定义域内,称为定义域错误,errno中存储EDOM这个值。
如果一个函数的结果值太大或太小,无法用double类型表示,称为范围错误。errno会不会返回ERANGE取决于编译器。
2.1三角函数<math.h>
double sin( double angle );
double cos( double angle );
double tan( double angle );
double asin( double value );
double acos( double value );
double atan( double value );
double atan2( double x, double y );
angle是一个用弧度来表示的角度。asin和atan的返回值是-π/2和π/2之间的一个弧度,acos的返回值是0和π之间的一个弧度。atan2函数返回表达式y/x的反正切值,但它使用这两个参数的符号决定值在那个象限。
2.2双曲函数<math.h>
double sinh( double angle );
double cosh( double angle );
double tanh( double angle );
2.3对数和指数函数<math.h>
double exp( double x );
double log( double x );
double log10( double x );
exp函数返回e^x,log函数返回x以e为底的对数。
2.4浮点表示形式<math.h>
double frexp( double value, int *exponent );
double ldexp( double fraction, int *exponent );
double modf( double value, double *ipart );
这三个函数提供一种根据一个编译器定义的格式存储一个浮点值的方法。
frexp函数计算一个指数(exponent)和小数(fraction),使fraction ×2^exponent = value,其中0.5≤fraction<1。
exponent存储在第2个参数所指向的内存位置,函数返回fraction的值。ldexp的返回值为fraction ×2^exponent。用于必须在那些浮点格式不兼容的机器之间传递浮点数的时候(fraction也是浮点,也不兼容吧?)。
modf函数把一个浮点值分成整数和小数两部分,每个部分都具有和原值一样的符号。整数部分以double类型存储在第2个参数指向的内存位置,小数部分作为函数的返回值返回。
2.5幂<math.h>
double pow( double x, double y );
double sqrt( double x );
2.6底数、顶数、绝对值和余数<math.h>
double floor( double x );
double ceil( double x );
double fabs( double x );
double fmod( double x, double y );
floor返回不大于其参数的最大整数值,ceil返回不小于其参数的最小整数值。fabs返回其参数绝对值,fmod返回x除以y所产生的余数。
2.7字符串转换<stdlib.h>
double atof( char const *string );
double strtod( char const *string, char **unused );
strtod保存一个指向转换值后面第1个字符的指针。如果函数的第2个参数并非NULL,这个指针便保存在第2个参数所指向的位置。这个指针运行字符串的剩余部分进行处理而无需推测转换在字符串的哪个位置终止。
如果函数的string参数中并不包含一个合法的数值,函数就返回0.如果被转换的值太大或太小,函数就在errno中存储ERANGE这个值,太大返回HUGE_VAL,太小返回0.
3.日期和时间函数
3.1处理器时间<time.h>
clock_t clock( void );
clock函数返回从程序开始执行起处理器所消耗的时间,单位通常是处理器时钟滴答的次数,若转换为秒单位,需除以常量CLOCKS_PER_SEC。如果机器无法提供处理器时间或者如果时间值太大,无法用clock_t变量表示,函数就返回-1.
3.2当天时间<time.h>
time_t time( time_t *returned_value );
time返回当前的日期和时间。如果参数是一个非NULL的指针,时间值也将通过这个指针进行存储。如果机器无法提供当前的日期和时间,或者时间值太大,无法用time_t变量表示,函数返回-1.
3.3日期和时间的转换<time.h>
char *ctime( time_t const *time_value );
double difftime( time_t time1, time_t time2 );
difftime返回time1-time2的差,并将结果转换为秒。ctime函数返回一个指向字符串的指针,字符串格式为:星期+月份+日期+时间+年月日。标准并未提及存储这个字符串的内存类型,许多编译器使用一个静态数组,所以需要保存时应事先复制一份。ctime实际上可能以下面这种方式实现:
asctime( localtime( time_value ));
struct tm *gmtime( time_t const *time_value );
struct tm *localtime( time_t const *time_value );
gmtime把时间转换成世界协调时间(UTC)。localtime把时间转换成当地时间。tm结构的字段如下表所示:
类型 & 名称 | 范围 | 含义 |
---|---|---|
int tm_sec; | 0-61 | 分之后的秒数(运行偶尔出现的“闰秒”加到每年的最后一分钟) |
int tm_min; | 0-59 | 小时之后的分数 |
int tm_hour; | 0-23 | 午夜之后的小时数 |
int tm_mday; | 1-31 | d当月的日期 |
int tm_mon; | 0-11 | 1月之后的月数,1月为0 |
int tm_year; | 0-?? | 1900之后的年数 |
int tm_wday; | 0-6 | 星期天之后的天数 |
int tm_yday; | 0-365 | 1月1日之后的天数 |
int tm_isdat; | 夏令时标志 |
char *asctime( struct tm const *tm_ptr );
size_t strftime( char *string, size_t maxsize, char const *format, struct tm const *tm_ptr );
asctime将tm表示的时间值转换成ctime函数所用的一样的格式。strftime函数把一个tm结构体转换为一个根据某个格式字符串而定的字符串。如果转换结果字符串的长度小于maxsize参数,返回字符串长度,否则返回-1且数组内容未定义。
格式字符串包含了普通字符和格式代码。普通字符被复制到它们原先在字符串中出现的位置。格式代码则被一个日期或时间值代替。格式代码包括一个%字符,后面跟一个表示所需值的字符。
代码 | 被……代替 |
---|---|
%% | 一个%字符 |
%a | 一个星期的某天,以当地的星期几的简写形式表示 |
%A | 一个星期的某天,以当地的星期几的全写形式表示 |
%b | 月份,以当地月份名的简写形式表示 |
%B | 月份,以当地月份名的全写形式表示 |
%c | 日期和时间,使用%x%X |
%d | 一个月的第几天(01-31) |
%H | 小时,以24小时的格式(00-23) |
%I | 小时,以12小时的格式(00-12) |
%J | 一年的第几天(0-366) |
%m | 月数(01-12) |
%M | 分钟(00~59) |
%P | AM或PM(不论哪个合适)的当地对等表示形式 |
%S | 秒(0-61) |
%U | 一年的第几个星期(0-53),以星期日为第1天 |
%w | 一星期的第几天,星期日为第0天 |
%W | 一年的第几个星期(0-53),以星期一为第1天 |
%x | 日期,使用本地的日期格式 |
%X | 时间,使用本地的时间格式 |
%y | 当前世纪的年份(00-99) |
%Y | 年份的全写形式(如1984) |
%Z | 时区的简写 |
time_t mktime( struct tm *tm_ptr );
4.非本地跳转<setjmp.h>
setjmp和longjmp函数提供一种类似goto语句的机制,但它并不局限于一个函数的作用域之内。这些函数常用于深层嵌套的函数调用链。如果在某个底层的函数中检测到一个错误,可以立即返回顶层的函数,不必向调用链中的每个中间层函数返回一个错误标志。
int setjmp( jmp_buf state );
void longjmp( jmp_buf state, int value );
声明一个jmp_buf变量,并调用setjmp函数初始化,返回值为0.setjmp把程序的状态信息(例如,堆栈指针的当前位置和程序的计数器)保存到跳转缓冲区。调用该函数的函数成为“顶层”函数。以后,在顶层函数或者其他任何它所调用的函数(无论是直接调用还是间接调用)内调用longjmp函数,将会导致这个被保存的状态重新恢复。longjmp的效果是使执行流通过再次从setjmp返回,从而立即跳转回顶层函数中,此时,setjmp返回的值是longjmp的第2个参数。
5.信号
信号(signal)表示一种事件,它可能异步的发生,也就是并不与程序执行过程的任何事件同步。
5.1信号名<signal.h>
信号 | 含义 |
---|---|
SIGABRT | 程序请求异常终止,由abort函数引发。 |
SIGFPE | 具体错误由编译器确定,常见有算术上溢、下溢以及除零错误 |
SIGILL | 检测到非法指令,可能由不正确的编译器设置导致 |
SIGSEGV | 检测到内存的非法访问,程序访问未分配内存或者访问超过内存访问的边界(segmentation violation) |
SIGINT | 程序外部产生,通常是用户尝试中断程序时发生,一般定义处理函数来执行日常维护和退出前保存数据(interrupt) |
SIGTERM | 程序外部产生,请求终止程序的信号(terminate) |
5.2处理信号<signal.h>
int raise( int sig );
raise函数用于显示的引发参数所指定的信号。当一个信号发生时,程序可以使用三种方式对其作出反应。默认的反应由编译器定义,一般是终止程序。程序也可以指定其他对信号的反应行为:忽略或者信号处理函数:
void ( *signal( int sig, void ( *handler )( int ) ) )( int );
signal接收2个参数,第1个参数是信号,第2个参数是希望为这个信号设置的信号处理函数的指针。返回值是一个接收1个整型参数返回值是空的函数指针。事实上,signal函数返回一个指向该信号以前的处理函数的指针。如果因为非法信号导致调用失败,signal返回SIG_ERR。SIG_DEF和SIG_IGN可以用作signal函数的第2个参数。
5.3信号处理函数
当一个已经设置了信号处理函数的信号发生时,系统为了防止如果信号处理函数内部也产生这个信号可能导致的无限循环,将首先恢复对该信号的默认行为,然后调用信号处理函数。
信号处理函数可能执行的工作类型是很有限的。如果信号是异步的,也就是说不是由于调用abort或raise函数引起的,信号处理函数就不应调用除signal之外的任何的库函数,因为在这种情况下其结果是未定义的。而且,信号处理函数除了能向一个类型为volatile sig_atomic_t的静态变量赋一个值以外,可能无法访问其他静态数据。(信号处理函数修改的变量值可能会在任何时候发生改变,因此可能在两条相邻的程序语句语句中变量的值不同,volatile关键字将告诉编译器这个事实。即当要求使用volatile声明变量值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。精确地说就是,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问)
这些严格的限制是由于信号处理的本质产生的。信号通常用于提示发生了错误。在这些情况下,CPU的行为是精确定义的。但在这个程序中,错误所处的上下文环境可能不同,因此信号并不一定能良好定义。
从一个信号处理函数返回导致程序的执行流从信号发生的地点恢复执行(SIGFPE例外)。如果希望捕捉将来同种信号,从当前这个信号的处理函数返回之前注意要调用signal函数重新设置信号处理函数。否则,只有第1个信号才会被捕捉,接下来的同种信号将按默认处理。
6.打印可变参数列表<stdarg.h>
int vprintf( char const *format, va_list arg );
int vfprintf( FILE *stream, char const *format, va_list arg );
int vsprintf( char *buffer, char const *format, va_list arg );
这组函数用于可变参数列表必须被打印的场合。必须包含<stdio.h>和<stdarg.h>。在调用这些函数之前,arg参数必须使用va_start进行初始化,这些函数不需要调用va_end。
7.执行环境
这些函数与程序的执行环境进行通信或者对程序的执行环境施加影响。
7.1终止执行<stdlib.h>
void abort( void );
void atexit( void (func)( void ) );
void exit( int status );
abort函数用于不正常地终止一个正在执行的程序,将触发SIGABRT信号,若设置了信号处理函数,在程序终止前可以采取任何措施,哪怕不终止程序。
atexit函数可以把一些函数注册为退出函数(exit function)。当程序将要正常终止(或者由于调用exit,或者由于main函数返回),退出函数将被调用。
当exit函数被调用时,所有被atexit函数注册为退出函数的函数将按照它们所注册的顺序被反序调用。然后,所有用于流的缓冲区被刷新,所有打开文件被关闭。用tmpfile函数创建的文件被删除。然后退出状态返回给宿主环境,程序停止执行。
7.2断言<assert.h>
void assert( int expression );
assert宏由ANSIC实现,常用于调试程序。当assert被执行时,这个宏对表达式参数进行测试。如果参数表达式值为0,它就向标准错误打印一条诊断信息并终止程序,这个消息格式由编译器定义,但会包含这个表达式和源文件的名字以及这个断言所在行号。
该宏提供了一个对应该为真的东西进行检查的方便方法,例如函数在对一个不能为NULL的指针参数进行调用前用assert进行验证。当程序被完整地测试完毕之后,可以在编译时通过定义NDEBUG消除所有断言(使用-DNDEBUG编译器命令行选项或在源文件assert.h被包含之前增加#define NDEBUG语句)。
7.3环境<stdlib.h>
环境是一个由编译器定义的名字/值对的列表,由操作系统进行维护。getenv函数在这个列表中查找一个特定的名字,如果找到,返回一个指向其对应值的指针,程序不能修改返回的字符串。如果名字未找到,函数就返回NULL指针。
char *getenv( char const *name );
7.4执行系统命令<stdlib.h>
void system( char const *command );
system函数把它的字符串参数传递给宿主操作系统,由系统的命令处理器执行。如果参数是NULL,则system用于询问命令处理器是否实际存在。在这种情况下,如果存在一个可用的命令处理器,system返回非0值,否则返回0。
7.5排序和查找<stdlib.h>
void qsort( void *base, size_t n_elements, size_t el_size, int (*compare)(void const *, void const *) );
qsort函数在一个数组中以升序的方式对数据进行排序,与类型无关,只是数组内元素的长度需固定。第1个参数指向需要排序的数组,第2个参数指定数组中元素的数目,第3个参数指定每个元素的长度(以字节为单位)。第4个参数是一个函数指针,用于对需要排序的元素类型进行比较。
比较函数应该返回一个整数,大于0、等于0和小于0表示第1个参数大于、等于和小于第2个参数。
void *bsearch( void const *key, coid const *base, size_t n_elements, size_t el_size, int (*compare)(void const *, void const *) );
8.locale
为了使C语言在全世界的范围内更为通用,标准定义了locale,这是一组特定的参数,每个国家可能各不相同。
char *setlocale( int category, char const *locale );
setlocale常用于修改整个或部分locale,可能影响库函数的运行方式。category参数指定locale的哪个部分需要进行修改,允许出现的值列于下表。如果第2个参数locale为NULL,函数将返回一个指向给定类型的当前locale的名字的指针。这个值可能被保存并继续在后续的setlocale中使用用以恢复。如果第2个参数不是NULL,它指定需要使用的新locale。如果函数调用成功,它将返回新locale的值,否则返回一个NULL指针,原来的locale不受影响。
值 | 修改 |
---|---|
LC_ALL | 整个locale |
LC_COLLATE | 对照序列,将影响strcoll和strxfrm函数的行为 |
LC_CTYPE | 定义于ctype.h中的额函数所使用的字符类型分类信息 |
LC_MONETARY | 在格式化货币值时使用的字符 |
LC_NUMERIC | 在格式化非货币值时使用的字符。同时修改由格式化输入/输出函数和字符串转换函数所使用的小数点符号 |
LC_TIME | strftime函数的行为 |
struct lconv *localeconv( void );
localeconv函数用于获得根据当前的locale对非货币值和货币值进行合适的格式化所需要的信息。该函数不实际执行格式化任务,只是提供一些如何进行格式化的信息。lconv结构包含两种类型的参数:字符和字符指针。字符参数为非负值,如果一个字符参数为CHAR_MAX,那么这个值就在当前的locale中不可用(不使用)。对于字符指针,如果指向一个空字符串,与前者同意。
字段和类型 | 含义 |
---|---|
char *decimal_point | 用作小数点的字符。这个值绝不能是个空字符串。例如:"." |
char *thousands_sep | 用作分隔小数点左边各组数字的符号。例如:"," |
char *grouping | 指定小数点左边多少数字组成。例如:"\3" |
字段和类型 | 含义 |
---|---|
char *currency_symbol | 本地货币符号 |
char *mon_decimal_point | 小数点字符 |
char *mon_thousands_sep | 用于分隔小数点左边各组数字的字符 |
char *mon_group | 指定出现在小数点左边各组数字的数字个数 |
char *postive_sign | 用于提示非负值的字符串 |
char *negative_sign | 用于提示负值的字符串 |
char frac_digits | 出现在小数点右边的数字个数 |
char p_cs_precedes | 如果currency_symbol出现在一个非负值之前,其值为'\1';如果出现在后面,其值为'\0' |
char n_cs_precedes | 如果currency_symbol出现在一个负值之前,其值为'\1';如果出现在后面,其值为'\0' |
char p_sep_by_space | 如果currency_symbol和非负值之间用一个空格字符分隔,其值为'\1';否则其值为'\0' |
char n_sep_by_space | 如果currency_symbol和负值之间用一个空格字符分隔,其值为'\1';否则其值为'\0' |
char p_sign_posn |
提示positive_sign出现在一个非负值的位置,允许下列值: '\0' 货币符号和值两边的括号 '\1' 正号出现在货币符号和值之前 '\2' 正号出现在货币符号和值之后 '\3' 正号紧邻货币符号之前 '\4' 正号紧随货币符号之后 |
char n_sign_posn | 提示negative_sign出现在一个负值中的位置。用于p_sign_posn的值也可用于此处 |
int strcoll( char const *s1, char const *s2 );
size_t strcfrm( char *s1, char const *s2, size_t size );
一个机器的字符集的对照序列是固定的。但setlocale提供了一种方法指定不同的序列,当使用一个并非默认的对照列表时,可以采用上面两个函数。strcoll函数对两个根据当前locale的LC_COLLATE类型参数指定的字符串进行比较,比较可能比strcmp需要多得多的计算了,因为其需要遵循一个并非本地机器的对照序列。当字符串必须以这种方式反复进行比较时,使用strcfrm函数可以减少计算量。strcfrm把根据当前locale解释的第2个参数转换成一个不依赖于locale的字符串,尽管转换后的字符串内容不确定,但比较结果和strcoll相同。
8.3改变locale的效果
- locale可能向正在执行的程序所使用的字符集增加字符(但可能不会改变现存字符的含义)。例如,许多欧洲语言使用了能够提示重音、货币符号和其他特殊符号的扩展字符集。
- 打印的方向可能会改变。尤其,locale决定一个字符应该根据前面一个被打印的字符的哪个方向进行打印。
- printf和scanf函数机组使用当前locale定义的小数点符号。
- 如果locale扩展了正在使用的字符集,isalpha、islower、isspace和isupper函数可能比以前包含更多的字符。
- 正在使用的字符集的对照序列可能会改变。这个序列有strcoll函数使用,用于字符串之间的相互比较。
- strftime函数产生的日期和时间格式的很多方面都是特定于locale的。
上一篇: C和指针第十一章 动态内存分配
下一篇: C语言数组与指针的关系