C和指针 第14章 预处理器 14.9 问题

1. 预处理器定义了5个符号,给出了进行编译的文件名、文件的当前行号、当前日期和时间以及编译器是否为ANSI C编译器。为每一种符号举出一种可能的用途。 
解析:
在打印错误信息时,文件名和行号可能是很有用的,尤其是在调试的早期阶段。事实上,assert宏使用它们来实现自己的功能。__DATA__和__TIME__可以把版本信息编译到程序中。最后,__STDC__可以用在条件编译中,用于在必须由两种类型的编译器进行编译的源代码中选择ANSI和前ANSI结构。 

2. 说出使用#define定义的名字替代字面值常量的两个优点。 
解析:
优点1:定义的名字一般全部大写,能清晰告知程序员这个字是字面值常量。
优点2:只需要修改#define定义的名字一处,就可以修改所有使用该名字的地方。 

3. 编写一个用于调试的宏,打印出任意的表达式。它被调用时应该接受两个参数。第1个是printf格式码,第2个是需要打印的表达式。 
解析: 
#include <stdio.h>
#include <stdlib.h>

#define DEBUG( FORMAT, EXPRESSION ) printf( FORMAT, EXPRESSION ) 

int main( void ){
    const char *format = "%s\n";
    int x;
    const char *pc = "hello";
    float y;
    
    x = 1;
    y = 1.0;
    DEBUG( format, pc );
    DEBUG( "%s\n", "hello" );
    DEBUG( "%d\n", x );
    DEBUG( "%f\n", y );
     
    return EXIT_SUCCESS;
}
输出:

4. 下面的程序员将打印出什么?在展开#define内容时必须非常小心!
    #define MAX( a, b ) (a) > (b) ? (a) : (b)
    #define SQUARE( x ) x * x
    #define DOUBLE( x ) x + x
    main()
    {
        int x, y, z;
        y = 2; z = 3;
        x = MAX( y, z );
        /* a */printf( "%d %d %d\n", x, y, z );
        
        y = 2; z = 3;
        x = MAX( ++y, ++z );
        /* b */printf( "%d %d %d\n", x, y, z );
        x = 2;
        y = SQUARE( x );
        z = SQUARE( x + 6 );
        /* c */printf( "%d %d %d\n", x, y, z );
        x = 2;
        y = 3;
        z = MAX( 5 * DOUBLE( x ), ++y );
        /* d */printf( "%d %d %d\n", x, y, z );    
    }
解析: 
#include <stdio.h>
#include <stdlib.h>

#define MAX( a, b ) (a) > (b) ? (a) : (b)
#define SQUARE( x ) x * x
#define DOUBLE( x ) x + x
int main( void )
{
    int x, y, z;
    y = 2; z = 3;
    x = MAX( y, z );
    /* a */printf( "%d %d %d\n", x, y, z );        
    y = 2; z = 3;
    x = MAX( ++y, ++z );
    /* b */printf( "%d %d %d\n", x, y, z );
    x = 2;
    y = SQUARE( x );
    z = SQUARE( x + 6 );
    /* c */printf( "%d %d %d\n", x, y, z );
    x = 2;
    y = 3;
    z = MAX( 5 * DOUBLE( x ), ++y );
    /* d */printf( "%d %d %d\n", x, y, z );
    
    return EXIT_SUCCESS;    

输出:

5. putchar函数定义于stdio.h中,尽管它的内容比较长,但它是作为一个宏实现的。你认为它为什么以这种方式定义呢?
解析:
因为会经常使用它,而它作为宏实现的执行速度比作为函数实现的执行速度快。

6. 下列代码是否有错?如果有,错在何处?
    /*
    ** Process all the values in the array.
    */ 
    result = 0;
    i = 0;
    while( i < result ){
        result += process( value[i++] );
    }
解析:
我们无法通过给出的源代码进行判断。如果process以宏的方式实现,并且对它的参数求值超过一次,增加下标值的副作用可能会导致不正确的结果。 

7. 下列代码是否有错?如果有,错在何处?
    #define SUM( value ) ((value) + (value))
    int array[SIZE];
    ...
    /*
    ** Sum all the values in the array.
    */ 
    sum = 0;
    i = 0;
    while( i < SIZE )
        sum += SUM( array[i++] );
解析:
这个代码有几个地方存在错误,其中几处比较微妙。它的主要问题是这个宏依赖于具有副作用(增加下标值)的参数。这种依赖性是非常危险的,由于宏的名字并没有提示它实际执行的任务(这是第二个问题),这种危险进一步加大了。假定循环后来改写为:
    for( i = 0; i < SIZE; i += 1 ){
        sum += SUM( array[i] );
    } 
尽管看上去相同,但程序此时将会失败。最后一个问题是:由于宏始终访问数组中的两个元素,因此如果size是个奇数值,程序就会失败。
#include <stdio.h>
#include <stdlib.h> 

#define SUM( value ) ((value) + (value))
#define SIZE 4
#define SIZE2 5

int main( void ){
    int array[SIZE] = { 1, 2, 3, 4 };
    int array2[SIZE2] = {1, 2, 3, 4, 5}; 
    int sum, i;
    /*...*/
    /*
    ** Sum all the values in the array.
    */ 
    sum = 0;
    i = 0;
    while( i < SIZE )
        sum += SUM( array[i++] );
    printf( "i = %d, SIZE = %d, sum = %d\n", i, SIZE, sum );
    sum = 0;
    for( i = 0; i < SIZE; i += 1 ){
        sum += SUM( array[i] );
    } 
    printf( "i = %d, SIZE = %d, sum = %d\n", i, SIZE, sum );
    sum = 0;
    i = 0;
    while( i < SIZE2 )
        sum += SUM( array2[i++] );
    printf( "i = %d, array2[%d] = %d, SIZE2 = %d, sum = %d\n", i, SIZE2, array2[SIZE2], SIZE2, sum );
    sum = 0;
    for( i = 0; i < SIZE2; i += 1 ){
        sum += SUM( array2[i] );
    } 
    printf( "i = %d, array2[%d] = %d, SIZE2 = %d, sum = %d\n", i, SIZE2 - 1, array2[SIZE2 - 1], SIZE2, sum );
    
    return EXIT_SUCCESS;
}
输出:

8. 下列代码是否有错?如果有,错在何处?
    在文件header1.h中:
    #ifndef  _HEADER1_H
    #define _HEADER1_H
    #include "header2.h"
        其他声明
    #endif
    在文件header2.h中:
    #ifndef  _HEADER2_H
    #define _HEADER2_H
    #include "header1.h"
        其他声明
    #endif
解析:
没有错误。但是这是一个坏习惯,因为包含这两个头文件的任何一个都会包含另外一个头文件。由于#include的使用,因此两次包含了两个头文件中的任何一个。但是根据#ifndef、#define和#endif的使用,因此两次包含了两个头文件中的任何一个只有一个被成功执行。
/*
** header1.h
*/
#ifndef     _HEADER1_H
#define    _HEADER1_H
#include <stdio.h>
#include <stdlib.h>
#include "header2.h"
    /*其他声明*/
int print_hello( void ){
    return printf( "hello\n" );
}
#endif

/*
** header2.h
*/
#ifndef     _HEADER2_H
#define    _HEADER2_H
#include "header1.h"
    /*其他声明*/
int print_good_morning( void ){
    return printf( "good morning\n" );
}
#endif

/*
** problem_8_header_file_include.c
*/
#include <stdio.h>
#include <stdlib.h>
#include "header1.h"

int main( void ){
    print_hello();
    print_good_morning();
     
    return EXIT_SUCCESS;
}
输出:

9. 在一次提高程序可读性的尝试中,一位程序员编写了下面的声明。
    #if sizeof( int ) == 2
        typedef long int32;
    #else 
        typedef int int32;
    #endif
这段代码是否有错?如果有,错在何处?
解析:
有错误。首先long拥有的比特数比int拥有的比特数多,其次,int拥有的比特数并没有硬性规定为32位。这里我们假设1字节占用8个比特的内存。修改如下:
#if  sizeof(int) == 2
    typedef int int16;
#elif sizeof(int) == 4
    typedef int int32;
#else 
    printf( "I don't know the int owned number of bit.\n" ); 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_40186813

你的能量无可限量。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值