第16章 标准库函数

整型函数

这组函数返回整型值,其分为3类:算术、随机数和字符串转换。
算术<stdlib.h>,包含4个函数

int abs(int value); // 返回参数的绝对值 
// 如果其结果不能用整数表示 则行为未定义
long int labs(long int value); // 也是返回绝对值 但作用对象是长整数
div_t div(int numerator, int denominator); // 分子除以分母 得到商和余数 
// 结果用一个div_t结构返回 
// 其包含2个字段 int quot; 商 int rem; 余数
// 注意 /操作符的运算结果未精确定义 其结果取决于编译器
ldiv_t ldiv(long int number, long int denom); // 与div作用相同 但作用于长整数
// 其返回值是一个长整数结构

随机数<stdlib.h> 包含2个函数,两个函数合在一起用于产生伪随机数。

int rand(void); // 返回一个在[0,RAND_MAX]之间的伪随机数
// RAND_MAX至少为32767 当重复调用时 返回这个范围内的其他数
void srand(unsigned int seed);

为了得到一个更小范围的伪随机数,首先把rand函数的返回值根据所需范围的大小进行取模,然后通过加上或减去一个偏移量来对它进行调整。
为了避免程序每次运行时获得相同的随机数序列,可以调用srand函数。它用它的参数值对随机数发生器进行初始化。一个常用的技巧是使用每天的时间作为随机数产生器的种子,如:

srand((unsigned int)time(0));

字符串转换<stdlib.h>,4个函数,把字符串转换为数值。其中最简单的是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);

如果任何一个上述函数的第1个参数包含了前导空白字符,它们将被跳过。然后函数把合法的字符转换为指定类型的值。如果存在任何非法缀尾字符,它们也被忽略。
后两个函数的第3个参数是转换所执行的基数。如果基数为0,任何在程序中用于书写整数字面值的形式都将被接受,包括指定数字基数的形式,如0x2af4和0377。否则基数值应在2到36的范围内。

浮点型函数

头文件math.h包含了函数库中剩余的数学函数声明,这些函数的返回值及绝大部分参数都是double类型。
如果一个函数的参数不在该函数的定义域之内,如:sqrt(-5.0),称为定义域错误。函数返回一个由编译器定义的错误值,并在errno中存储EDOM这个值。
如果一个函数的结果值过大或过小,无法用double类型表示,则为范围错误。结果值过大,函数会返回HUGE_VAL,而结果值过小,函数返回0。

三角函数<math.h>,7个函数:

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);

前3个函数的参数是一个用弧度表示的角度。中间3个函数返回它们参数的反正弦、反余弦、反正切值。asin和acos的参数取值范围是-1到1,asin和atan的返回值范围是-π/2到π/2,acos的返回值范围是0到π。atan2返回的是y/x的反正切值,返回值范围是-π到π。

双曲函数<math.h>,3个函数:

double sinh(double angle);
double cosh(double angle);
double tanh(double angle);

这些函数返回它们参数的双曲正弦、双曲余弦、双曲正切值。函数的参数是以弧度表示的角度。

对数和指数函数<math.h>,3个函数:

double exp(double x);
double log(double x);
double log10(double x);

对于logbx=logex/logeb,可通过调用log函数来计算。

浮点表示形式<math.h>,3个函数:

double frexp(double value, int *exponent);
double ldexp(double fraction, int exponent);
double modf(double value, double *ipart);

frexp函数根据fraction2exponent=value来计算,并返回fraction的值,其中0.5≤fraction<1。
ldexp返回的是fraction
2exponent的计算值。
当必须在浮点格式不兼容的机器之间传递浮点数时,上述函数非常有用。
modf把一个浮点数分成整数和小数两部分,每个部分都具有和原值一样的符号,整数部分存储于第2个参数所指向的内存位置,小数部分作为函数的返回值返回。

幂<math.h>,2个函数:

double pow(double x, double y); // 返回x**y
double sqrt(double x);

底数、顶数、绝对值和余数<math.h>,4个函数:

double floor(double x);
double ceil(double x);
double fabs(double x);
double fmod(double x, double y);

floor返回不大于其参数的最大整数值,ceil返回不小于其参数的最小整数值,fabs返回其参数的绝对值,fmod返回x除以y的余数。

字符串转换<stdlib.h>,2个函数:

double atof(char const *string);
double strtod(char const *string, char **unused);

日期和时间函数

处理器时间<time.h>,1个函数:

clock_t clock(void);

返回从程序开始执行起处理器所消耗的时间。这个值是一个近似值,如果需要更精确的值,可以在main函数刚开始执行时调用该函数,然后用以后调用该函数所返回的值减去前面这个值。如果机器无法提供处理器时间或者如果时间值太大,无法用clock_t表示时,函数返回-1。
上述函数返回一个数字,它是由编译器定义的。通常是处理器时钟滴答的次数。为了把这个值转换为秒,应该把它除以常量CLOCK_PER_SEC。

当天时间<time.h>,1个函数:

time_t time(time_t *returned_value);

函数返回当前的日期和时间。如果参数是一个非NULL指针,时间值也将通过这个指针进行储存。如果机器无法提供当前的日期和时间,或者时间值太大,无法用time_t变量表示,则函数返回-1。

日期和时间转换<time.h>,2个函数:

char *ctime(time_t const *time_value);
double difftime(time_t time1, time_t time2);
struct tm *gmtime(time_t const *time_value); 
struct tm *localtime(time_t const *time_value);
char *asctime(struct tm const *tm_ptr);
size_t strftime(char *string, size_t maxsize, char const *format, struct tm const *tm_ptr);
time_t mktime(struct tm *tm_ptr);

ctime返回一个指向字符串的指针,字符串格式为:Sun Jul 4 04:02:48 1976\n\0
difftime函数计算time1-time2的差,并把结果值转换为秒。
gmtime把时间值转换为世界协调时间UTC(也叫格林尼治时间),localtime把时间转换为当地时间,但并没有给出二者之间的关系。
tm时间结构的值除了tm_mday是从1开始的,其他都是从0开始的。例如tm_wday的取值范围是0-6。
asctime函数把参数所表示的时间转换为如下的字符串:Sun Jul 4 04:02:48 1976\n\0
strftime函数把tm结构转换为一个根据某个格式字符串而定的字符串,其形式过于灵活,不好使用。
mktime把一个tm结构转换为一个time_t值。

非本地跳转

<setjmp.h>,2个函数:

int setjmp(jmp_buf state);
void longjmp(jmp_buf state, int value);

**这两个函数提供了一种类似goto语句的机制,但它并不局限于一个函数的作用域之内。这些函数常用于深层嵌套的函数调用链。**如果在某个低层的函数中检测到一个错误,你可以立即返回到顶层函数,不必向调用链中的每个中间层函数返回一个错误标志。
如果存在一长串的函数调用链,即使只有最深层的那个函数发现了错误,调用链中的所有函数都必须返回并检查错误代码。在这种情况下使用setjmp和longjmp去除了中间函数的错误代码逻辑,从而对它们进行了简化。

信号

程序中所发生的事件绝大多数都是由程序本身所引发的,例如执行各种语句和请求输入。但是,有些程序必须遇到的事件却不是程序本身所引发的。一个常见的例子就是用户中断了程序。如果部分计算好的结果必须进行保存以避免数据的丢失,程序必须预备对这类事件作出反应,虽然它并没有办法预测什么时候会发生这种情况。
信号就是用于这种目的。信号表示一种事件,它可能异步发生,也就是并不与程序执行过程的任何事件同步。如果程序并未安排怎样处理一个特定的信号,那么当该信号出现时程序就作出一个缺省的反应。标准并未定义这个缺省反应是什么,但绝大多数编译器都选择终止程序。另外,程序可以调用signal函数,或者忽略这个信号,或者设置一个信号处理函数(signal handler),当信号发生时程序就调用这个函数。
SIGABRT是一个由abort函数所引发的信号,用于终止程序。SIGFPE则是由算术上溢或下溢以及除零错误所引发的。这2个信号是同步的,它们都是在程序内部发生的。
SIGINT和SIGTERM是异步的,它们在程序外部产生,通常是由程序的用户所触发,表示用户试图向程序传达一些信息。SIGINT信号在绝大多数机器中都是当用户试图中断程序时发生的,目的是执行一些日常维护工作,并在程序退出前保存数据。SIGTERM则是另一种用于请求终止程序的信号,不配备信号处理函数。
从异步信号的处理函数中调用exit或abort函数是不安全的。

处理信号<signal.h>,2个函数:

int raise(int sig);
void (*signal(int sig, void(*handle)(int)))(int);

通常我们关心的是怎样处理那些自主发生的信号,也就是无法预测其什么时候会发生的信号。
调用raise函数将引发它的参数所指定的信号,程序对这类信号的反应和那些自主发生的信号是相同的,你可以调用这个函数对信号处理函数进行测试。
当一个信号发生时,程序可以使用3种方式对它作出反应。缺省的反应是由编译器定义的,通常是终止程序。程序也可以指定其他行为对信号作出反应:信号可以被忽略,或者程序可以设置一个信号处理函数,当信号发生时调用该函数。函数signal用于指定程序希望采取的反应。
将上述函数进行分解,首先是:

signal(int sig, void(*handler)(int))

其第1个参数是一个信号名,第2个参数是希望为这个信号设置的信号处理函数。其处理函数的参数是该信号名。
然后是:

void (*signal())(int);

其说明signal是一个函数,其返回一个函数指针,后者指向的函数接受一个整型参数且没有返回值。signal函数返回一个指向该信号以前的处理函数的指针。通过保存这个值,你可以为信号设置一个处理函数并在将来恢复为先前的处理函数。如果调用signal函数失败,函数将返回SIG_ERR值。

当一个已经设置了信号处理函数的信号发生时,系统首先恢复对该信号的缺省行为。这样做是为了防止如果信号处理函数内部也发生这个信号可能导致的无限循环。然后,信号处理函数被调用,信号代码作为参数传递给函数。

标准表示信号处理函数可以通过调用exit终止程序。用于处理除了SIGABRT之外所有信号的处理函数也可以通过调用abort终止程序。但是,由于这两个都是库函数,所以当它们被异步信号处理函数调用时可能无法正常运行。如果你必须用这种方式终止程序,注意仍然存在一种微小可能性导致失败。如果发生这种情况,函数的失败可能破坏数据或者表现出奇怪的症状,但程序最终将终止

信号可能在任何时候发生,所以由信号处理函数修改的变量的值可能会在任何时候发生改变。因此,你不能指望这些变量在两条相邻的程序语句中肯定具有相同的值。volatile关键字用于告诉编译器,不要对程序进行相关优化,从而保证程序意思的准确表达。

如果你希望捕捉将来同种类型的信号,从当前这个信号的处理函数返回之前,注意要调用signal函数重新设置信号处理函数。否则只有第1个信号才会被捕捉,接下来的信号将使用缺省反应进行处理。
当每次信号发生时,你必须重新设置信号处理函数。

由于各种计算机对不可预料的错误的反应各不相同,其结果就是信号处理函数的程序比不使用信号处理函数的程序可移植性弱一些。

避免exit函数的多重调用。

打印可变参数列表

<stdarg.h>,包含3个函数:

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);

这组函数用于可变参数列表必须被打印的场合。在调用这些函数之前,arg参数必须使用va_start进行初始化,这些函数都不需要调用va_end。

执行环境

终止执行<stdlib.h>,3个函数:

void abort(void);
void atexit(void (func)(void));
void exit(int status);

abort函数用于不正常地终止一个正在执行的程序。
atexit函数可以把一些函数注册为退出函数,当程序将要正常终止时(或者由于调用exit,或者由于main函数返回),退出函数将被调用。
exit函数用于正常终止程序。如果程序以main函数返回一个值结束,那么其效果相当于用这个值作用参数调用exit函数。
当exit被调用时,所有被atexit函数注册为退出函数的函数将按照它们所注册的顺序被反序依次调用。然后,所有用于流的缓冲区被刷新,所有打开文件被关闭。用tmpfile函数创建的文件被删除。

断言<assert.h>

void assert(int expression);

断言就是声明某种东西应该为真。当它被执行时,这个宏对表达式参数进行测试。如果它的值为假,它就向标准错误打印一条诊断信息并终止程序。
可通过在源文件的头文件assert.h被包含之前,使用:

#define NDEBUG

当NDEBUG被定义之后,预处理器将丢弃所有的断言,这样就消除了编译开销,而不必从源文件中删除所有断言。

环境<stdlib.h>

char *getenv(char const *name);

环境就是一个由编译器定义的名字/值对的列表,它由操作系统进行维护。getenv函数在这个列表中查找一个特定的名字,如果找到,返回一个指向其对应值的指针,如果未找到,返回NULL。

执行系统命令<stdlib.h>

void system(char const *command);

system函数把它的字符串参数传递给宿主操作系统,这样就可以作为一条命令,由系统的命令处理器执行。

排序和查找<stdlib.h>

void qsort(void *base, size_t n_elements, size_t el_size, int (*compare)(void const *, void const *));

qsort函数在一个数组中以升序的方式对数据进行排序。由于它是类型无关的,所以你可以使用qsort排序任意类型的数据,只是数组中元素的长度是固定的。
第1个参数指向需要排序的数组,第2个参数指定数组中元素的数目,第3个参数指定每个元素的长度(以字符为单位),第4个参数是一个函数指针,用于对需要排序的元素类型进行比较。该函数返回一个整数,大于零、等于零和小于零分别表示第1个参数大于、等于、小于第2个参数。在比较函数中必须使用强制类型转换将它们转换为合适的指针类型。

void *bsearch(void const *key, void const *base, size_t n_elements, size_t el_size, int (*compare)(void const *, void const *));

bsearch函数在一个已经排好序的数组中用二分法查找一个特定元素。如果数组尚未排序,则其结果是未定义的。
第1个参数指向你需要查找的值,第2个参数指向查找所在的数组,第3个参数指定数组中元素的数目,第4个参数是每个元素的长度(以字符为单位),最后一个参数是和qsort中相同的比较函数指针。

locale

为了使C语言在全世界范围内更为通用,标准定义了locale,其是一组特定的参数,每个国家可能各不相同。
setlocal函数用于修改整个或部分locale。locale包括了一些用于定义数值如何进行格式化的参数。它们描述的值包括非货币值、本地货币值和国际货币值。locale本身并不执行任何形式的格式化,它只是简单地提供格式化的规范。locale可以指定一个和机器的缺省序列不同的对照序列。在这种情况下,strxcoll用于根据当前的对照序列对字符串进行比较。它所返回的值类型类似strcmp函数的返回值。strxfrm函数把一个当前对照序列的字符串转换为一个位于缺省对照序列的字符串。用这种方式转换的字符串可以用strcmp函数进行比较,比较的结果和用strxcoll比较原先的字符串的结果相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值