多数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. 通过学生-课程关系表,熟悉hive语句

    通过学生-课程关系表,熟悉hive语句 1.在hive中创建以下三个表. create table  student(Sno int,Sname string,Sex string,Sage int, ...

  2. Android开发ListView嵌套ImageView实现单选按钮

    做Android开发两年的时间,技术稍稍有一些提升,刚好把自己实现的功能写出来,记录一下,如果能帮助到同行的其他人,我也算是做了件好事,哈哈!!废话不多说,先上个图. 先上一段代码: if (last ...

  3. C++中 结构体和类的异同

    在C++中,结构体是一种特殊形态的类. 结构体和类的唯一区别就是:  结构体和类具有不同的默认访问控制属性. 类中,对于未指定访问控制属性的成员,其访问控制属性为私有类型(private) 结构体中, ...

  4. 【Linux】Core dump故障分析

    引入: Q:如果一个程序运行3天后才会出错,这个时候难道需要我们一直用GDB调试程序3天吗? A:答案当然是否定的. 我们有更厉害的工具--Core dump 一.Coredump定义 Core Du ...

  5. matlab练习程序(随机粒子切换特效)

    视频制作软件中一般都会有相邻帧切换的特效,我过去用过vagas好像就有很多切换特效. 我想这个也算是其中一种吧,虽然我不确定实际中到底有没有这种切换. 实际上我只是下班后太无聊了,写着玩的,没什么高深 ...

  6. javax.servlet.ServletException: java.lang.NoClassDefFoundError: javax/el/ELResolver错误解决办法

    今天不用eclipse.myeclipse等开发工具,纯手写JSP页面(有点作死)时突然出现以前从来没遇到过的问题,报错如下: HTTP Status 500 - java.lang.NoClassD ...

  7. 诊断GRID无法启动问题

    诊断 Grid Infrastructure 启动问题 (文档 ID 1623340.1)

  8. June 15th 2017 Week 24th Thursday

    Whatever is worth doing is worth doing well. 任何值得做的,就把它做好. Whatever is worth doing is worth doing we ...

  9. zt dup() 和 dup2()

    dup() 和 dup2() 2011-10-07 11:06:31|  分类: Linux学习心得 |字号 订阅   dup和dup2都可用来复制一个现存的文件描述符,使两个文件描述符指向同一个fi ...

  10. java--内存管理的几点小技巧

    今天看一本书,书上提到了内存泄露,后面也提到了内存管理的小技巧,在这里记下来,以免以后忘记. 1.尽量使用直接量.比如:String str = "I can play!";而不是 ...