多数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. 编程进阶:Java小白的序列化Serializable接口

    在之前的学习过程中,我们知道了如何使用FileInputStream输入流和FileOutputStream输出流编写程序读写文件. 下面我们来学习一下如何使用序列化和反序列化读写文件. 一.序列化 ...

  2. 使用HTML5 canvas做地图(2)瓦片以及如何计算的

    上一篇也说到瓦片,我们为什么使用瓦片?这一篇主要是关于如何拼接地图? 下面的一张图,可以一眼明了,地图是如何切割以及拼接的. 瓦片信息 瓦片信息包括切图原点,瓦片大小,格式,分辨率以及分辨率级别等. ...

  3. 【阿里云产品公测】PTS压力测试服务器性能

    作者:阿里云用户xsnjxjj 在PTS服务之前,经常使用webbench来对服务器进行压力测试,在看到阿里云PTS服务的介绍以后,深深的被PTS强大的功能所吸引     非常感谢阿里云团队给予的测试 ...

  4. 数据质量控制软件Q-CHECKER(转)

    随着企业信息化建设的不断深入进行,我们的企业将逐步地发展成为数字化企业.其中作为最基本构成的CATIA数模已经是产品开发制造的唯一依据,CATIA数模的质量就是加工的质量,就是制造的质量,就是生产出的 ...

  5. aiohttp模块1 client

    Make a Request import aiohttp async with aiohttp.ClientSession() as session: async with session.get( ...

  6. raw_input与input的区别

    1. 版本差异 raw_input——>python2版本 input——>python3版本 2. 输入格式差异 就是raw_input()随便输都是字符串,而input()必须按照Py ...

  7. sqlserver row_number函数的用法

    ROW_NUMBER()函数将针对SELECT语句返回的每一行,从1开始编号,赋予其连续的编号 必须和over一起使用 select *,ROW_NUMBER() over(order by prod ...

  8. 解决未安装unit测试和jest的Vue项目运行karma start时的错误

    一.起因 在#单元测试#以karma+mocha+chai 为测试框架的Vue webpack项目(一)文中,说明了是对已有的Vue项目进行测试框架的搭建,并进行测试.但是此项目在利用 vue-cli ...

  9. IIS中X509Certificate遇见的问题

    由于开发过程中需要用到证书,所以通过调用 X509Certificate2 访问p12文件. 代码开发完成后,在本地VS上测试通过,本地IIS上测试通过,发布到线上服务器IIS后不通过:提示找不到文件 ...

  10. May 18th 2017 Week 20th Thursday

    The greater the struggle, the more glorious the triumph. 挑战愈艰巨,胜利愈辉煌. Sometimes it would be better t ...