• 抬起头,继续前进吧,去把这个不完美的故事,变成你成所期望的样子
  • 登山路上我们会放弃很多东西,但这些被我们丢掉在我们登上山顶之际,都会一一回来
  • 不论开发还是逆向,数学水平的高低直接决定了”你的饭碗里有没有肉”
  • 万丈高楼平地起,勿在浮沙筑高台

《C语言程序设计:现代方法》第十四章练习

C 菜鸟 11个月前 (11-10) 7415次浏览 已收录 0个评论
[隐藏]

        本篇为菜鸟之前在学习《C 语言程序设计:现代方法》中第十四章预处理器时做的练习题,如果有错误或疑问的话,欢迎大家指正~

1、宏定义

编写宏来计算下面的值
(a) x 的立方
(b) x 除以 4 的余数
(c) 如果 x 与 y 的乘积小于 100 值为 1,否则值为 0
答:
(a) #define CUBE(x) ((x) * (x) * (x))
(b) #define REMAINDER_FORE(x) ((x) % 4)
(c) #define CHECK_100(x) ((x) < 100 ? 1 : 0)

2、功能性宏 1

编写一个宏 NELEMS(a)来计算一个一维数组 a 中元素的个数。提示:使用 sizeof 运算符
答: #define NELEMS(a) (sizeof(a) / sizeof(a[0]))

3、DOUBLE 宏

#define DOUBLE(x) 2*x
(a) DOUBLE(1+2)的值是多少?
(b) 4/DOUBLE(2)的值是多少?
(c) 改正 DOUBLE 的定义
答:
(a) 4
(b) 4
(c) #define DOUBLE(x) (2 * (x))

4、修改宏 1

(a) #define AVG(x, y) (x+y)/2
(b) #define AREA(x, y) (x)*(y)
答:
(a) #define AVG(x, y) (((x) + (y)) / 2)
(b) #define AREA(x, y) ((x) * (y))

5、修改宏 2

下面的宏有定义问题
#define ABS(a) ((a) < 0 ? -(a) : a) 举例说明为什么 ABS 不能正常工作,并提出修改方法。(可以假定 ABS 的参数没有副作用) 答: 例如在如下表达式引用 ABS 宏 ABS(-3) + 3; 经过与编译处理后变为 (-3) < 0 ? -(-3) : -3 + 3; 与预先期望不同。修改方法就是将替换列表中的所有的参数都用圆括号括起来

6、TOUPPER 宏

假定 TOUPPER 定义成下面的宏
#define TOUPPER(c) (‘a’ <= (c) && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) 假设 s 是一个字符串,i 是一个 int 型变量。给出下面每个代码段所产生的输出 (a) strcpy(s, "abcd"); i = 0; putchar(TOUPPER(s[++i])); (b) strcpy(s, "0123"); i = 0; putchar(TOUPPER(s[++i])); 答: (a) D (b) 2

7、DISP 宏

(a) 编写宏 DISP(f, x),使其扩展后调用 printf 函数来显示函数 f 的参数为 x 时的值。例如:
DISP(sqtr, 3.0);
应该扩展为
printf(“sqrt(%g) = %g\n”, 3.0, sqrt(3.0));
(b) 编写宏 DISP2(f, x, y),类似 DISP 但应用于有两个参数的函数
答:
(a) #DISP(x, y) (printf(“(x)(%g) = %g\n”), (y), sqrt((y)))
(b) #DISP(x, y, z) (printf(“(x)(%g, %g) = %g\n”), (y), (y), sqrt(3.0))

8、GENERIC_MAX 宏

#define GENERIC_MAX(type)   \
type type##_max(type x, type y) \
{                           \
    return x > y ? x : y;   \
}

(a) 写出 GENERIC_MAX(long)被预处理器扩展后的形式。
(b) 解释为什么 GENERIC_MAX 不能应用在想 unsigned long 这样的基本类型上
(c) 如何使 GENERIC_MAX 对任何基本类型都可以正常工作?提示:不要改变 GENERIC_MAX 的定义
答:
(a) long longmax(long x, long y) { return x > y ? x : y; };
(b) 因为 type 中间有空格,预处理后返回值格式为 unsigned long unsigned ,不合法。
(c) 在传入前定义一个宏,替换列表为 type,例如:#define UNSIGNED_LONG unsigned long。然后传入宏作为 GENERIC_MAX 的参数
知识点:
1、如果#x 出现在替换列表中,其中 x 是一个宏参数,其对应的实际参数也不会被扩展
2、C 标准指明,在替换列表中,位于##运算符之前和之后的宏参数在替换时不被扩展

9、行与文件名

如果需要一个宏,使它展开后包含当前行号和文件名。换言之
const char *str = LINE_FILE;
扩展后为
const char *str = “Line 10 of file foo.c”
其中 foo.c 是包含程序的文件,10 是调用 LINE_FILE 行的行号。
答:
#define LINE_FILE _LINE_FILE(__LINE__, __FILE__)
#define _LINE_FILE(x, y) __LINE_FILE(Line x of file y)
#define __LINE_FILE(x) #x

10、宏测试

假定 M 有如下定义
#define M 10
下面哪项测试会失败?
(a) #if M
(b) #ifdef M
(c) #ifndef M
(d) #if defined(M)
(e) #if !defined(M)
答:(c) (e)

知识点:对于没有定义过的标识符,#if 指令会将其当作值为 0 的宏

11、预读 1

(a) 指出下面的程序预处理后的形式。
#define N 100

void f(void);

int main()
{
    f();
#ifdef N
#undef N
#endif
    return 0;
}

void f(void)
{
#if defined(N)
    printf("N is %d\n", N);
#else
    printf("N is undefined\n");
#endif
}

(b) 这个程序的输出是什么?
答:
(a)

void f(void);

int main()
{
    f();
    return 0;
}

void f(void)
{
    printf("N is undefined\n");
}

(b) N is undefined

12、预读 2

指出下面的程序预处理后的形式。其中有几行可能会导致编译错误,请找出这些错误

#define N = 10
#define INC(x) x + 1
#define SUB(x, y) x - y
#define SQR(x) ((x) * (x))
#define CUBE(x) (SQR(x) * (x))
#define M1(x, y) x##y
#define M2(x, y) #x #y

main()
{
    int a[N], i, j, k, m;
#ifdef N
    i = j;
#else
    j = i;
#endif
    i = 10 * INC(j);
    i = SUB(j, k);
    i = SQR(SQR(j++));
    i = CUBE(j);
    i = M1(j, k);
    puts(M2(i, j));
#undef SQR
    i = SQR(j);
#define SQR
    i = SQR(j);
    return 0;
}

答:

main()
{
    int a[ = 10], i, j, k, m; //编译错误,=
    i = j;
    i = 10 * j + 1;
    i = j - k;
    i = ((((j++) * (j++))) * (((j++) * (j++))));
    i = (((j) * (j)) * (j));
    i = jk;  //编译错误,jk 未定义
    puts("i" "j"); 
    i = SQR(j);  //编译错误,无法识别 SQR
    i = (j);
    return 0;
}

学习心得 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明《C 语言程序设计:现代方法》第十四章练习
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址