多数c语言的初学者对c工程开发过程各个阶段的作用理解不到位,而这方面的的知识又是实际开发过程中经常用到的技能点,所以就成为面试考察中一个重要的考察方面。例如:头文件的作用、头文件的内容;链接的作用和意义;条件编译的作用等等此类问题,接下来将c工程开发过程中的各种问题进行总结使大家能够自如应对这方面的面试题目。
  c工程开发经过以下几个阶段:
            编辑--》预处理--》编译--》汇编--》链接--》运行--》调试

预处理阶段通常要完成三件事情,用头文件内容替换#include语句;宏体替换宏名;根据条件编译选择合适的代码段,在上一篇中已经讲解了头文件的作用和意义,本篇主要讲解宏定义和条件编译的概念和作用,这在开发过程是比较常用的技术,但是多数面试者由于项目经验有限对此部分内容理解上比较模糊,本次主要从应用角度阐述这两个知识点引入的原因和作用。

一、 宏定义:分为不带参数的宏和带参数的宏
       1、不带参数的宏:
格式: #define 宏名 宏体
作用: 给常量起名字或者代替固定代码段,增强程序可读性并方便编程
   2、带参数的宏:
格式: #define 宏名(参数) 宏体
作用: 代替比较短的函数,函数代码3句以内,这样做的优点是能够避免频繁调用函数带来系统的开销(保存断点状态和回复断点状态)
面试题目1:设计一个宏,求两个数的最大值
#define MAX(x,y) x > y ? x:y
以上答案被认为是错误的,正确的答案:
#define MAX(x,y) (x) > (y) ? (x):(y)
所有参数两边都要加括号,这样做的目的是为了保证安全,因为宏替换发生
在预处理阶段,对数据类型不做任何检查,只是机械进行相关的替换,不加括号很容易造成错误。
面试题目2:设计两个宏:将一个32位寄存器某一个为置1,或清零

将data的第12位设置为1:SETBIT(data,12)
将data的第28位清0: CLEARBIT(data,28)
两个宏设计如下,用位运算解决
#define SETBIT(data,n) (data) = (data) | 0x01<<(n)
#define CLEARBIT(data,n) (data) = (data) & ~(0x01<<(n))

注意:参数两边都需要加括号

二、条件编译
1、根据一个宏是不是被定义过作为条件,选择保留部分代码,抹除部分代码
2、条件编译的基本形式:
形式1:

#define 宏名
#ifdef 宏名
代码
#endif
功能:宏名定义过了,则保留代码段,否则舍弃

形式2:
#ifndef 宏名
代码
#endif
功能:宏名未定义过了,则保留代码段,否则舍弃

形式3:
#ifdef 宏名
代码1
#else
代码2
#endif
功能:根据宏名是否被定义过作为条件,在代码1和代码2中选择保留其中之一,另外一个舍弃

3、宏定义的作用
作用1:用在头文件中,防止重复包含
例如:
头文件func.h内容:

#ifndef _FUNC_H_
#define _FUNC_H_
头文件内容
#endif
    这样当头文件被一个文件包含多次(开发中由于头文件中可以再包含头文件,此种情况比较常见)
按照以上格式编写的头文件,不管包含语句包含的多少次该头文件,由于条件编译的作用使的头文件
内容仅包含依次

例如:#include "func.h"
#include "func.h"
预处理以上两句被替换为:
#ifndef _FUNC_H_
#define _FUNC_H_
头文件内容 //该部分内容保留
#endif
#ifndef _FUNC_H_
#define _FUNC_H_
头文件内容 //该部分内容舍弃
#endif

作用2:用于注释程序
     c程序开发过程中不能嵌套注释,这样在编写或者修改较大的工程中时就显得极为不方便,可以用条件编译实现嵌套注释。

例如如下的场景,如果需要注释如下的代码,则必须用条件编译来实现
形式如下:
#define DEBUG
#ifdef DEBUG
//
/*
----
----
*/
----
-----
//
/*----
-----
-----*/
-----
----
#endif
DEBUG作为注释的控制标志,
#define DEBUG 则注释所有代码
//#define DEBUG 则取消注释

作用3、用于调试程序

程序开发过程中为了调试程序,寻找其中的逻辑错误,经常需要打印内存的值和位置,因此系统给开发者提供了三个宏,可以在开发过程中直接使用。

__FILE__:代表文件名
__FUNCTION__:代表函数名
__LINE__:代表行号

printf("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__)
作用:打印本句所在的文件名、函数名和行号,利用该语句寻找程序中逻辑错误

如果由于开发环境的限制,不能使用调试器,则灵活的使用以上三个宏,则能起到事半功倍的效果。
示例代码如下:

#define DEBUG //加入所有调试信息
//#define DEBUG //取消所有的调试信息

#ifdef DEBUG
#define PRINT printf
#else
#define PRINT(...)
#endif

int main()
{
int data1 = 3,data2 = 4;

data1 = data1 + 1;
PRINT("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
data1++;
PRINT("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
data2 = data2 + 3;
data2++;
PRINT("%s %d\n",__FUNCTION__,__LINE__);
data2 = data1 + data2;
PRINT("data2 = %d\n",data2);

data1 = data2 + 4;

PRINT("data1 = %d,data2 = %d\n",data1,data2);

return 0;
}

以上的代码,DEBUG宏是否被定义过作为是否加调试信息依据,这是某些工程中典型的一种
用法。

如果gcc编译该程序,程序中不需要对DEBUG进行定义,用参数-D控制
gcc ex1.c -o res -D DEBUG DEBUG被宏定义过编译该程序
gcc ex1.c -o res DEBUG宏未定义过编译该程序

    

c c++面试c工程开发之宏定义和条件编译的更多相关文章

  1. c/c++面试----c工程开发之头文件

    多数c语言的初学者对c工程开发过程各个阶段的作用理解不到位,而这方面的的知识又是实际开发过程中经常用到的技能点,所以就成为面试考察中一个重要的考察方面.例如:头文件的作用.头文件的内容:链接的作用和意 ...

  2. c c++面试----c工程开发之链接

    多数c语言的初学者对c工程开发过程各个阶段的作用理解不到位,而这方面的的知识又是实际开发过程中经常用到的技能点,所以就成为面试考察中一个重要的考察方面.例如:头文件的作用.头文件的内容:链接的作用和意 ...

  3. iOS开发笔记--宏定义的黑魔法 - 宏菜鸟起飞手册

    宏定义在C系开发中可以说占有举足轻重的作用.底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行.而在更高层级进行开发时,我们会将更多的重心放在 ...

  4. iOS开发之--宏定义与const的区别及使用方法

    宏定义的常见用法: 定义一段代码,或指定字符串抽成宏. const(常量): 当有字符串常量的时候,苹果推荐我们使用const,苹果经常把常用的字符串定义成const 宏定义与const的区别: 编译 ...

  5. Object-C开发常用宏定义

    Object-C开发中宏会将经常用到的系统常量进行封装,方便使用: 1.获取通知中心 #define EYNotificationCenter(name, object, userInfo) [[NS ...

  6. C++面试常见问题——01预处理与宏定义

    C++面试常见问题--------01预编译和宏的使用 C++预处理器 预处理器是一些指令,它将指示编译器在实际编译之前需要完成的预处理.预处理必须要在对程序进行词法与语义分析.代码生成与优化等通常的 ...

  7. Makefile中用宏定义进行条件编译

    在源代码里面如果这样是定义的: #ifdef   MACRONAME //可选代码 #endif 那在makefile里面 gcc   -D   MACRONAME=MACRODEF 或者 gcc   ...

  8. 标C编程笔记day04 预处理、宏定义、条件编译、makefile、结构体使用

    预处理:也就是包括须要的头文件,用#include<标准头文件>或#include "自己定义的头文件" 宏定义,如:#define PI 3.1415926 查看用宏 ...

  9. Makefile中用宏定义进行条件编译(gcc -D)/在Makefile中进行宏定义-D【转】

    本文转载自:http://blog.csdn.net/maopig/article/details/7230311 在源代码里面如果这样是定义的:#ifdef   MACRONAME//可选代码#en ...

随机推荐

  1. SQL Server 数据库定时自动备份(转)

    本文转载自:http://www.cnblogs.com/zhangq723/archive/2012/03/13/2394102.html 作者:清风寻梦 在SQL Server中出于数据安全的考虑 ...

  2. [MySQL] - MySQL连接字符串总结

    来源:http://blog.sina.com.cn/s/blog_5f0dab1e0100e4pv.html?retcode=0 一.MySQL Connector/ODBC 2.50 (MyODB ...

  3. struts2的基本配置

    1.package标签下主要的点在namespace和name,extend上 extend 一般继承自struts-default.xml下,继承其拦截器及一些功能,这个已在本人的拦截器一文中有讲到 ...

  4. Highcharts - Bar Chart & Column Chart

    1. 条形图(Bar Chart)需要的数据格式类型如下: ["Luke Skywalker", "Darth Vader", "Yoda" ...

  5. js 中有关字符串的操作

    1. substring(start, end) 1). 包头不包尾 2). start 必需项 3). end 非必需项 4). start end 谁大谁小无所谓 5). start end 若为 ...

  6. svn图标显示不正常,文件夹显示但文件不显示svn图标

    svn图标显示不正常,文件夹显示但文件不显示svn图标   这个问题的引发是自己造成的,使用myEclipse时progress会卡在 refresh svn status cache (0%)这里, ...

  7. Linux中如何安装配置Mysql和SVN服务端

    目标Linux系统为centOS 一.安装登陆mysql   1.直接以root用户运行:yum install mysql 和yum install mysql-server等带安装完成. 2.安装 ...

  8. 离线安装wxpython

    离线安装wxpython 前言 由于工作环境,我的工作机是在离线环境下的,没法连接外网.但是自己又想学习一下wxpython,只好自己手动离线安装,本来以为很简单的,但是实际上...一言难尽. 基本环 ...

  9. submit text3 常用快捷键

    Ctrl+D : 选择单词,重复可增加选择下一个相同的单词 Ctrl+F : 查找内容 Ctrl+G : 跳转到指定行 Ctrl+H : 替换 Ctrl+J : 合并行(已选择需要合并的多行时) Ct ...

  10. February 26 2017 Week 9 Sunday

    There is only one happiness in life, to love and be loved. 生命中只有一种幸福,爱与被爱. Some one told me that hea ...