其他分享
首页 > 其他分享> > 《C Primer Plus》第十六章——C预处理器和C库

《C Primer Plus》第十六章——C预处理器和C库

作者:互联网

文章目录

C预处理器和C库

本章内容

本章介绍以下内容:

翻译程序的第一步

明示常量:#define

/* preproc.c -- 简单的预处理示例 */
#include <stdio.h>
#define TWO 2  /* 可以使用注释 */
#define OW "Consistency is the last refuge of the 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;
}

记号

重定义常量

在#define中使用参数

/* 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;
}

x = 5

Evaluating SQUARE(x): The result is 25.

Evaluating SQUARE(2): The result is 4.

Evaluating SQUARE(x+2): The result is 17.

Evaluating 100/SQUARE(2): The result is 100.

x is 5.

Evaluating SQUARE(++x): The result is 42.

After incrementing, x is 7.

用宏参数创建字符串:#运算符

/* 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;
}
该程序的输出如下:
The square of y is 25.
The square of 2 + 4 is 36.

预处理器黏合剂:##运算符

// 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;
}
该程序的输出如下:
x1 = 14
x2 = 20
x3 = 30

变参宏:…和_ _VA_ARGS_ _

// 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;
}
第1个宏调用,X的值是1,所以#X变成"1"。展开后成为:
printf("Message " "1" ": " "x = %g\n", x);

宏和函数的选择

文件包含:#include

头文件示例

// 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.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 -- 使用 names_st 结构
#include <stdio.h>
#include "names_st.h"
// 记住要链接 names_st.c
int main(void)
{
  names candidate;
  get_names(&candidate);
  printf("Let's welcome ");
  show_names(&candidate);
  printf(" to this program!\n");
  return 0;
}

使用头文件

其他指令

#undef指令

从C预处理器角度看已定义

#define LIMIT 1000     // LIMIT是已定义的
#define GOOD       // GOOD 是已定义的
#define A(X) ((-(X))*(X)) // A 是已定义的
int q;         // q 不是宏,因此是未定义的
#undef GOOD       // GOOD 取消定义,是未定义的

条件编译

#ifdef MAVIS
#include "horse.h" // 如果已经用#define定义了 MAVIS,则执行下面的指令
#define STABLES 5
#else
#include "cow.h" //如果没有用#define定义 MAVIS,则执行下面的指令
#define STABLES 15
#endif
/* 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;
}
编译并运行该程序后,输出如下:
i=1, running total = 3
i=2, running total = 12
i=3, running total = 31
i=4, running total = 64
Grand total = 64
// names.h --修订后的 names_st 头文件,避免重复包含
#ifndef NAMES_H_
#define NAMES_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);
#endif
#if SYS == 1
#include "ibm.h"
#endif
// 可以按照if else的形式使用#elif(早期的实现不支持#elif)。例如,可以这样写:
#if SYS == 1
  #include "ibmpc.h"
#elif SYS == 2
  #include "vax.h"
#elif SYS == 3
  #include "mac.h"
#else
  #include "general.h"
#endif
#if defined (IBMPC)
#include "ibmpc.h"
#elif defined (VAX)
#include "vax.h"
#elif defined (MAC)
#include "mac.h"
#else
#include "general.h"
#endif

预定义宏

在这里插入图片描述

// predef.c -- 预定义宏和预定义标识符
#include <stdio.h>
void why_me();
int main()
{
  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()
{
  printf("This function is %s\n", _ _func_ _);
  printf("This is line %d.\n", _ _LINE_ _);
}
下面是该程序的输出:
The file is predef.c.
The date is Sep 23 2013.
The time is 22:01:09.
The version is 201112.
This is line 11.
This function is main
This function is why_me
This is line 21.

#line和#error

#line指令重置_ _LINE_ _和_ _FILE_ _宏报告的行号和文件名。可以这样使用#line:

#line 1000 // 把当前行号重置为1000

#line 10 “cool.c” // 把行号重置为10,把文件名重置为cool.c

#pragma

泛型选择(C11)

// 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 的类型是 int *
  return 0;
}
下面是该程序的输出:
int
double
other
other

内联函数(C99)

#include <stdio.h>
inline static void eatline() // 内联函数定义/原型
{
  while (getchar() != '\n')
   continue;
}
int main()
{
  ...
  eatline();  // 函数调用
  ...
}
// eatline.h
#ifndef EATLINE_H_
#define EATLINE_H_
inline static void eatline()
{
  while (getchar() != '\n')
   continue;
}
#endif
//file1.c
...
inline static double square(double);
double square(double x) { return x * x; }
int main()
{
  double q = square(1.3);
  ...
//file2.c
...
double square(double x) { return (int) (x*x); }
void spam(double v)
{
  double kv = square(v);
  ...
//file3.c
...
inline double square(double x) { return (int) (x * x + 0.5); }
void masp(double w)
{
  double kw = square(w);
  ...

_Noreturn函数(C11)

C库

访问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;
}
下面是运行该程序后的一个输出示例:
Enter x and y coordinates; enter q to quit:10 10
magnitude = 14.14, angle = 45.00-12 -5
magnitude = 13.00, angle = -157.38
q
Bye.

类型变体

// 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);
  printf("%.17Lf\n", y); // 匹配 float
  printf("%.17Lf\n", yy); // 匹配 default
  printf("%.17Lf\n", yyy); // 匹配 long double
  int i = 45;
  yy = SQRT(i);     // 匹配 default
  printf("%.17Lf\n", yy);
  yyy = SIN(xxx);     // 匹配 long double
  printf("%.17Lf\n", yyy);
  return 0;
}
下面是该程序的输出:
6.70820379257202148
6.70820393249936942
6.70820393249936909
6.70820393249936942
0.70710678118654752

tgmath.h库(C99)

// 如果包含了tgmath.h,要调用sqrt()函数而不是sqrt()宏,可以用圆括号把被调用的函数名括起来:
#include <tgmath.h>
...
  float x = 44.0;
  double y;
  y = sqrt(x); // 调用宏,所以是 sqrtf(x)
  y = (sqrt)(x); // 调用函数 sqrt()

通用工具库

exit()和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); /* 注册 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.");
}
下面是该程序的一个运行示例:
Enter an integer:212
212 is even.
Thus terminates another magnificent program from
SeeSaw Software!

qsort()函数

/* 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;
}
下面是该程序的运行示例:
Random list:
0.0001  1.6475 2.4332 0.0693 0.7268 0.7383
24.0357 0.1009 87.1828 5.7361 0.6079 0.6330
1.6058  0.1406 0.5933 1.1943 5.5295 2.2426
0.8364  2.7127 0.2514 0.9593 8.9635 0.7139
0.6249  1.6044 0.8649 2.1577 0.5420 15.0123
1.7931  1.6183 1.9973 2.9333 12.8512 1.3034
0.3032  1.1406 18.7880 0.9887
Sorted list:
0.0001  0.0693 0.1009 0.1406 0.2514 0.3032
0.5420  0.5933 0.6079 0.6249 0.6330 0.7139
0.7268  0.7383 0.8364 0.8649 0.9593 0.9887
1.1406  1.1943 1.3034 1.6044 1.6058 1.6183
1.6475  1.7931 1.9973 2.1577 2.2426 2.4332
2.7127  2.9333 5.5295 5.7361 8.9635 12.8512
15.0123 18.7880 24.0357 87.1828

断言库

assert的用法

/* assert.c -- 使用 assert() */
#include <stdio.h>
#include <math.h>
#include <assert.h>
int main()
{
  double x, y, z;
  puts("Enter a pair 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;
}
下面是该程序的运行示例:
Enter a pair of numbers (0 0 to quit):4 3
answer is 2.645751
Next pair of numbers:5 3
answer is 4.000000
Next pair of numbers:3 5
Assertion failed: (z >= 0), function main, file /Users/assert.c, line 14.

_Static_assert(C11)

// 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;
}
下面是在命令行编译的示例:
$ clang statasrt.c
statasrt.c:4:1: error: static_assert failed "16-bit char falsely assumed"
_Static_assert(CHAR_BIT == 16, "16-bit char falsely assumed");
^     ~~~~~~~~~~~~~~
1 error generated.
$

string.h库中的memcpy()和memmove()

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

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

// 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()
{
  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');
}
下面是该程序的输出:
memcpy() used:
values (original data):
1 2 3 4 5 6 7 8 9 10
target (copy of values):
1 2 3 4 5 6 7 8 9 10
Using memmove() with overlapping ranges:
values -- elements 0-4 copied to 2-6:
1 2 1 2 3 4 5 8 9 10
Using memcpy() to copy double to int:
target -- 5 doubles into 10 int positions:
0 1073741824 0 1091070464 536870912
1108516959 2025163840 1143320349 -2012696540 1179618799

可变参数:stdarg.h

va_copy()。该宏接受两个va_list类型的变量作为参数,它把第2个参数拷贝给第1个参数:
va_list ap;
va_list apcopy;
double tic;
int toc;
...
va_start(ap, lim);    // 把ap初始化为一个参数列表
va_copy(apcopy, ap);   // 把apcopy作为ap的副本
tic = va_arg(ap, double); // 检索第1个参数
toc = va_arg(ap, int);  // 检索第2个参数
//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;
}
下面是该程序的输出:
return value for sum(3, 1.1, 2.5, 13.3):       16.9
return value for sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1): 31.6

复习题

  1. 下面的几组代码由一个或多个宏组成,其后是使用宏的源代码。在每种情况下代码的结果是什么?这些代码是否是有效代码?(假设其中的变量已声明)
    a.
    #define FPM 5280 /每英里的英尺数/
    dist = FPM * miles;
    b.
    #define FEET 4
    #define POD FEET + FEET
    plort = FEET * POD;
    c.
    #define SIX = 6;
    nex = SIX;
    d.
    #define NEW(X) X + 5
    y = NEW(y);
    berg = NEW(berg) * lob;
    est = NEW(berg) / NEW(y);
    nilp = lob * NEW(-berg);
  2. 修改复习题1中d部分的定义,使其更可靠。
  3. 定义一个宏函数,返回两值中的较小值。
  4. 定义EVEN_GT(X, Y)宏,如果X为偶数且大于Y,该宏返回1。
  5. 定义一个宏函数,打印两个表达式及其值。例如,若参数为3+4和4*12,则打印:
    3+4 is 7 and 4*12 is 48
  6. 创建#define指令完成下面的任务。
    a.创建一个值为25的命名常量。
    b.SPACE表示空格字符。
    c.PS()代表打印空格字符。
    d.BIG(X)代表X的值加3。
    e.SUMSQ(X, Y)代表X和Y的平方和。
  7. 定义一个宏,以下面的格式打印名称、值和int类型变量的地址:
    name: fop; value: 23; address: ff464016
  8. 假设在测试程序时要暂时跳过一块代码,如何在不移除这块代码的前提下完成这项任务?
  9. 编写一段代码,如果定义了PR_DATE宏,则打印预处理的日期。
  10. 内联函数部分讨论了3种不同版本的square()函数。从行为方面看,这3种版本的函数有何不同?
  11. 创建一个使用泛型选择表达式的宏,如果宏参数是_Bool类型,对"boolean"求值,否则对"not boolean"求值。
  12. 下面的程序有什么错误?
    #include <stdio.h>
    int main(int argc, char argv[])
    {
    printf(“The square root of %f is %f\n”, argv[1],sqrt(argv[1]) );
    }
  13. 假设scores是内含1000个int类型元素的数组,要按降序排序该数组中的值。假设你使用qsort()和comp()比较函数。
    a.如何正确调用qsort()?
    b.如何正确定义comp()?
  14. 假设data1是内含100个double类型元素的数组,data2是内含300个double类型元素的数组。
    a.编写memcpy()的函数调用,把data2中的前100个元素拷贝到data1中。
    b.编写memcpy()的函数调用,把data2中的后100个元素拷贝到data1中。

  1. a.dist = 5280 * miles;有效。
    b.plort = 4 * 4+4;有效。但是如果用户需要的是4 * (4+4),则应该使用#define POD (FEET+FEET)。
    c.nex = = 6;;无效(如果两个等号之间没有空格,则有效,但是没有意义)。显然,用户忘记了在编写预处理器代码时不用加=。
    d.y = y+5;有效。berg = berg+5 * lob;有效,但是可能得不到想要的结果。est = berg+5/y+5;有效,但是可能得不到想要的结果。

  2. #define NEW(X) ((X) + 5)

  3. #define MIN(X,Y) ( (X) < (Y) ? (X) : (Y) )

  4. #define EVEN_GT(X,Y) ( (X) > (Y) && (X) % 2 == 0 ? 1 : 0 )

  5. #define PR(X,Y) printf(#X " is %d and " #Y " is %d\n", X,Y)
    (因为该宏中没有运算符(如,乘法)作用于X和Y,所以不需要使用圆括号。)

  6. a.#define QUARTERCENTURY 25
    b.#define SPACE ’ ’
    c.#define PS() putchar(’ ')或#define PS() putchar(SPACE)
    d.#define BIG(X) ((X) + 3)
    e.#define SUMSQ(X,Y) ((X)(X) + (Y)(Y))

  7. 试试这样:#define P(X) printf(“name: “#X”; value: %d; address: %p\n”, X, &X)
    (如果你的实现无法识别地址专用的%p转换说明,可以用%u或%lu代替。)

  8. 使用条件编译指令。一种方法是使用#ifndef:
    #define _SKIP_ /* 如果不需要跳过代码,则删除这条指令 */

    #ifndef _SKIP_
    /* 需要跳过的代码 */
    #endif*

  9. #ifdef PR_DATE
    printf(“Date = %s\n”, _ DATE _);
    #endif

  10. 第1个版本返回x*x,这只是返回了square()的double类型值。例如,square(1.3)会返回1.69。第2个版本返回(int)(x*x),计算结果被截断后返回。但是,由于该函数的返回类型是double,int类型的值将被升级为double类型的值,所以1.69将先被转换成1,然后被转换成1.00。第3个版本返回(int)(x*x+0.5)。加上0.5可以让函数把结果四舍五入至与原值最接近的值,而不是简单地截断。所以,1.69+0.5得2.19,然后被截断为2,然后被转换成2.00;而1.44+0.5得1.94,被截断为1,然后被转换成1.00。

  11. 这是一种方案: #define BOOL(X) _Generic((X), _Bool : “boolean”, default : “not boolean”)

  12. 应该把argv参数声明为char *argv[]类型。命令行参数被存储为字符串,所以该程序应该先把argv[1]中的字符串转换成double类型的值。例如,用stdlib.h库中的atof()函数。程序中使用了sqrt()函数,所以应包含math.h头文件。程序在求平方根之前应排除参数为负的情况(检查参数是否大于或等于0)。

  13. a.qsort( (void )scores, (size_t) 1000, sizeof (double), comp);
    b.下面是一个比较使用的比较函数:
    int comp(const void * p1, const void * p2)
    {
    /
    要用指向int的指针来访问值 */
    /* 在C中是否进行强制类型转换都可以,在C++中必须进行强制类型转换 */
    const int * a1 = (const int *) p1; const int * a2 = (const int *)
    p2;
    if (*a1 > *a2)
    return -1;
    else if (*a1 == *a2)
    return 0;
    else
    return 1;
    }

  14. a.函数调用应该类似:memcpy(data1, data2, 100 * sizeof(double));
    b.函数调用应该类似:memcpy(data1, data2+200 , 100 * sizeof(double));

编程练习

  1. 开发一个包含你需要的预处理器定义的头文件。
  2. 两数的调和平均数这样计算:先得到两数的倒数,然后计算两个倒数的平均值,最后取计算结果的倒数。使用#define指令定义一个宏“函数”,执行该运算。编写一个简单的程序测试该宏。
  3. 极坐标用向量的模(即向量的长度)和向量相对x轴逆时针旋转的角度来描述该向量。直角坐标用向量的x轴和y轴的坐标来描述该向量(见图16.3)。编写一个程序,读取向量的模和角度(单位:度),然后显示x轴和y轴的坐标。相关方程如下:
    x = r*cos A y = r*sin A
    需要一个函数来完成转换,该函数接受一个包含极坐标的结构,并返回一个包含直角坐标的结构(或返回指向该结构的指针)。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3E7TRiiP-1617684629543)(file:///C:\Users\Administrator\Documents\Tencent Files\2429894028\Image\C2C\06FBD1F671345D80D978CD71207C587A.png)]
    图16.3 直角坐标和极坐标
  4. ANSI库这样描述clock()函数的特性:
    #include <time.h>
    clock_t clock (void);
    这里,clock_t是定义在time.h中的类型。该函数返回处理器时间,其单位取决于实现(如果处理器时间不可用或无法表示,该函数将返回-1)。然而,CLOCKS_PER_SEC(也定义在time.h中)是每秒处理器时间单位的数量。因此,两个clock()返回值的差值除以CLOCKS_PER_SEC得到两次调用之间经过的秒数。在进行除法运算之前,把值的类型强制转换成double类型,可以将时间精确到小数点以后。编写一个函数,接受一个double类型的参数表示时间延迟数,然后在这段时间运行一个循环。编写一个简单的程序测试该函数。
  5. 编写一个函数接受这些参数:内含int类型元素的数组名、数组的大小和一个代表选取次数的值。该函数从数组中随机选择指定数量的元素,并打印它们。每个元素只能选择一次(模拟抽奖数字或挑选陪审团成员)。另外,如果你的实现有time()(第12章讨论过)或类似的函数,可在srand()中使用这个函数的输出来初始化随机数生成器rand()。编写一个简单的程序测试该函数。
  6. 修改程序清单16.17,使用struct names元素(在程序清单16.17后面的讨论中定义过),而不是double类型的数组。使用较少的元素,并用选定的名字显式初始化数组。
  7. 下面是使用变参函数的一个程序段:
    #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;
    }
    new_d_array()函数接受一个int类型的参数和double类型的参数。该函数返回一个指针,指向由malloc()分配的内存块。int类型的参数指定了动态数组中的元素个数,double类型的值用于初始化元素(第1个值赋给第1个元素,以此类推)。编写show_array()和new_d_array()函数的代码,完成这个程序。

  1. /*
     * @Description: 
     * @version: 
     * @Author: 
     * @Date: 2021-04-05 23:10:27
     * @LastEditors: Please set LastEditors
     * @LastEditTime: 2021-04-05 23:36:44
     */
    /* 
    TODO:2. 两数的调和平均数这样计算:先得到两数的倒数,然后计算两个倒数的平均值,最后取计算结果的倒数。使用#define指令定义一个宏“函数”,执行该运算。编写一个简单的程序测试该宏。
    */
    #include <stdio.h>
    #include <stdlib.h>
    #define AVE(X, Y) (1.0 / (((1.0 / X) + (1.0 / Y)) / 2.0))
    int main(void)
    {
        printf("%g\n", AVE(2, 1));
        system("pause");
        return 0;
    }
    
  2. /*
     * @Description: 
     * @version: 
     * @Author: 
     * @Date: 2021-04-05 23:37:50
     * @LastEditors: Please set LastEditors
     * @LastEditTime: 2021-04-06 11:53:28
     */
    /* 
    TODO:3. 极坐标用向量的模(即向量的长度)和向量相对x轴逆时针旋转的角度来描述该向量。直角坐标用向量的x轴和y轴的坐标来描述该向量(见图16.3)。编写一个程序,读取向量的模和角度(单位:度),然后显示x轴和y轴的坐标。相关方程如下:
       x = r*cos A y = r*sin A
       需要一个函数来完成转换,该函数接受一个包含极坐标的结构,并返回一个包含直角坐标的结构(或返回指向该结构的指针)。
     */
    #include <tgmath.h>
    #include <stdio.h>
    #include <stdlib.h>
    struct stru1
    {
        double r;
        double A;
    };
    struct stru2
    {
        double x;
        double y;
    };
    struct stru2 tranverse(struct stru1 a)
    {
        struct stru2 temp;
        temp.x = a.r * cos(a.A);
        temp.y = a.r * sin(a.A);
        return temp;
    }
    int main(void)
    {
        double r, a;
        scanf("%lf %lf", &r, &a);
        struct stru1 A = {.r = r, .A = a};
        struct stru2 t = tranverse(A);
        printf("%g %g\n", t.x, t.y);
        system("pause");
        return 0;
    }
    
  3. /*
     * @Description: 
     * @version: 
     * @Author: 
     * @Date: 2021-04-06 11:56:26
     * @LastEditors: Please set LastEditors
     * @LastEditTime: 2021-04-06 12:07:34
     */
    /* 
    TODO:4. ANSI库这样描述clock()函数的特性:
       #include <time.h>
    clock_t clock (void);
       这里,clock_t是定义在time.h中的类型。该函数返回处理器时间,其单位取决于实现(如果处理器时间不可用或无法表示,该函数将返回-1)。然而,CLOCKS_PER_SEC(也定义在time.h中)是每秒处理器时间单位的数量。因此,两个clock()返回值的差值除以CLOCKS_PER_SEC得到两次调用之间经过的秒数。在进行除法运算之前,把值的类型强制转换成double类型,可以将时间精确到小数点以后。编写一个函数,接受一个double类型的参数表示时间延迟数,然后在这段时间运行一个循环。编写一个简单的程序测试该函数。
    */
    #include <stdio.h>
    #include <time.h>
    #include <stdbool.h>
    #include <stdlib.h>
    void delay(double sec)
    {
        time_t start = clock();
        while (true)
        {
            time_t current = clock();
            if (((double)current - (double)start) / CLOCKS_PER_SEC >= sec)
                break;
            printf("hhh\n");
        }
    }
    int main(void)
    {
        delay(.1);
        system("pause");
        return 0;
    }
    
  4. /*
     * @Description: 
     * @version: 
     * @Author: 
     * @Date: 2021-04-06 12:07:56
     * @LastEditors: Please set LastEditors
     * @LastEditTime: 2021-04-06 12:21:37
     */
    /* 
    TODO:5. 编写一个函数接受这些参数:内含int类型元素的数组名、数组的大小和一个代表选取次数的值。该函数从数组中随机选择指定数量的元素,并打印它们。每个元素只能选择一次(模拟抽奖数字或挑选陪审团成员)。另外,如果你的实现有time()(第12章讨论过)或类似的函数,可在srand()中使用这个函数的输出来初始化随机数生成器rand()。编写一个简单的程序测试该函数。
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <stdbool.h>
    void fun(int arr[], int size, int n)
    {
        srand((unsigned int)time(0));
        // 初始化种子
        bool *flag = (bool *)malloc(sizeof(bool) * size);
        for (int i = 0; i < size; i++)
            flag[i] = false;
        for (int i = 0; i < n; i++)
        {
            int index = rand() % size;
            if (!flag[index])
            {
                printf("%d\n", arr[index]);
                flag[index] = true;
            }
            else
                i--;
        }
    }
    int main(void)
    {
        int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
        fun(arr, 10, 10);
        system("pause");
        return 0;
    }
    
  5. /*
     * @Description: 
     * @version: 
     * @Author: 
     * @Date: 2021-04-06 12:22:03
     * @LastEditors: Please set LastEditors
     * @LastEditTime: 2021-04-06 12:37:00
     */
    /* 
    TODO: 6. 修改程序清单16.17,使用struct names元素(在程序清单16.17后面的讨论中定义过),而不是double类型的数组。使用较少的元素,并用选定的名字显式初始化数组。 
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define NUM 3
    struct names
    {
        char *last;
        char *first;
    };
    void fillarray(struct names ar[], int n);
    void showarray(const struct names ar[], int n);
    int mycomp(const void *p1, const void *p2);
    int main(void)
    {
        struct names vals[NUM];
        fillarray(vals, NUM);
        puts("Random list:");
        showarray(vals, NUM);
        qsort(vals, NUM, sizeof(struct names), mycomp);
        puts("\nSorted list:");
        showarray(vals, NUM);
        system("pause");
        return 0;
    }
    void fillarray(struct names ar[], int n)
    {
        int index;
        ar[0].first = "Li";
        ar[0].last = "Ming";
        ar[1].first = "Dunol";
        ar[1].last = "Trump";
        ar[2].first = "Bie";
        ar[2].last = "Deng";
    }
    void showarray(const struct names ar[], int n)
    {
        int index;
        for (index = 0; index < n; index++)
            printf("%s %s\n", ar[index].first, ar[index].last);
    }
    /* 按从小到大的顺序排序 */
    int mycomp(const void *p1, const void *p2)
    {
        /* 要使用指向double的指针来访问这两个值 */
        const struct names *a1 = (const struct names *)p1;
        const struct names *a2 = (const struct names *)p2;
        int res;
        res = strcmp(a1->first, a2->first);
        if (res != 0)
            return res;
        else
            return strcmp(a1->last, a2->last);
    }
    
  6. /*
     * @Description: 
     * @version: 
     * @Author: 
     * @Date: 2021-04-06 12:37:23
     * @LastEditors: Please set LastEditors
     * @LastEditTime: 2021-04-06 12:44:55
     */
    /* 
    TODO:7. new_d_array()函数接受一个int类型的参数和double类型的参数。该函数返回一个指针,指向由malloc()分配的内存块。int类型的参数指定了动态数组中的元素个数,double类型的值用于初始化元素(第1个值赋给第1个元素,以此类推)。编写show_array()和new_d_array()函数的代码,完成这个程序。
    */
    #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);
        system("pause");
        return 0;
    }
    void show_array(const double ar[], int n)
    {
        for (int i = 0; i < n; i++)
            printf("%g\n", ar[i]);
    }
    double *new_d_array(int n, ...)
    {
        va_list ap;
        va_start(ap, n);
        double *res = (double *)malloc(sizeof(double) * n);
        for (int i = 0; i < n; i++)
        {
            res[i] = va_arg(ap, double);
        }
        va_end(ap);
        return res;
    }
    
    

标签:第十六章,include,函数,int,double,void,Plus,Primer,define
来源: https://blog.csdn.net/weixin_46698891/article/details/115458415