16.17 复习题
1. 下面的几组代码由一个或多个宏组成,其后是使用宏的源代码。在每种情况下代码的结果是什么?这些代码是否是有效代码?(假设其中的变量已声明)。
a.
#define FPM 5280
dist = FPM * miles;
b.
#define FEET 4
#define POD FEET + FEET
plort = FEET * POD;
c.
#define SIX = 6;
new = SIX;
d.
#define NEW(X) X + 5
y = NEW(y);
berg = NEW(berg) * lob;
est = NEW(berg) / NEW(y);
nilp = lob * NEW(-berg);
答案:
a. dist = 5280 * miles; 有效。
b.plort = 4 * 4 + 4;有效。但是如果用户需要的是4 * (4 + 4),应使用#define POD (FEET + FEET)
c. new == 6;;无效(如果两个等号之间没有空格,则有效,但是没有意义)。
d. y = y + 5;有效。 berg = berg + 5 * lob;有效。est = berg + 5/ y + 5;有效。nilp = lob * -berg + 5;有效。
2. 修改复习题1中部分的定义,使其更可靠。
答案:
#define NEW(X) ((X) + 5)
3. 定义一个宏函数,返回两值中的较小值。
#include <stdio.h>
#define MIN(x, y) ((x < y) ? (x) : (y))
int main(void)
{int x = 5, y = 3;printf("min(%d,%d) is %d ", x, y , MIN(x, y));return 0;
}
4. 定义宏函数EVEN_GT(X, Y)宏,如果X为偶数且大于Y,该宏返回1。
#include <stdio.h>
#define EVEN_GT(X, Y) (((X) % 2 == 0) && ((X) > (Y))) ? 1 : 0
int main(void)
{int x = 6, y = 3;printf("EVEN_GT(%d,%d) is %d ", x, y, EVEN_GT(x, y));return 0;
}
5. 定义一个宏函数,打印两个表达式及其值。例如,若参数为3+4和4*12,则打印:
3+4 is 7 and 4*12 is 48
#include <stdio.h>
#define PR(X, Y) printf(#X " is %d and " #Y " is %d\n", X ,Y);
int main(void)
{PR(3+4, 4*12);return 0;
}
6. 创建#define指令完成下面的任务:
a. 创建一个值为25的命名常量。
b. SPACE表示空格字符。
c. PS()代表打印空格字母。
d. BIG(X)代表X的值加3。
e. SUMSQ(X, Y)代表X和Y的平方和。
#define NUM 25
#define SPACE ' '
#define PS() putchar(' ');
#define BIG(X) ((X) + 3)
#define SUMSQ(X, Y) ((X)*(X) + (Y)*(Y))
7. 定义一个宏,以下面的格式打印名称、值和int类型变量的地址:
name: fop; value: 23; address: ff464016
#include <stdio.h>
#define PR(X) printf("name: " #X "; value: %d; address %p\n", X, &X);
int main(void)
{int fop = 23;PR(fop);return 0;
}
8. 假设在测试程序时要暂时跳过一块代码,如何在不移除这块代码的前提下完成这项任务?
答案:
使用条件编译指令。一种方法是使用#ifndef:
#define _SKIP_ /*如果不需要跳过代码,则删除这条指令*/
#ifndef _SKIP_
/*需要跳过的代码*/
#endif
9. 编写一段代码,如果定义了PR_DATE宏,则打印预处理的日期。
#include <stdio.h>
#define PRDATE
int main(void)
{
#ifdef PRDATEprintf("Date is %s\n", __DATE__);
#endif // PRDATEreturn 0;
}
10. 内联函数部分讨论了3种不同版本的square()函数。从行为方面看,这3种版本的函数有何不同?
答案:
第1个版本返回x*x,这只是返回了square()的double类型值。例如square(1.3)会返回1.69。
第2个版本返回(int)(x*x),计算结果被截断后返回。但是,由于该函数的返回类型是double,所以1.69先被转换成1,然后被转换成1.00。
第3个版本返回(int)(x*x +0.5),可以让函数把结果四舍五入,而不是简单的截断。1.69+0.5的2.19,然后被截断为2,然后被转换成2.00。
11. 创建一个使用泛型选择表达式的宏,如果宏参数是_Bool类型,对"boolean"求值,否则对"not boolean“求值。
答案:
#define BOOL(X) _Generic((X), _Bool:"boolean", default: "not boolean")
12. 下面的程序有什么错误?
答案:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{printf("The square root of %f is %f\n", argv[1], sqrt(atof(argv[1])));
}
argv参数声明改为char *argv[]类型。
命令行参数被储存为字符串,所以应把argv[1]转换成double类型,使用stdlib.h库中的atof()函数。
程序使用了sqrt函数,应包含math.h头文件。
13. 假设scores是内含1000个int类型的数组,要按降序排序该数组中的值。假设你使用qsort()和comp()比较函数。
a. 如何正确调用qsort()?
b. 如何正确定义comp()?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM 1000
void fillarray(int ar[], int n);
void showarray(const int ar[], int n);
int mycomp(const void* p1, const void* p2);
int main(void)
{int scores[NUM];fillarray(scores, NUM);puts("Random list:");showarray(scores, NUM);qsort(scores, NUM, sizeof(int), mycomp);puts("\nSorted list:");showarray(scores, NUM);return 0;
}
void fillarray(int ar[], int n)
{srand((unsigned int) time(NULL));int index;for (index = 0; index < n; index++)ar[index] = (int)rand() ;
}
void showarray(const int ar[], int n)
{int index;for (index = 0; index < n; index++){printf("%9.4d ", ar[index]);if (index % 10 == 9)putchar('\n');}if (index % 9 != 0)putchar('\n');
}
int mycomp(const void* p1, const void* p2)
{const int* a1 = (const int*)p1;const int* a2 = (const int*)p2;if (*a1 < *a2)return -1;else if (*a1 == *a2)return 0;elsereturn 1;
}
14. 假设data1是内含100个double类型元素的数组,data2是内含300个double类型元素的数组。
a. 编写memcpy()的函数调用,把data2中的前100个元素拷贝到data1中。
b. 编写memcpy()的函数调用,把data2中的后100个元素拷贝到data1中。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SIZE 100
#define THIRDSIZE 300
void set_array(double* data, int num);
void show_array(const double ar[], int n);
int main(void)
{double data1[SIZE] = {0}, data2[THIRDSIZE];set_array(data2, THIRDSIZE);puts("values (original data):");show_array(data1, SIZE);memcpy(data1, data2, SIZE * sizeof(double));puts("Using memcpy() (0 - 100):");show_array(data1, SIZE);puts("\nUsing memcpy() (200 - 300):");memcpy(data1, data2 + 200 , SIZE * sizeof(double));show_array(data1, SIZE);return 0;
}
void set_array(double *data, int num)
{for (int i = 0; i < num; i++){data[i] = i;}
}
void show_array(const double ar[], int n)
{int i;for (i = 0; i < n; i++){printf("%g ", ar[i]);if (i % 10 == 9)putchar('\n');}putchar('\n');
}
16.18 编程练习
1. 开发一个包含你需要的预处理器定义的头文件。
#ifndef NAMES_H_
#define NAMES_H_
#define LENGTH 1000
struct names_st
{char first[LENGTH];char last[LENGTH];
};
typedef struct names_st names;
char* s_gets(char* st, int n);
#endif // !NAMES_H_
2. 两数的调和平均数这样计算:先得到两数的倒数,然后计算两个倒数的平均值,最后取计算结果的倒数。使用#define指令定义一个宏”函数“,执行该运算。编写一个简单的程序测试该宏。
#include <stdio.h>
#define PR(X, Y) (2.0 / ((1.0/(X))+(1.0/(Y))))
int main(void)
{double numOne, numTwo, result;printf("Please enter two double number:");scanf_s("%lf %lf", &numOne, &numTwo);result = PR(numOne, numTwo);printf("PR(%.1lf, %.1lf) is %.1lf\n", numOne, numTwo, result);return 0;
}
3. 极坐标用向量的模(即向量的长度)和向量相对x轴逆时针旋转的角度来描述该向量。直角坐标用向量的x轴和y轴的坐标来描述该向量(见图16.3)。编写一个程序,读取向量的模和角度(单位:度),然后显示x轴和y轴的坐标。有关方程如下:
x = r*cos A y = r*sin A
需要一个函数来完成转换,该函数接受一个极坐标的结构,并返回一个包含直角坐标的结构(或返回指向该结构的指针)。
#include <stdio.h>
#include <math.h>
#define PI 3.14
typedef struct polar_v {double magnitude;double angle;
} Polar_V;
typedef struct rect_v {double x;double y;
} Rect_V;
Rect_V polar_to_rect(Polar_V);
int main(void)
{Rect_V result;Polar_V input;puts("Enter magnitude and angle; enter q to quit: ");while (scanf_s("%lf %lf", &input.magnitude, &input.angle) == 2){input.angle = input.angle * (PI / 180.0);result = polar_to_rect(input);printf("x = %0.2f, y = %0.2f\n", result.x, result.y);puts("Enter magnitude and angle; enter q to quit: ");}puts("Bye.");return 0;
}
Rect_V polar_to_rect(Polar_V pv)
{Rect_V rv;rv.x = pv.magnitude * cos(pv.angle);rv.y = pv.magnitude * sin(pv.angle);return rv;
}
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>
void delay(double seconds);
int main(void)
{double seconds;printf("Enter a second to delay:");scanf_s("%lf", &seconds);delay(seconds);return 0;
}
void delay(double seconds)
{clock_t start = clock();printf("Now let's test %.1lf second delay\n", seconds);clock_t now = clock();while (((double)(now - start)) / CLOCKS_PER_SEC < seconds){now = clock();printf("You delay %.1lf second.\n", ((double)(now - start)) / CLOCKS_PER_SEC);}
}
5. 编写一个函数接受这些参数:内含int类型元素的数组名、数组的大小和一个代表选取次数的值。该函数从数组中随机选择指定数量的元素,并打印它们。每个元素只能选择一次(模拟抽奖数字或挑选陪审团成员)。另外,如果你的实现有time()(第12章讨论过)或类似的函数,可在srand()中使用这个函数的输出来初始化随机数生成器rand()。编写一个简单的程序测试该函数。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define SIZE 100
void select(int data[], int length, int n);
int main()
{int data[SIZE], number;printf("Enter number to selected:");scanf_s("%d", &number);for (int i = 0; i < SIZE; i++) {data[i] = i;}select(data, SIZE, number);return 0;
}
void select(int data[], int length, int n)
{srand((unsigned long)clock());printf("Start to select number.\n");int* marks = (int*)malloc(length * sizeof(int));if (marks == NULL){printf("memory allocation error!\n");return;}for (int i = 0; i < length; i++) {marks[i] = 0;}int index;while (n > 0){index = rand() % length;if (marks[index] != 0)continue;elsemarks[index] = 1;printf("Selected ID: %3d DATA: %3d\n", index, data[index]);n--;}
}
6. 修改程序清单16.17,使用struct names元素(在程序清大16.17后面讨论过),而不是double类型的数组。使用较少的元素,并用选定的名字显式初始化数组。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM 4
typedef struct {char first[40];char last[40];
}names;
void fillarray(names list[], int n);
void showarray(const names list[], int n);
int mycomp(const void* p1, const void* p2);
int main(void)
{names list[NUM];fillarray(list, NUM);showarray(list, NUM);qsort(list, NUM, sizeof(names), mycomp);puts("\nSorted list:");showarray(list, NUM);return 0;
}
void fillarray(names list[], int n)
{int index;for (index = 0; index < n; index++){printf("Enter the %d stuff name:", index + 1);scanf_s("%s %s", list[index].first, sizeof(list[index].first), list[index].last, sizeof(list[index].last));}
}
void showarray(const names list[], int n)
{int index;for (index = 0; index < n; index++){printf("Stuff No %d %10s.%-10s\n", index + 1, list[index].first, list[index].last);}
}
int mycomp(const void* p1, const void* p2)
{const names* a1 = (const names*)p1;const names* a2 = (const names*)p2;int res = strcmp(a1->last, a2->last);if (res != 0)return res;elsereturn strcmp(a1->first, a2->first);
}
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(void)
{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.0, 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()函数的代码。
#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(void)
{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.0, 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)
{for (int index = 0; index < n; index++)printf("%.2lf ", ar[index]);putchar('\n');
}
double* new_d_array(int n, ...)
{va_list ap;va_start(ap, n);double* pResult = (double*)malloc(n * sizeof(double));if (!pResult){fprintf(stderr, "Memory allocation failed\n");exit(EXIT_FAILURE);}for (int index = 0; index < n; index++)pResult[index] = va_arg(ap, double);va_end(ap);return pResult;
}