c c++面试c工程开发之宏定义和条件编译
多数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工程开发之宏定义和条件编译的更多相关文章
- c/c++面试----c工程开发之头文件
多数c语言的初学者对c工程开发过程各个阶段的作用理解不到位,而这方面的的知识又是实际开发过程中经常用到的技能点,所以就成为面试考察中一个重要的考察方面.例如:头文件的作用.头文件的内容:链接的作用和意 ...
- c c++面试----c工程开发之链接
多数c语言的初学者对c工程开发过程各个阶段的作用理解不到位,而这方面的的知识又是实际开发过程中经常用到的技能点,所以就成为面试考察中一个重要的考察方面.例如:头文件的作用.头文件的内容:链接的作用和意 ...
- iOS开发笔记--宏定义的黑魔法 - 宏菜鸟起飞手册
宏定义在C系开发中可以说占有举足轻重的作用.底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行.而在更高层级进行开发时,我们会将更多的重心放在 ...
- iOS开发之--宏定义与const的区别及使用方法
宏定义的常见用法: 定义一段代码,或指定字符串抽成宏. const(常量): 当有字符串常量的时候,苹果推荐我们使用const,苹果经常把常用的字符串定义成const 宏定义与const的区别: 编译 ...
- Object-C开发常用宏定义
Object-C开发中宏会将经常用到的系统常量进行封装,方便使用: 1.获取通知中心 #define EYNotificationCenter(name, object, userInfo) [[NS ...
- C++面试常见问题——01预处理与宏定义
C++面试常见问题--------01预编译和宏的使用 C++预处理器 预处理器是一些指令,它将指示编译器在实际编译之前需要完成的预处理.预处理必须要在对程序进行词法与语义分析.代码生成与优化等通常的 ...
- Makefile中用宏定义进行条件编译
在源代码里面如果这样是定义的: #ifdef MACRONAME //可选代码 #endif 那在makefile里面 gcc -D MACRONAME=MACRODEF 或者 gcc ...
- 标C编程笔记day04 预处理、宏定义、条件编译、makefile、结构体使用
预处理:也就是包括须要的头文件,用#include<标准头文件>或#include "自己定义的头文件" 宏定义,如:#define PI 3.1415926 查看用宏 ...
- Makefile中用宏定义进行条件编译(gcc -D)/在Makefile中进行宏定义-D【转】
本文转载自:http://blog.csdn.net/maopig/article/details/7230311 在源代码里面如果这样是定义的:#ifdef MACRONAME//可选代码#en ...
随机推荐
- 阿里云服务器Linux常用命令
系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...
- javascript接入阿里云服务器
authInfo = r.data.authInfo[0]//后台请求的 var client = new OSS({ region: 'oss-cn-beijing',// 云账号AccessKey ...
- 创建简单的node服务器
昨天咱们说了封装ajax,今天咱们说一下 自己创建一个建议的node服务器: 话不多说直接上代码: var http = require('http') //对URL 解析为对象//1.导入模块 UR ...
- 前端自动化构建工具Grunt
一.了解Gurnt(http://www.open-open.com/lib/view/open1433898272036.html) Grunt 是一个基于任务的JavaScript工程命令行构建工 ...
- 在MVC中使用Bundle打包压缩js和css
第一步:安装 安装“System.Web.Optimization”:在中“NuGet”中搜索 安装. 第二步:配置 配置“Views”目录下的“web.config”文件增加“System.Web. ...
- HTML 5入门知识(二)
使用HTML 5结构标签 <article> <article>标签可以在网页中定义独立的内容,包括文章.博客和用户评论等.一个article元素通常有它自己的标题,一般放在一 ...
- QTableview 只显示横向线
#include <QApplication> #include <QTableWidget> #include <QPainter> #include <Q ...
- SharePoint Server 2010的移动设备配置方案
随着微软全新一代办公系统Office 2010 的推出,其所支持的移动方案也日渐受到人们的关注,而这些移动功能则是依赖于 SharePoint 2010 的移动性支持.下面就将大家介绍如何设置 Sha ...
- windows下使用VNC进行远程连接
在 windows 电脑上安装 VNC,包含 VNC server 和 VNC viewer,如果仅需要被操控或操控他人,选择型下载安装 VNC server 或 VNC viewer 即可. 在需要 ...
- 安卓Socket开发注意事项
如果要在安卓app里用到Socket通信,要满足: 1.在写代码的activity里import进Socket相关的包,这个很easy,如果你不知道要import什么包,也可以先不指定,可 ...