1ifdef和#if defined, 2错误: multiple definition of `xxx`, 3#ifndef, #define, #endif,4extern

#ifdef和#if defined的区别

转自:https://blog.csdn.net/ydyang1126/article/details/51392219

两者都有个define的作用,区别在于使用方式上。

1、前者的通常用法是:
#ifdef XXX

#else

#endif

只能在两者中选择是否有定义。

2、对于后者,常用法是:

#if defined xxx1

#elif defined xxx2

#elif defined xxx3

#endif

或者这样用:

#if defined (AAA) && defined (BBB)
xxxxxxxxx
#endif

#if defined (AAA) || VERSION > 12
xxxxxxxxx
#endif

可以在多个中选择是否有定义

multiple definition of xxxxx错误出现了,MD是不是谁写代码没在.h文件加条件编译啊?仔细查看了代码发现确实加了条件编译。

#ifndef TEST_H
#define TEST_H
。。。
#endif

不是这个问题。这个问题哪里引起的呢?重复定义的问题,在哪里出现的呢?

要解决这个问题先来看看变量的定义和声明的区别。
声明是向编译器介绍名字--标识符,它告诉编译器“这个函数或变量在某处可找到,它的模样象什么”。而定义是说:“在这里建立变量”或“在这里建立函数”。它为名字分配存储空间。无论定义的是函数还是变量,编译器都要为它们在定义点分配存储空间。对于变量,编译器确定变量的大小,然后在内存中开辟空间来保存其数据,对于函数,编译器会生成代码,这些代码最终也要占用一定的内存。总之就是:把建立空间的声明成为“定义”,把不需要建立存储空间的成为“声明”。
查看代码,确实我在一个.h文件中定义了一个变量,而这个.h文件被多个文件包含,单独编译都没有问题,但是到链接的时候就出现问题了。

#ifndef TEST_H
#define TEST_H

struct pdesc const cameractrl_params[] = {
{PT_STRI_, 0, 1, OFFSET(cameractrl, homecmd), “homecmd”, 32, 0, NULL, NULL},
{PT_STRI_, 0, 1, OFFSET(cameractrl, zoomctrl), “zoomctrl”, 32, 0, NULL, NULL},
{PT_STRI_, 0, 1, OFFSET(cameractrl, focusctrl), “focusctrl”, 32, 0, NULL, NULL},
{PT_STRI_, 0, 1, OFFSET(cameractrl, aperturectrl), “aperturectrl”, 32, 0, NULL, NULL},
{PT_NULL_, 0, 0, 0, “”, 0, 0, NULL, NULL} /* PT_NULL means tail of struct pdesc array */
};

#endif

错误: multiple definition of xxx

一般在.h文件中定义一个变量声明时,在其他文件中只要包含了这个.h文件,编译的时候就会独立被编译器解释,然后每个.C文件会生成独立的标识符和符号表,所以上述代码在单独编译的时候并不会报错,语法是合法的。但是,最后在编译器链接的时候,就会将工程中所有的符号整合在一起,由于文件中有重复的变量,于是就会出现重复定义的错误,系统就是提示你
“multiple definition of xxx”。
进一步解释,我们可以这样想象编译每一个C源文件时,相当于一条有管道包围的纵向水流,二者互不干扰。当链接时两条原本相互独立的水管横向流了,所有就出现了重复的元素。所以当进行链接时就会出现重复定义的标示符。重复定义的标示符在这里只是变量,函数不会。因为函数确实只在.c中定义了一次,多次声明是没有问题的,而变量确实出现了两次定义。两次重复的变量定义链接器就不知道该已那个地址作为变量的内存,所以报错。

怎么解决这个问题呢?

其实只需要将全局变量定义从.h文件中挪到.c文件里,然后在.h文件中用extern做外部声明即可。即在.c文件中声明变量,然后在头文件.h所有的变量声明前加上extern,注意在.h文件中就不要对变量进行初始化赋值了。然后其他需要使用全局变量的.c文件中包含.h文件即可。编译器会为.c生成目标文件,然后链接时,如果该.c文件使用了全局变量,链接器就会链接到此.c文件。其他文件需要使用此全局变量也是同样的方式,目的其实只有一个,就是使变量在内存中唯一化。

例子,上面代码如此修改就对了:

在test.c中定义
//test.c

struct pdesc const cameractrl_params[] = {
{PT_STRI_, 0, 1, OFFSET(cameractrl, homecmd), “homecmd”, 32, 0, NULL, NULL},
{PT_STRI_, 0, 1, OFFSET(cameractrl, zoomctrl), “zoomctrl”, 32, 0, NULL, NULL},
{PT_STRI_, 0, 1, OFFSET(cameractrl, focusctrl), “focusctrl”, 32, 0, NULL, NULL},
{PT_STRI_, 0, 1, OFFSET(cameractrl, aperturectrl), “aperturectrl”, 32, 0, NULL, NULL},
{PT_NULL_, 0, 0, 0, “”, 0, 0, NULL, NULL} /* PT_NULL means tail of struct pdesc array */
};

在test.h中定义

//test.h
#ifndef TEST_H
#define TEST_H

extern struct pdesc const cameractrl_params[];

#endif

这样,multiple definition of xxxx就搞明白了。

done

作者:炸鸡叔
来源:CSDN
原文:https://blog.csdn.net/mantis_1984/article/details/53571758
版权声明:本文为博主原创文章,转载请附上博文链接!

=====================================

extern

1、函数的声明extern关键词是可有可无的,因为函数本身不加修饰的话就是extern。但是引用的时候一样需要声明的。

2、全局变量在外部使用声明时,extern关键字是必须的,如果变量没有extern修饰且没有显式的初始化,同样成为变量的定义,因此此时必须加extern,而编译器在此标记存储空间在执行时加载内并初始化为0。而局部变量的声明不能有extern的修饰,且局部变量在运行时才在堆栈部分分配内存。

3、全局变量或函数本质上讲没有区别,函数名是指向函数二进制块开头处的指针。而全局变量是在函数外部声明的变量。函数名也在函数外,因此函数也是全局的。

4、谨记:声明可以多次,定义只能一次。

5、extern int i; //声明,不是定义
int i; //声明,也是定义

作者:worthsen
来源:CSDN
原文:https://blog.csdn.net/qq_38880380/article/details/81474580
版权声明:本文为博主原创文章,转载请附上博文链接!

#ifndef, #define, #endif 作用

#ifndef
  它是if not define 的简写,是宏定义的一种,实际上确切的说,这应该是预处理功能三种(宏定义、文件包含、条件编译)中的一种----条件编译。
  在c语言中,对同一个变量或者函数进行多次声明是不会报错的。所以如果h文件里只是进行了声明工作,即使不使用# ifndef宏定义,多个c文件包含同一个h文件也不会报错。

但是在c++语言中,#ifdef的作用域只是在单个文件中。所以如果h文件里定义了全局变量,即使采用#ifdef宏定义,多个c文件包含同一个h文件还是会出现全局变量重定义的错误。
使用#ifndef可以避免下面这种错误:如果在h文件中定义了全局变量,一个c文件包含同一个h文件多次,如果不加#ifndef宏定义,会出现变量重复定义的错误;如果加了#ifndef,则不会出现这种错误。
示例:
   
#ifndef x //先测试x是否被宏定义过
#define x
程序段1blabla~ //如果x没有被宏定义过,定义x,并编译程序段 1
#endif
  程序段2blabla~   //如果x已经定义过了则编译程序段2的语句,“忽视”程序段 1
  条件指示符#ifndef 的最主要目的是防止头文件的重复包含和编译。了解:条件编译当然也可以用条件语句来实现。 但是用条件语句将会对整个源程序进行编译,生成的目标程序程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。如果条件选择的程序段很长,采用条件编译的方法是十分必要的。

#ifndef 和 #endif 要一起使用,如果丢失#endif,可能会报错。总结一下:在c语言中,对同一个变量或者函数进行多次声明是不会报错的。所以如果h文件里只是进行了声明工作,即使不使用# ifndef宏定义,一个c文件多次包含同一个h文件也不会报错。 使用#ifndef可以避免下面这种错误:如果在h文件中定义了全局变量,一个c文件包含同一个h文件多次,如果不加#ifndef宏定义,会出现变量重复定义的错误;如果加了#ifndef,则不会出现这种错.

#ifdef

与ifndef类似,ifdef顾名思义,就是if define,看例子

#ifdef x
程序1blabla~
#endif
  翻译:如果宏定义了x,则执行程序1.

此外,还有其他形式,还是看例子好些:

#ifndef x
#define x
程序段 1
#else
程序段 2
#endif
  当x没有由#define定义过,则编译“程序段1”,否则编译“程序段2”。

#if 表达式
程序段 1
#else
程序段 2
#endif
  它的作用是 当“表达式”值为真时。编译程序段1。否则则编译程序段2。当没有程序段2时,直接是#if—#endif

#define

在C或C++语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。“define”为宏定义命令。

被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。

优点:

(1) 方便程序的修改。这个就不多说了。

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

在C或C++语言中,“宏”分为有参数和无参数两种。

一、无参宏定义

1. 无参宏定义的一般形式为:#define 标识符 字符串

2. 其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。

复制代码
1 #include <stdio.h>
2 #define M ( a+b )
3 int main( int argc, char * argv[] )
4 {
5 int s, a, b;
6 printf( "input number a& b: " );
7 scanf( “%d%d”, &a, &b );
8 s = M*M;
9 printf( “s=%d\n” ,s );
10 }
复制代码

上例程序中首先进行宏定义,定义M来替代表达式(a+b),在 s= M * M 中作了宏调用。在预处理时经宏展开后该语句变为: S=(a+b)(a+b)  但要注意的是,在宏定义中表达式(a+b)两边的括号不能少。否则会发生错误。  如当作以下定义后:#define M (a)+(b)  在宏展开时将得到下述语句:S= (a)+(b)(a)+(b)

还要说明的是:

1.宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。

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

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

二、带参宏定义

c语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。

带参宏定义的一般形式为:  #define 宏名(形参表) 字符串

例:

#define M(y) ((y)(y)+3(y)) /宏定义/
k = M(5); /宏调用/
复制代码
#include <stdio.h>
#define MAX( a, b ) ((a>b)?(a):(b))
int main( int argc, char * argv[] )
{
int x, y, max;
printf( "input two numbers: " );
scanf( “%d%d”, &x, &y );
max = MAX( x, y );
printf( “max=%d\n”, max );
return 0;
}
复制代码
  上例程序的第一行进行带参宏定义,用宏名MAX表示条件表达式 (a>b)?a:b ,形参a,b均出现在条件表达式中。程序中 max=MAX(x,y) 为宏调用,实参x,y,将代换形参a,b。宏展开后该语句为: max=(x>y)?x:y;  用于计算x,y中的大数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值