常用C库简介

一、数学库<math.h>

数学库包含许多有用的数学函数。头文件math.h提供了这些函数的函数声明或原型。注意角度的单位为弧度。

ANSI C标准数学函数原型描述
原型 描述
double acos (double x) 返回余弦值为x的角度值
double asin (double x) 返回正弦值为x的角度值
double atan (double x) 返回正切值为x的角度值
double atan2 (double y, double x) 返回正切值为y/x的角度值
double cos (double x) 返回x的余弦值
double sin (double x) 返回x的正弦值
double tan (double x) 返回x的正切值
double exp (double x) 返回x的指数函数的值(e的x次方)
double log (double x) 返回x的自然对数值
double log10 (double x) 返回x的以10为底的对数值
double pow (double x, double y) 返回x的y次幂的值
double sqrt (double x) 返回x的平方根
double ceil (double x) 返回不小于x的最小整数值
double fabs (double x) 返回x的绝对值
double floor (double x) 返回不大于x的最大整数值

pi的值可以通过计算表达式4 * atan(1)得到:

const int PI = 4 * atan (1);

UNIX系统要求使用-lm标记以指示链接器搜索数学库:

cc rect_pol.c -lm

Linux的gnu编译器也使用相同的形式:

gcc rect_pol.c -lm

 

二、通用工具库<stdlib.h>

通用工具库包含各种函数,其中包括随机数产生函数、搜索和排序函数、转换函数和内存管理函数等。

1. exit( )atexit( )函数

void exit (int)
int atexit (void (*)(void))

程序从main( )返回时会自动调用exit( )函数。另外,ANSI C标准还增加了一些新功能。其中一个是,可以指定执行exit( )时调用的特定函数。通过对退出时调用的函数进行注册,atexit( )函数也提供这项功能;atexit( )函数使用函数指针作为参数。示例程序如下:

// byebye.c -- atexit( )示例程序
#include <stdio.h>
#include <stdlib.h>

void sign_off (void);
void too_bad (void);

int main (void)
{
    int n;

    atexit (sign_off); // 注册sign_off( )函数
    puts ("Enter an integer: ");
    if (scanf ("%d", &n) != 1)
    {
        puts ("That's no integer!");
        atexit (too_bad);
        exit (EXIT_FAILURE);
    }
    printf ("%d is %s.\n", n, (n % 2 == 0) ? "even" : "odd");

    return 0;
}

void sign_off(void)
{
    puts ("Thus terminates another magnificent program from");
    puts ("SeeSaw Software!");
}

void too_bad (void)
{
    puts ("SeeSaw Software extends its heartfelt condolences");
    puts ("to you upon the failure of your program");
}

atexit( )把作为其参数的函数在调用exit( )时执行的函数列表中进行注册。ANSI保证在这个列表中至少可放置32个函数。通过使用一个单独的atexit( )调用把每个函数添加到列表中。最后,调用exit( )函数时,按先进后出的顺序执行这些函数。

由atexit( )注册的函数的类型应该为不接受任何参数的返回void的函数。通常它们执行内部处理事务。

注意,main( )终止时会隐式地调用exit( );因此,即使未显式地调用eixt( ),也会调用经atexit( )注册的函数。

对于exit( )函数:exit( )函数执行了atexit( )指定的函数之后,将做一些自身清洁工作。它会刷新所有输出流、关闭所有打开的流,并关闭通过调用标准I/O函数tmpfile()创建的临时文件。然后,exit( )把通知返回给主机环境。习惯上,UNIX程序用0表示终止,用非零值表示失败。ANSI C定义了可移植的表示失败的宏EXIT_FAILURE和表示成功的宏EXIT_SUCCESS,但是exit( )也接受用0表示成功。ANSI C中,在非递归的main( )函数中使用exit( )函数等价于使用关键字return。但是,在main( )之外的函数中使用exit( )也会终止程序。

 

2. qsort( )函数

void qsort (void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

快速排序法是最有效的排序算法之一,对大型数组而言更是如此。

函数的第一个参数为指向要排序的数组头部的指针;第二个参数为需要排序的项目数量;第三个参数为每个数组元素的大小;最后一个参数为一个指向函数的指针,被指向的函数用于确定排序顺序。

函数调用的结果是数组被按照递增的顺序被排序。

比较函数:

int (*compar) (const void *, const void *)

比较函数接受两个参数,即分别指向进行比较的两个项目的指针。如果第一个项目的值大于第二个项目的指,那么比较函数返回整数;如果两个项目的值相等,那么返回0;如果第一个项目的值小于第二个项目的值,那么返回负数。示例程序如下:

// qsorter.c -- 使用qsort()对一组数字排序
#include <stdio.h>
#include <stdlib.h>

#define NUM 40

void fillarray (double ar[], int n);
void showarray (const double ar[], int n);
int mycomp (const void * p1, const void * p2);

int main (void)
{
    double vals[NUM];
    fillarray (vals, NUM);
    puts ("Random list: ");
    showarray (vals, NUM);
    qsort (vals, NUM, sizeof(double), mycomp);
    puts ("Sorted list: ");
    showarray (vals, NUM);

    return 0;
}

void fillarray (double ar[], int n)
{
    int index;

    for (index = 0; index < n; index++)
        ar[index] = (double)rand() / ((double)rand() + 0.1);
}

void showarray (const double ar[], int n)
{
    int index;

    for (index = 0; index < n; index++)
    {
        printf ("%9.4f ", ar[index]);
        if (index % 6 == 5)
            putchar ('\n');
    }
    if (index % 6 != 0)
        putchar('\n');
}

int mycomp (const void * p1, const void * p2)
{
    const double * a1 = (const double *)p1;
    const double * a2 = (const double *)p2;

    return *a1 - *a2;
}

 

三、诊断库<assert.h>

诊断库是设计用于辅助调试程序的小型库,有宏assert( )构成。该宏接受整数表达式作为参数。如果表达式值为假(非零),宏assert()向标准错误流(stderr)写一条错误信息并调用abort( )函数以终止程序;值为真则什么也不做。错误信息包括失败的判断表达式、该判断所在的文件名和行号。例如:

assert (z >= 0)

如果z < 0,则判断失败。assert会中断程序,并且可能的输出如下:

Assertion failed: z >= 0, file C:\use_assert.c, line 14

如果您认为已经排除了程序的漏洞不再需要assert调试,那么可以把宏定义#define NDEBUG放在assert.h包含语句的前面,并重新编译该程序。编译器将禁用文件中所有的assert()语句。去掉#define NDEBUG并重新编译程序,就又重新启动了assert( )语句。

 

四、string.h库中的memcpy( )memmove( )

不能把一个数组的值直接赋予另一个数组,因此,我们使用循环把数组中的元素逐个复制到另一个数组。一个例外情况是:可以使用strcpy( )和strncoy( )函数复制字符数组。memcpy( )和memmove( )函数为复制其他类型的数组提供了类似的便利工具。下面是这两个函数的原型:

void *memcpy (void * restrict s1, const void * restrict s2, size_t n);
void *memmove (void *s1, const void *s2, size_t n);

这两个函数均从s2指向的位置复制n字节数据到s1指向的位置,且返回s1的指。两者间的差异由关键字restrict造成,即memcpy( )可以假定两个内存区域之间没有重叠。memmove( )函数则不作这个假设,因此,复制过程类似于首先将所有字节复制到一个临时缓冲区,然后再复制到最终目的地。对存在重叠的两个区域使用memcpy( )的结果是不可欲知的。因此,使用memcpy( )时,您必须确保没有重叠区域。第三个参数指定要复制的字节数。注意,字节数不一定等于元素的个数。一般情况下,n = nmemb * sizeof(type)。

 

五、可变参数:stdarg.h

头文件stdarg.h为函数提供了接受可变个数的参数的能力。为使用这个特性,需要按如下步骤进行操作:

  1. 在函数原型中使用省略号。double sum(int lim, ...) {......}
  2. 在函数定义中创建一个va_list类型的变量。va_list ap;
  3. 用宏将该变量初始化为一个参数列表。va_start (ap, lim);
  4. 用宏访问这个参数列表。double tic = va_arg(ap, double);
  5. 用宏完成清理工作。va_end(ap);
  6. 附加操作:复制va_list,应当在va_list被初始化后进行。va_copy(ls_list dest, ls_list src);

可变参数列表必须为函数的参数列表的最后一个,且之前需要至少存在一个普通参量:

void f1 (int n, ...); // 合法
int f2 (int n, char * s, ...); // 合法
char f3 (char c1, ..., char c2); // 非法,省略号不是最后一个参量
double f3 (...); //非法,没有普通参量

需要使用到的数据类型和宏:

va_list ap; // 用于存放参数的变量

va_start(ap, lim); // 初始化参数列表。lim为函数列表中最后一个普通参量

double tic = va_arg(ap, double); // 依此取得一个参数,第二个参数为需要获得的数据类型。

va_end(ap); // 最后的清理工作

va_copy(apcopy, ap); // 将参数列表ap复制到apcopy,注意要在ap被初始化后进行。

程序示例:

// varargs.c -- 使用可变个数的参数
#include <stdio.h>
#include <stdarg.h>

double sum (int, ...);

int main (void)
{
    double s, t;
    s = sum(3, 1.1, 2.5, 13.3);
    t = sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1);
    printf("return value for "
           "sum(3, 1.1, 2.5, 13.3): %g\n",s);
    printf("return value for "
           "sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1): %g\n", t);

    return 0;
}

double sum (int lim, ...) // 使用lim记录传入参数的个数
{
    va_list ap;  // 声明用于存放参数的变量
    double tot = 0;
    int i;
    va_start(ap, lim); // 初始化为参数列表。注意:需要由最后一个普通参量来确定不定参数列表的地址。
    for (i = 0; i < lim; i++)
        tot += va_arg(ap, double); // 访问参数列表中的项目
    va_end(ap); // 清理参数列表
    return tot;
}

va_copy的使用:

va_list ap;
va_list apcopy;
double tic;
...
va_start(ap, lim);
va_copy(apcopy, ap); // 将ap复制到apcopy
tic = va_arg(ap, double);
...

 

posted @ 2013-01-28 16:45  qwertWZ  阅读(4998)  评论(0编辑  收藏  举报