精华内容
下载资源
问答
  • 对于宏定义还要说明以下几点: 1. 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一 种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程 序对它不作任何检查。...

     对于宏定义还要说明以下几点:

    1. 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一

    种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程

    序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。


    2. 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。


    3. 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作

    用域可使用#undef命令。


    4.#define 条件编译头文件(.h)可以被头文件或C文件包含;

    重复包含(重复定义)由于头文件包含可以嵌套,那么C文件就有可能包含多次同一个头文件,

    就可能出现重复定义的问题的。通过条件编译开关来避免重复包含(重复定义)

    例如

    #ifndef __headerfileXXX__ 

    #define __headerfileXXX__  „ 文件内容„

     #endif

    展开全文
  • 宏定义和const的用法

    2014-09-14 18:12:28
    define是C语言中提供的宏定义命令,特点具有以下几条:  (1)为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率。  (2)用宏定义定义一个统一的函数,这样的话调用不用入栈出栈,对...
     define是C语言中提供的宏定义命令,特点具有以下几条:
    

        (1)为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率。

        (2)用宏定义定义一个统一的函数,这样的话调用不用入栈出栈,对程序的执行速度大有好处。

        (3)对于多处用到的常量,用宏的话,不易错,而且容易修改。

        (4)对于定义宏"函数",它的"参数"一定要加括号,使用宏的时候,要小心使用 ++ -- 等.

      1 #define命令剖析 

      1.1 #define的概念 

      #define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。 

      该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义。 

      (1) 简单的宏定义: 

      #define <宏名> <字符串> 

      例: #define PI 3.1415926 

      (2) 带参数的宏定义 

      #define <宏名> (<参数表>) <宏体> 

      例: #define A(x) x 

      一个标识符被宏定义后,该标识符便是一个宏名。这时,在程序中出现的是宏名,在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。 

      1.2 宏替换发生的时机 

      为了能够真正理解#define的作用,让我们来了解一下对C语言源程序的处理过程。当我们在一个集成的开发环境如Turbo C中将编写好的源程序进行编译时,实际经过了预处理、编译、汇编和连接几个过程,见图1。 

      源程序 

      预处理器 

      修改后的源程序 

      编译器 

      汇编程序 

      汇编器 

      可重定位的目标程序 

      连接器 

      可执行的目标程序 

      图1 C语言的编译过程 

      其中预处理器产生编译器的输出,它实现以下的功能: 

      (1) 文件包含 

      可以把源程序中的#include 扩展为文件正文,即把包含的.h文件找到并展开到#include 所在处。 

      (2) 条件编译 

      预处理器根据#if和#ifdef等编译命令及其后的条件,将源程序中的某部分包含进来或排除在外,通常把排除在外的语句转换成空行。 

      (3) 宏展开 

      预处理器将源程序文件中出现的对宏的引用展开成相应的宏定义,即本文所说的#define的功能,由预处理器来完成。 

      经过预处理器处理的源程序与之前的源程序有所有不同,在这个阶段所进行的工作只是纯粹的替换与展开,没有任何计算功能,所以在学习#define命令时只要能真正理解这一点,这样才不会对此命令引起误解并误用。 

      2 #define使用中的常见问题解析 

      2.1 简单宏定义使用中出现的问题 

      在简单宏定义的使用中,当替换文本所表示的字符串为一个表达式时,容易引起误解和误用。如下例: 

      例1 #define N 2+2 

      void main() 

      { 

      int a=N*N; 

      printf(“%d”,a); 

      } 

      (1) 出现问题:在此程序中存在着宏定义命令,宏N代表的字符串是2+2,在程序中有对宏N的使用,一般认为在读该程序时,容易产生的问题是先求解N为2+2=4,然后在程序中计算a时使用乘法,即N*N=4*4=16,其实该题的结果为8,为什么结果有这么大的偏差? 

      (2)问题解析:如1节所述,宏展开是在预处理阶段完成的,这个阶段把替换文本只是看作一个字符串,并不会有任何的计算发生,在展开时是在宏N出现的地方 只是简单地使用串2+2来代替N,并不会增添任何的符号,所以对该程序展开后的结果是a=2+2*2+2,计算后=8,这就是宏替换的实质,如何写程序才 能完成结果为16的运算呢? 

      (3)解决办法:将宏定义写成如下形式 

      #define N (2+2) 

      这样就可替换成(2+2)*(2+2)=16

        注意:宏展开只是原封不动的替换,不会做任何工作。

      2.2 带参数的宏定义出现的问题 

      在带参数的宏定义的使用中,极易引起误解。例如我们需要做个宏替换能求任何数的平方,这就需要使用参数,以便在程序中用实际参数来替换宏定义中的参数。一般容易写成如下形式: 

      #define area(x) x*x 

      这在使用中是很容易出现问题的,看如下的程序 

      void main() 

      { 

      int y=area(2+2); 

      printf(“%d”,y); 

      } 

      按理说给的参数是2+2,所得的结果应该为4*4=16,但是错了,因为该程序的实际结果为8,仍然是没能遵循纯粹的简单替换的规则,又是先计算再替换 了,在这道程序里,2+2即为area宏中的参数,应该由它来替换宏定义中的x,即替换成2+2*2+2=8了。那如果遵循(1)中的解决办法,把2+2 括起来,即把宏体中的x括起来,是否可以呢?#define area(x) (x)*(x),对于area(2+2),替换为(2+2)*(2+2)=16,可以解决,但是对于area(2+2)/area(2+2)又会怎么样 呢,有的学生一看到这道题马上给出结果,因为分子分母一样,又错了,还是忘了遵循先替换再计算的规则了,这道题替换后会变为 (2+2)*(2+2)/(2+2)*(2+2)即4*4/4*4按照乘除运算规则,结果为16/4*4=4*4=16,那应该怎么呢?解决方法是在整个 宏体上再加一个括号,即#define area(x) ((x)*(x)),不要觉得这没必要,没有它,是不行的。 

      要想能够真正使用好宏定义,那么在读别人的程序时,一定要记住先将程序中对宏的使用全部替换成它所代表的字符串,不要自作主张地添加任何其他符号,完全展 开后再进行相应的计算,就不会写错运行结果。如果是自己编程使用宏替换,则在使用简单宏定义时,当字符串中不只一个符号时,加上括号表现出优先级,如果是 带参数的宏定义,则要给宏体中的每个参数加上括号,并在整个宏体上再加一个括号。看到这里,不禁要问,用宏定义这么麻烦,这么容易出错,可不可以摒弃它, 那让我们来看一下在C语言中用宏定义的好处吧。 

      3 宏定义的优点 

      (1) 方便程序的修改

        使用简单宏定义可用宏代替一个在程序中经常使用的常量,这样在将该常量改变时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时, 我们可以用较短的有意义的标识符来写程序,这样更方便一些。我们所说的常量改变不是在程序运行期间改变,而是在编程期间的修改,举一个大家比较熟悉的例 子,圆周率π是在数学上常用的一个值,有时我们会用3.14来表示,有时也会用3.1415926等,这要看计算所需要的精度,如果我们编制的一个程序中 要多次使用它,那么需要确定一个数值,在本次运行中不改变,但也许后来发现程序所表现的精度有变化,需要改变它的值, 这就需要修改程序中所有的相关数值,这会给我们带来一定的不便,但如果使用宏定义,使用一个标识符来代替,则在修改时只修改宏定义即可,还可以减少输入 3.1415926这样长的数值多次的情况,我们可以如此定义 #define pi 3.1415926,既减少了输入又便于修改,何乐而不为呢? 

      (2) 提高程序的运行效率 使用带参数的宏定义可完成函数调用的功能,又能减少系统开 销,提高运行效率。正如C语言中所讲,函数的使用可以使程序更加模块化,便于组织,而且可重复利用,但在发生函数调用时,需要保留调用函数的现场,以便子 函数执行结束后能返回继续执行,同样在子函数执行完后要恢复调用函数的现场,这都需要一定的时间,如果子函数执行的操作比较多,这种转换时间开销可以忽 略,但如果子函数完成的功能比较少,甚至于只完成一点操作,如一个乘法语句的操作,则这部分转换开销就相对较大了,但使用带参数的宏定义就不会出现这个问 题,因为它是在预处理阶段即进行了宏展开,在执行时不需要转换,即在当地执行。宏定义可完成简单的操作,但复杂的操作还是要由函数调用来完成,而且宏定义 所占用的目标代码空间相对较大。所以在使用时要依据具体情况来决定是否使用宏定义。 

      形式参数不能用带引号的字符串替换。 

      但是,如果在替换文本中,参数名以#作为前缀则结果将被扩展为由 实际参数 替换 此实际参数的带引号的字符串。 

      例如,可以将它与字符串连接运算结合起来编写一个调试打印宏: 

      #define dprint(expr) printf(#expr “ = %/n”,expr) 

      使用语句 dprint(x/y); 

      调用宏时,该宏将被扩展为:printf(“x/y”“ = %/n”,x/y); 

      其中的字符串被连接起来了,这样便等价于printf(“x/y = %/n”,x/y); 

      在实际参数中,每个双引号“ 将被替换为 /” ;反斜杠/将被替换为//,因此替换后的字符串是合法的字符串常量。 

      预处理运算符 ## 为宏扩展提供了一种连接实际参数的手段。如果替换文本中的参数与 ## 相邻,则该参数将被实际参数替换,##与前后的空白符将被删除,并对替换后的结果重新扫描。 

      例如,下面定义的宏paste用于连接两个参数 

      #define paste(front, back) front ## back 

      因此,宏调用past(name,1)的结果将建立记号name1. 

      c语言中没有swap这个函数,C语言不支持重载,也没有模版的概念,所以对于每一种类型,都要写出相应的swap,如 

      intSwap (int *, int *); 

      longSwap (long *, long *); 

      stringSwap (char *, char *); 

      宏定义swap(t,x,y)以交换t类型的两个参数(要使用程序块结构)。 

      程序如下: 

      #include <iostream.h> 

      #define SWAP(t,x,y) / 

      {/ 

      temp = *y;/ 

      *y = *x;/ 

      *x = temp;/ 

      } 

      main() 

      { 

      int a = 10, b = 5; 

      SWAP(int,&a,&b) 

      cout << a << endl << b<<endl; 

      } 

      用/换行,/的意思是说把下一行看作和这行是同一行.换行必须要反斜杠,而且/后面直接回车,不能有空格。

    展开全文
  • 对于底层软件开发的工程师, 对宏定义肯定不陌生吧, 我在写代码的时候就习惯使用宏定义, 代码结构清晰, 修改也及其便捷, 在以下场景时使用宏定义的优势. 1. 防止头文件被重复包含#ifndef MAIN_H #define MAIN_H ........

    对于底层软件开发的工程师, 对宏定义肯定不陌生吧, 我在写代码的时候就习惯使用宏定义, 代码结构清晰, 修改也及其便捷, 在以下场景时使用宏定义的优势.

    1. 防止头文件被重复包含

    #ifndef MAIN_H
    #define MAIN_H
    .....    //代码部分, 一般为全局变量, 函数声明
    #endif

    2. 简单函数实现

    #define ABS(x) ((x)>=0 ? (x):(-x);)  //取绝对值
    #define ADD(x,y) ((x)+(y))   //加法 
    #define MAX(x,y) ((x)>(y) ? (x):(y))  //最大值

    3. 获取变量地址

    #define PTR(var) ((UINT32*)(void*)&(var))

    4. 字节拼字/字拆字节

    #define BYTE_TO_WORD(ray) ((word)((ray)[0]*256)+(ray)[1])  //字节拼字 LSB方式
    #define WORD_TO_BYTE(ray,val)    \
            (ray)[0] = ((val)/256);  \
            (ray)[1] = ((val) & 0xFF)       //字拆字节

    5. 类型重定义, 使代码跨平台和编译器

    typedef unsigned char      UINT8;
    typedef signed char        INT8;
    typedef unsigned short     UINT16; 
    typedef signed short       INT16;  
    typedef unsigned long int  UINT32; 
    typedef signed long int    INT32;  
    typedef UINT8              BOOL;

    6. 获取成员在结构体中的偏移/所占字节数

    #define MEM_OFF(type, field)  (((UINT32)&(type *)0)->field)   //偏移
    #define MEM_SIZE(type, field)  (sizeof(type *)0)->field) //所占字节数

    7. 获取指定地址上的字节/半字/字

    #define ADDR_BYTE(x)  (*(UNIT8*)(x))      //字节
    #define ADDR_BYTE(x)  (*(UNIT16*)(x))     //半字
    #define ADDR_WORD(x)  (*(UINT32*)(x))     //字

    8. 大小写转换

    #define WORD_LOW(c)  ((c)>='A' && (c)<='Z') ? ((c)+0x20) : (c))
    #define WORD_UP(c)  ((c)>='a' && (c)<='z') ? ((c)-0x20) : (c))

    9. 检查是否是十进制/十六进制

    #define DEC_CHK(c)  ((c)>='0' && (c)<='9')   //十进制
    #define HEX_CHK(c)  ((c)>='0' && (c)<='9') ||   \
                        ((c)>='A' && (c)<='F') ||   \
                        ((c)>='a' && (c)<='f')   //十六进制

    10. 获取半字的高低位

    #define WORD_LO(c) (UINT8) ((UINT16)(c) & 0xFF))   //低8位
    #define WORD_HI(c) (UINT8) ((UINT16)((c)>>8) & 0xFF))   //高8位

    11. 结构体初始化

    typedef struct          //结构体定义
    {
        uint16_t prescaler;          /**< Prescaler. */
        uint8_t  interrupt_priority; /**< Interrupt priority. */
        uint8_t  tick_latency;       /**< Maximum length of interrupt handler in ticks (max 7.7 ms). */
        bool     reliable;           /**< Reliable mode flag. */
    } nrf_drv_rtc_config_t;
    
    #define NRF_DRV_RTC_DEFAULT_CONFIG                                                               \      //使用宏初始化结构体成员变量
    {                                                                                                \
        .prescaler          = RTC_FREQ_TO_PRESCALER(RTC_DEFAULT_CONFIG_FREQUENCY),                   \
        .interrupt_priority = RTC_DEFAULT_CONFIG_IRQ_PRIORITY,                                       \
        .reliable           = RTC_DEFAULT_CONFIG_RELIABLE,                                           \
        .tick_latency       = RTC_US_TO_TICKS(NRF_MAXIMUM_LATENCY_US, RTC_DEFAULT_CONFIG_FREQUENCY), \
    }
    
    static nrf_drv_rtc_config_t const m_rtc_config = NRF_DRV_RTC_DEFAULT_CONFIG;
    

    12. 防止溢出

    #define OVERFLOW(val) (val = ((val)+1 > (val)) ? (val)+1 : (val))
    

    13. 求数组元素个数

    #define ARR_NUM(a) ((sizeof(a)/sizeof(a[0]))

    14. 求最接近的倍数值 //常用于内存分配

    #define POWER8(x) ((((x)+7)/8)*8)
    

    15. 用于跟踪调试 //ANSI标准(仅限于标准编译器, 非标准编译器可能支持不全或不支持)

    __LINE__     //对应 %d    表示当前指令所在行, 是个整型数
    __FILE__     //对应 %s    表示当前指令所在文件, 包含完整路径 
    __DATE__     //对应 %s    包含形式为月/日/年的字符串, 表示源文件被翻译到代码时的日期
    __TIME__     //对应 %s    包含源代码翻译到目标代码的时间, 字符串形式为 时:分:秒
    __STDC__     //含有十进制常量1, 如果它含有任何其它数,则实现是非标准的
    

    16. 调试使用

    #ifdef __DEBUG
        #define DEBUG_MSG(msg, date)  printf(msg);  printf("%d%d%d", date, __LINE__, __FILE__)
    #else
        #define DEBUG_MSG(msg, date)

    17. 防止多次初始化(只能进行一次)

    #define NRF_LOG_INTERNAL_FLUSH()            \
        do {                                    \
            while (NRF_LOG_INTERNAL_PROCESS()); \
        } while (0)
    展开全文
  • 关于C语言define宏定义字符串常量

    万次阅读 2017-01-08 14:43:50
    本人一直以为宏对于字符串的处理也是直接在预处理时进行替换;但是最近在工作中遇到了字符串+1的情况;于是彻底的颠覆了以前的思维;于是乎进行测试验证得出以下结果。 /*测试*/ #include #define LUOJIAN ...

    1.问题由来:

    本人一直以为宏对于字符串的处理也是直接在预处理时进行替换;但是最近在工作中遇到了字符串宏+1的情况;于是彻底的颠覆了以前的思维;于是乎进行测试验证得出以下结果。

    2.测试代码

    /*测试*/
    #include<stdio.h>
    #define HELLO "hello" 
    int main() 
    { 		
    	printf("%s\n",HELLO+1); 		
    	return 0 ; 
    }
    

    3.现实的输出结果:

    “ello” ;
    实际上,宏定义的字符串常量在预编译的时候把HELLO替换成字符串常量(“hello”);所以HELLO+1可以理解为“hello”+1 即常量首地址+1,故输出“ello”.

    展开全文
  • 使用switch-case/if-else对于条件/分支处理的程序设计,我们惯性地会选择switch-case或者if-else,这也是C语言老师当初教的。以下,我们用一个播放器的例子来说明,要实现的功能如下:收到用户操作播放器命令请求,...
  • 对于宏定义还要说明以下几点: 1) 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。...
  • 过了很久,现在突然想起了一道网易TTT计划中的一道笔试题,是关于宏定义的,但是对于宏定义很不了解,现在记录一下,大概整理一下思路,不知道对不对,希望大家勿喷。 具体的题目记不大清楚了,大概是根据以下两种...
  • 将iOS开发中经常使用的宏定义整理例如以下,仅包括Objective-C。 而对于Swift,不能使用宏,则能够定义全局函数或者extension。请參考博客iOS — 总结Swift中经常使用的全局函数和extension(持续更新中)。 // //...
  • 在实现的过程中,才发现自己对于字符编码、以及 python 内部字符串表示的相关知识的缺乏。在这期间,踩过了不少坑,到最后虽然还有些模糊的地方,但总算有一个总体清晰的了解。在此记录下心得,避免以后在同一个地方...
  • 和函数

    2019-02-26 00:37:17
    在程序中扩展#define定义符号和宏时,需要涉及以下几个步骤: 调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换; 替换文本随后被插入到程序中原来文本的位置,对于宏...
  • 宏定义时主要用于以下两方面: 1、简单文本或字符串的替换; 2、宏函数。 对于宏函数,我们可能比较陌生,下面举例来介绍一下宏函数: #define SQUARE(x) ((x)*(x)) int main() { int input...
  • 宏定义时主要用于以下两方面: 1、简单文本或字符串的替换; 2、宏函数。 对于宏函数,我们可能比较陌生,下面举例来介绍一下宏函数: #define SQUARE(x) ((x)*(x)) int main() { int in...
  • C++和枚举

    2016-05-12 08:52:00
    我们的计算器程序,用1234对应加减乘除,对于人阅读很产生一点障碍。隔一个月后再看此代码可能想不起是0123还是1234了,还得去代码中查找,如果能为代表四则运算的四个数...//定义四则运算的 #define JIA 1 ...
  • offsetof的用法

    2013-05-25 10:32:08
    Offsetof() 定义:#define offsetof(s,m) (size_t )( &(((s *) 0)->m)) 参数说明:s为结构体名称,m为结构体内某成员; 作用:计算结构体s内成员m相对于结构体起始位置的偏移地址;   以下为某位高手的解说...
  • C/C++中宏定义(#define)

    2020-12-31 19:48:24
    #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质,总是在此处产生一些困惑,在编程时误用该命令,...
  • 替换的final变量

    2019-10-02 16:10:55
    对于一个final变量阿狸说,不管是类变量 实例变量 局部变量,只要满足以下三个条件,那么这个final变量就不是一个变量了,而是一个直接量. 使用final变量修饰符修饰 在定义该final变量时指定了初始值 该初始值可以在...
  • 对于条件/分支处理的程序设计,我们惯性地会选择switch-case或者if-else,这也是C语言老师当初教的。以下,我们用一个播放器的例子来说明,要实现的功能如下: 收到用户操作播放器命令请求,如“播放”、“暂停”等...
  •  全书包含27章,详细介绍了以下内容:Excel录制器和VBA语法,引用区域,使用用户定义函数,循环和流程控制,RlC1公式,使用VBA自动控制Excel 2007新增功能,事件编程,使用用户窗体,创建图表,实现高级筛选,...
  • 由于#define定义常量是全局的,不能达到目的,于是想当然地觉得应该用const修饰数据成员来实现。const数据成员的确是存在的,但其含义却不是我们所期望的。const数据成员只在某个对象生存期内是常量,而对于整个...
  • 由于#define定义常量是全局的,不能达到目的,于是想当然地觉得应该用const修饰数据成员来实现。const数据成员的确是存在的,但其含义却不是我们所期望的。const数据成员只在某个对象生存期内是常量,而对于整个...
  • 如果定义一个结构体A,用其定义一个变量a,我们由a访问结构体中变量时,有时候会直接想到用"&a+30"(此时30是偏移字节),事实上并不是在&a上加30,应将&a进行以下强转例如:“(char *)&a+30”,最好是在获得地址后...
  • 由于#define定义常量是全局的,不能达到目的,于是想当然地觉得应该用const修饰数据成员来实现。const数据成员的确是存在的,但其含义却不是我们所期望的。const数据成员只在某个对象生存期内是常量,而对于整个...
  • 为了在测试中打印方便,我们可以简单的定义一个: #define MDMLog(str) ({NSString *name = @#str; NSLog(@"%@--&gt;%@ %p, %zd", name, [str class], str, [str retainCount]);}) 以下内容...
  • 跨平台(platform.hpp)的定义参见另一篇博文,当然截取代码实现时可以换成自己习惯的定义。跨平台中条件编译的使用使得代码不是那么容易看,但是还好每个函数只有一两句话自旋锁比互斥量轻量一些,适合使用在轻量...
  • 跨平台(platform.hpp)的定义参见另一篇博文,当然截取代码实现时可以换成自己习惯的定义。跨平台中条件编译的使用使得代码不是那么容易看,但是还好每个函数只有一两句话windows/VC下使用系统API实现,其他情况下...
  • C语言Define

    2017-11-23 19:02:27
    一.不带参数的宏定义 不带参数的宏定义的格式: #define 标识符 字符或字符串 其中,标识符称为宏名。例如:#define PI 3.1415926 其作用是将宏名PI定义为实数3....对于不带参数的宏定义以及宏替换有以下说明:

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 138
精华内容 55
关键字:

对于以下宏定义