第十六章 C预处理器和C库

  • 程序清单16.1,prepro.c:

/* preproc.c -- 简单的预处理器示例 */
#include <stdio.h>
#define TWO 2 /* 可以使用注释 */
#define OW "Consistency is the last refuge of unimagina\
tive. - Oscar Wilde" /* 反斜杠把该定义延续到下一行 */
#define FOUR TWO*TWO
#define PX printf("X is %d.\n", x)
#define FMT "X is %d.\n"

int main(void)
{
    int x = TWO;
    
    PX;
    x = FOUR;
    printf(FMT, x);
    printf("%s\n", OW);
    printf("TWO: OW\n");
    
    return 0;
}

输出结果:

  • 程序清单16.2,mac_arg.c:

/* mac_arg.c -- 带参数的宏 */
#include <stdio.h>
#define SQUARE(X) X*X
#define PR(X) printf("The result is %d.\n", X)

int main(void)
{
    int x = 5;
    int z;
    
    printf("x = %d\n", x);
    z = SQUARE(x);
    printf("Evaluating SQUARE(x): ");
    PR(z);
    z = SQUARE(2);
    printf("Evaluating SQUARE(2): ");
    PR(z);
    printf("Evaluating SQUARE(x + 2):");
    PR(SQUARE(x + 2)) ;
    printf("Evaluating 100 / SQUARE(2): ");
    PR(100 / SQUARE(2));
    printf("x is %d.\n", x);
    printf("Evaluating SQUARE(++x): ");
    PR(SQUARE(++x));
    printf("After incrementing, x is %x.\n", x);
    
    return 0;
}

输出结果:

  • 程序清单16.3,subst.c:

/* subst.c -- 在字符串中替换 */
#include <stdio.h>
#define PSQR(x) printf("The square of " #x " is %d.\n", ((x)*(x)))

int main(void)
{
    int y = 5;
    
    PSQR(y);
    PSQR(2 + 4);
    
    return 0;
}

输出结果:

  • 程序清单16.4,glue.c:

/* glue.c -- 使用 ## 运算符 */
#include <stdio.h>
#define XNAME(n) x ## n
#define PRINT_XN(n) printf("x" #n " = %d\n", x ## n)

int main(void)
{
    int XNAME(1) = 14; // 变成 int x1 = 14;
    int XNAME(2) = 20; // 变成 int x2 = 20;
    int x3 = 30;
    
    PRINT_XN(1); // 变成 printf("x1 = %d\n", x1);
    PRINT_XN(2); // 变成 printf("x2 = %d\n", x2);
    PRINT_XN(3); // 变成 printf("x3 = %d\n", x3);
    
    return 0;
}

输出结果:

  • 程序清单16.5,variadic.c:

// variadic.c -- 变参宏
#include <stdio.h>
#include <math.h>

#define PR(X, ...) printf("Message " #X ": " __VA_ARGS__)

int main(void)
{
    double x = 48;
    double y;
    
    y = sqrt(x);
    PR(1, "x = %g\n", x);
    PR(2, "x = %.2f, y = %.4f\n", x, y);
    
    return 0;
}

输出结果:

  • 程序清单16.6,16.7,16.8,该项目由一个头文件和两个源文件构成,代码如下:

names_st.h:

// names_st.h -- names_st 结构的头文件
// 常量
#include <string.h>
#define SLEN 32

// 结构声明
struct names_st {
    char first[SLEN];
    char last[SLEN];
};

// 类型定义
typedef struct names_st names;

//函数原型
void get_names(names *);
void show_names(const names *);
char * s_gets(char * st, int n); 

names_st.c:

// names_st.c -- 定义 names_st.h 中的函数
#include <stdio.h>
#include "names_st.h" // 包含头文件

// 函数定义
void get_names(names * pn)
{
    printf("Please enter your first name: ");
    s_gets(pn->first, SLEN);
    printf("Please enter your last name: ");
    s_gets(pn->last, SLEN);
}

void show_names(const names * pn)
{
    printf("%s %s", pn->first, pn->last);
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n'); // 查找换行符
        if (find)                 // 如果地址不是NULL 
            *find = '\0';          // 在此处放置一个空字符
        else
            while (getchar() != '\n')
                continue;         // 处理输入行中剩余字符 
    }
    
    return ret_val; 
}

useheader.c:

// useheader.c -- 使用 names_st 结构
#include <stdio.h>
#include "names_st.h"
// 记住要链接 names_st.c

int main(void)
{
    names candidate;
    
    get_names(&candidate);
    printf("Let't welcome ");
    show_names(&candidate);
    printf(" to this program!\n");
    
    return 0;
} 

输出结果:

  • 程序清单16.9,ifdef.c:

/* ifdef.c -- 使用条件编译 */
#include <stdio.h>
#define JUST_CHECKING
#define LIMIT 4

int main(void)
{
    int i;
    int total = 0;
    
    for (i = 1; i <= LIMIT; i++)
    {
        total += 2 * i * i + 1;
#ifdef JUST_CHECKING
        printf("i = %d, running total = %d\n", i, total);
#endif
    }
    printf("Grand total = %d\n", total);
    
    return 0;
}

输出结果:

  • 程序清单16.10,16.11,该项目由一个头文件和一个源文件构成,头文件中的函数未定义,代码如下:

names.h:

// names.h -- 修订后的 names_st 头文件,避免重复包含

#ifndef NAMES_H_
#define NAMES_H_

// 明示常量
#define SLEN 32

// 结构声明
typedef struct names_st {
    char first[SLEN];
    char last[SLEN];
} names;

// 函数原型
void get_names(names *);
void show_names(const names *);
char * s_gets(char * st, int n);

#endif 

doubincl.c:

#include <stdio.h>
#include "names.h"
#include "names.h"  //不小心第二次包含头文件

int main(void)
{
    names winner = { "Less", "Ismoor" };
    printf("The winner is %s %s.\n", winner.first, winner.last);
    
    return 0;
}

输出结果:

  • 程序清单16.12,predef.c:

// predef.c -- 预定义宏和预定义标识符
#include <stdio.h>

void why_me(void);

int main(void)
{
    printf("The file is %s.\n", __FILE__);
    printf("The date is %s.\n", __DATE__);
    printf("The time is %s.\n", __TIME__);
    printf("The version is %ld.\n", __STDC_VERSION__);
    printf("This is line %d.\n", __LINE__);
    printf("This function is %s.\n", __func__);
    why_me();
    
    return 0;
}

void why_me(void)
{
    printf("This function is %s\n", __func__);
    printf("This is line %d.\n", __LINE__);
}

输出结果:

  • 程序清单16.13,mytype.c:

// mytype.c
#include <stdio.h>

#define MYTYPE(X) _Generic((X),\
    int: "int",\
    float: "float",\
    double: "double",\
    default: "other"\
)

int main(void)
{
    int d = 5;
    
    printf("%s\n", MYTYPE(d)); // d 是 int 类型
    printf("%s\n", MYTYPE(2.0 * d)); // 2.0 * d 是 double 类型
    printf("%s\n", MYTYPE(3L)); // 3L 是 long 类型
    printf("%s\n", MYTYPE(&d)); // &d 是 in * 类型
    
    return 0; 
}

输出结果:

  • 程序清单16.14,rect_pol.c:

/* rect_pol.c -- 把直角坐标转换为极坐标 */
#include <stdio.h>
#include <math.h>
#define RAD_TO_DEG (180 / (4 * atan(1)))

typedef struct polar_v {
    double magnitude;
    double angle;
} Polar_V;

typedef struct rect_v {
    double x;
    double y;
} Rect_V;

Polar_V rect_to_polar(Rect_V);

int main(void)
{
    Rect_V input;
    Polar_V result;
    
    puts("Enter x and y coordinates; enter q to quit:");
    while (scanf("%lf %lf", &input.x, &input.y) == 2)
    {
        result = rect_to_polar(input);
        printf("magnitude = %0.2f, angle = %0.2f\n",
                result.magnitude, result.angle);
    }
    puts("Bye.");
    
    return 0;
}

Polar_V rect_to_polar(Rect_V rv)
{
    Polar_V pv;
    
    pv.magnitude = sqrt(rv.x * rv.x + rv.y * rv.y);
    if (pv.magnitude == 0)
        pv.angle = 0.0;
    else
        pv.angle = RAD_TO_DEG * atan2(rv.y, rv.x);
        
    return pv;
}

输出结果:

// generic.c -- 定义泛型宏

#include <stdio.h>
#include <math.h>
#define RAD_TO_DEG (180 / (4 * atanl(1)))

// 泛型平方根函数
#define SQRT(X) _Generic((X), \
    long double: sqrtl, \
    default: sqrt, \
    float: sqrtf) (X)

// 泛型正弦函数,角度的单位为度
#define SIN(X) _Generic((X), \
    long double: sinl((X) / RAD_TO_DEG), \
    default: sin((X) / RAD_TO_DEG), \
    float: sinf((X) / RAD_TO_DEG) \
    )

int main(void)
{
    float x = 45.0f;
    double xx = 45.0;
    long double xxx = 45.0L;
    
    long double y = SQRT(x);
    long double yy = SQRT(xx);
    long double yyy = SQRT(xxx);
    __mingw_printf("%.17Lf\n", y); // 匹配 float
    __mingw_printf("%.17Lf\n", yy); // 匹配 default
    __mingw_printf("%.17Lf\n", yyy); // 匹配 long double
    int i = 45;
    yy = SQRT(i); // 匹配default
    __mingw_printf("%.17Lf\n", yy);
    yyy = SIN(xxx); // 匹配 long double
    __mingw_printf("%.17Lf\n", yyy);
    
    return 0; 
}

输出结果:

  • 程序清单16.16,byebye.c:

/* 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); /* 注册 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.");
}

输出结果:

  • 程序清单16.17,qsorter.c:

/* 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("\nSorted 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)
{
    /* 要使用指向double 的指针来访问这两个值 */
    const double * a1 = (const double *) p1;
    const double * a2 = (const double *) p2;
    
    if (*a1 < *a2)
        return -1;
    else if (*a1 == *a2)
        return 0;
    else
        return 1; 
} 

输出结果:

  • 程序清单16.18,assert.c:

/* assert.c -- 使用 assert.c */
#include <stdio.h>
#include <math.h>
//#define NDEBUG
#include <assert.h>

int main(void)
{
    double x, y, z;
    
    puts("Enter a pari of numbers(0 0 to quit): ");
    while (scanf("%lf%lf", &x, &y) == 2 && (x != 0 || y != 0))
    {
        z = x * x - y * y; // 应该用 +
        assert(z >= 0);
        printf("answer is %f\n", sqrt(z));
        puts("Next pair of numbers: ");
    }
    puts("Done");
    
    return 0;
} 

输出结果:

  • 程序清单16.19,statasrt.c:

// statasrt.c
#include <stdio.h>
#include <limits.h>

_Static_assert(CHAR_BIT == 16, "16-bit char falsely assumed");

int main(void)
{
    puts("char is 16 bits.");
    
    return 0;
}

输出结果:

  • 程序清单16.20,mems.c:

// mems.c -- 使用 memcpy() 和 memmove()
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SIZE 10

void show_array(const int ar [], int n);
// 如果编辑器不支持C11的_Static_assert,可以注释掉下面这行
_Static_assert(sizeof(double) == 2 * sizeof(int), "double not twice int size");
int main(void)
{
    int values[SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int target[SIZE];
    
    double curious[SIZE / 2] = { 2.0, 2.0e5, 2.0e10, 2.0e20, 5.0e30 };
    
    puts("memcpy() used:");
    puts("values (original data:) ");
    show_array(values, SIZE);
    memcpy(target, values, SIZE * sizeof(int));
    puts("target (copy of values):");
    show_array(target, SIZE);
    
    puts("\nUsing memmove() with overlapping ranges:");
    memmove(values + 2, values, 5 * sizeof(int));
    puts("values -- elements 0-4 copied to 2-6:");
    show_array(values, SIZE);
    
    puts("\nUsing memcpy() to copy double to int:");
    memcpy(target, curious, (SIZE / 2) * sizeof(double));
    puts("target -- 5 doubles into 10 int positions:");
    show_array(target, SIZE / 2);
    show_array(target + 5, SIZE / 2);
    
    return 0;
}

void show_array(const int ar [], int n)
{
    int i;
    
    for (i = 0; i < n; i++)
        printf("%d ", ar[i]);
    
    putchar('\n');
}

输出结果:

  • 代码清单16.21,varargs.c:

// varargs.c -- use variable number of arguments
#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, ...)
{
    va_list ap; // 声明一个对象存储参数
    double tot = 0;
    int i;
    
    va_start(ap, lim); // 把ap初始化为参数列表
    for (i = 0; i < lim; i++)
        tot += va_arg(ap, double); // 访问参数列表中的每一项
    va_end(ap);
    
    return tot; 
}

输出结果:

  • 编程练习:

题目1

方法:定义所需要的预处理信息并命名为 .h 格式的文件。示例代码info.h:

#ifndef INFO_H_
#define INFO_H_

#define NUM 20
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))

#endif

题目2

方法:用宏函数计算调和平均数。示例代码,16_2.c:

#include <stdio.h>

#define HMEN(X, Y) (2.0 * (X) * (Y) / ((X) + (Y)))

int main(void)
{
    double a = 0.5, b = 0.25;
    printf("The harmonic mean of a and b is: %g\n", HMEN(a, b));
    
    return 0;
}

输出结果:

题目3

方法:注意如果返回结构的指针则需要传入直角坐标的地址,否则如果在函数内部声明直角坐标的结构并返回其地址则返回的是局部变量的地址,而局部变量的生命周期仅仅在函数执行期间,可能会导致不可控的错误。示例代码,16_3.c:

// polar_to_rect.c -- 极坐标转换为直角坐标
#include <stdio.h>
#include <math.h>
#define PI 3.1415926

typedef struct polar {
    double length;
    double angle;
} Polar;

typedef struct rect {
    double x;
    double y;
} Rect;

Rect polar_to_rect(const Polar *);

int main(void)
{
    Polar test = { 10.0, 30 };
    Rect pr = polar_to_rect(&test);
    
    printf("x = %g\n", pr.x);
    printf("y = %g\n", pr.y);
    
    return 0;
}

Rect polar_to_rect(const Polar * pp)
{
    Rect temp;
    static const double rad = PI / 180.0;
    double ang = rad * pp->angle;
    
    temp.x = pp->length * cos(ang);
    temp.y = pp->length * sin(ang);
    
    return temp;
}

输出结果:

题目4

方法:两个clock()返回值之差为这段时间类经过的处理器时间单位的数量,除以单位时间(second)处理器时间单位的数量,就可以得到该段时间内经过的时间(second)。示例代码16_4.c:

// delay.c -- 在指定时间类进行循环
#include <stdio.h>
#include <time.h>

void delay(const double);

int main(void)
{
    double test = 4.0;
    
    delay(test);
    
    return 0;
}

void delay(const double n)
{
    clock_t begin = clock();
    clock_t end = clock();
    
    while ((double) (end - begin) / CLOCKS_PER_SEC < n)
        end = clock();
    printf("Delayed for %g seconds.\n", 
            (double) (end - begin) / CLOCKS_PER_SEC);
}

输出结果:

题目5

方法:通过标记已选择过的数字来达到从剩余数字中选取其他数字的目的。示例代码16_5.c:

/* random_pick.c -- 随机从指定整形序列中选取指定个数的数字  */
#include <stdio.h>
#include <time.h> //提供 time() 函数 
#include <stdlib.h> // 提供 srand()、rand() 函数 
#include <string.h> //提供 memcpy() 函数 
#define LEN 20

void random_pick(const int [], int, int);

int main(void)
{
    int test[LEN];
    int i;
    
    for (i = 0; i < LEN; i++)
        test[i] = i + 1;
    random_pick(test, LEN, 5);
    
    return 0; 
}

void random_pick(const int ar[], int length, int picks)
{
    int br[LEN];
    int i;
    
    memcpy(br, ar, length * sizeof(int)); // 备份数组ar
    srand((unsigned int) time(0));
    printf("Here are %d numbers picked.\n", picks);
    while (picks > 0)
    {
        i = rand() % length; // 选取范围为 0 - (length - 1) 
        if (0 == br[i]) // 如果被选取过则跳过 
            continue;
        else
        {
            printf("%d is picked.\n", br[i]);
            br[i] = 0; // 标记第 i + 1 个数已被选取 
            picks--; // 待选取数字数量减 1 
        }
    }
}

输出结果:

题目6

方法:先比较名,再比较姓。示例代码,16_6.c:

#include <stdio.h>
#include <stdlib.h> // 提供 qsort() 函数 
#include <string.h> // 提供 strcmp() 函数 
#define LEN 40
#define SLEN 5

typedef struct names {
    char first[LEN];
    char last[LEN];
} Names;

int comp(const void * p1, const void * p2);
void show_names(const Names * begin, int n);

int main(void)
{
    struct names staff[SLEN] = {
        { "Francy", "card" },
        { "Coffee", "cancy" },
        { "Stephen", "lory" },
        { "Jack", "rosery" },
        { "Black", "clover" }
    };
    puts("Random list:");
    show_names(staff, SLEN);
    qsort(staff, SLEN, sizeof(struct names), comp);
    puts("\nSorted list:");
    show_names(staff, SLEN);

    return 0;
}

int comp(const void * p1, const void * p2)
{
    const struct names * ps1 = (const Names *) p1;
    const struct names * ps2 = (const Names *) p2;

    if (strcmp(ps1->first, ps2->first) < 0)
        return -1;
    else if (strcmp(ps1->first, ps2->first) == 0)
        return 0;
    else if (strcmp(ps1->first, ps2->first) > 0)
        return 1;
    else
        return strcmp(ps1->last, ps2->last);
}

void show_names(const Names * begin, int n)
{
    const Names * end = begin + n;

    while (begin < end)
    {
        printf("%s %s\n", begin->first, begin->last);
        ++begin;
    }
}

输出结果:

题目7

方法:将传入的变参保存在malloc() 函数申请的内存空间中。示例代码16_7.c:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

void show_array(const double ar[], int n);
double * new_d_array(int n, ...);

int main()
{
    double * p1;
    double * p2;

    p1 = new_d_array(5, 1.2, 2.3, 3.4, 4.5, 5.6);
    p2 = new_d_array(4, 100.0, 20.00, 8.08, -1890.0);
    show_array(p1, 5);
    show_array(p2, 4);
    free(p1);
    free(p2);

    return 0;
}

void show_array(const double ar[], int n)
{
    int i;

    printf("%d elements:\n", n);
    for (i = 0; i < n; i++)
        printf("%-6g", ar[i]);
    putchar('\n');
}

double * new_d_array(int n, ...)
{
    int i;
    va_list ap;
    double * pt;

    va_start(ap, n);
    pt = (double *) malloc(n * sizeof(double));
    for (i = 0; i < n; i++)
        pt[i] = va_arg(ap, double);
    va_end(ap);
    
    return pt;
}

输出结果:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值