2109|13

786

帖子

2

资源

纯净的硅(初级)

C语言变量的声明与定义的问题

 
本帖最后由 皈依 于 2017-2-10 08:20 编辑

首先,请出我们的问题:编译环境:keil
现象:
int a;
int a;
int main()
{
for(;;)
{;}
}
在main之前有两次int a;但keil并未报错。
此时这里的int a;是声明还是定义?

int a = 1;
int a = 2;
int main()
{
for(;;)
{;}
}

此时keil会报错,提示重复定义,和上面的情况有何区别?
出现此种问题的原因是什么?
接下来让我们掌声请出水群的前辈解答一下,为以后遇到此问题依旧困惑的小伙伴提供思路~


个人签名物致DIY 欢迎你的加入~
QQ群:646461928 公众号:智物知心致成
小店

回复

5865

帖子

209

资源

版主

注:以下内容为本人查阅资料整理,并非权威且难免出错,如果有异议欢迎讨论,并且以下内容并不全面,欢迎补充。
要弄清楚这个问题,先要复习几个基本概念,由于C语言中声明和定义的概念涉及的比较广,本文仅限于讨论变量的声明和定义以及相关的基本知识。

翻译单元:源文件经过预处理后的结果。

概念1. 变量的作用域
作用域定义了变量的可见性,共有四种作用域:
(1)函数作用域:只有一种情况,就是函数内的标号,goto语句使用的那个标号,它具有函数作用域,如果在函数内有一个标号,则在整个函数的任何位置都可以访问。(这个与变量的讨论无关)。
(2)文件作用域:如果一个变量的声明不在任何块以及函数参数列表内,则该变量具有文件作用域。
(3)块作用域:如果一个变量声明在块内或者声明在函数定义的参数列表中,则该变量具有块作用域。
(4)函数原型作用域:函数声明的参数列表中的变量声明,具有函数原型作用域。

概念2. 变量的连接类型
在同一个作用域或者不同作用域对一个变量的多次声明被引用到同一变量的过程叫做连接。连接分为三类:
(1)内部连接:变量声明具有文件作用域且由static存储类型修饰的变量为内部连接类型。
(2)外部连接:变量声明具有文件作用域并且没有static存储类型修饰,则该变量为外部连接。
(3)没有连接:函数的参数;块作用域中未用extern存储修饰符的变量
使用extern修饰的变量声明,如果此声明之前有对此变量的声明,则其连接方式与前一个声明相同,如果此声明前没有此变量的其他声明,则此声明为外部连接。
内部连接可以连接到翻译单元内部(相当于同一个源文件内)的变量,外部连接可以连接到整个工程的所有翻译单元以及库中的变量。
如果一个变量没有连接,则在同一个作用域或者命名空间中对此变量的声明不能超过1次。(此规则可以解释局部变量的声明不能多于1个,局部变量没有连接功能,所以多个声明是非法的)

概念3. 外部定义
在函数外面的声明(具有文件作用域)叫做外部声明。
包含初始化的外部声明叫做外部定义。(由此可得出外部定义是外部声明的子集)
外部定义最多只能有一个。


如果一个变量声明具有文件作用域且未被初始化,且未使用存储修饰符或者使用static修饰符,则该声明为不确定的定义(tentative definition)
如果一个翻译单元包含一个或者多个不确定的定义,并且不包该变量的外部定义,则其行为相当于在翻译单元的结尾增加了一个具有文件作用域的相应的类型的并且初始化为0的变量声明。

在同一个作用域内对同一个变量的声明必须具有兼容的数据类型:
假定unsigned char与uint8_t类型是兼容的则:
  1. unsigned char a;
  2. uint8_t a; //合法
  3. int b;
  4. double b; //不合法
复制代码


  1. int i1 = 1; // definition, external linkage
  2. static int i2 = 2; // definition, internal linkage
  3. extern int i3 = 3; // definition, external linkage
  4. int i4; // tentative definition, external linkage
  5. static int i5; // tentative definition, internal linkage
  6. int i1; // valid tentative definition, refers to pre vious
  7. int i2; // 6.2.2 renders undefined, linkage disagreement
  8. int i3; // valid tentative definition, refers to pre vious
  9. int i4; // valid tentative definition, refers to pre vious
  10. int i5; // 6.2.2 renders undefined, linkage disagreement
  11. extern int i1; // refers to pre vious, whose linkage is external
  12. extern int i2; // refers to pre vious, whose linkage is internal
  13. extern int i3; // refers to pre vious, whose linkage is external
  14. extern int i4; // refers to pre vious, whose linkage is external
  15. extern int i5; // refers to pre vious, whose linkage is internal
复制代码




点评

干货  详情 回复 发表于 2017-2-10 07:32

赞赏

1

查看全部赞赏

个人签名

EEWORLD开发板置换群:309018200,——电工们免费装β的天堂,上班摸鱼场,释放压力好地方!商家勿入!加群暗号:喵


回复

660

帖子

2

资源

纯净的硅(初级)


LOOK,and LOOK
个人签名我是一头搞电子的猪,猪是一种好色的动物,猪八戒就是代表.       

回复

1088

帖子

6

资源

版主

队长,free叔叔,别怂了。出来解答啊啊啊啊啊啊啊啊 啊啊啊啊 啊啊
个人签名

加油,一切皆有可能。


回复

144

帖子

1

资源

一粒金砂(中级)

放在前面既是定义也是声明,后面那个两次定义并且初值不一样。报错。

回复

505

帖子

1

资源

一粒金砂(高级)

int a;
int a;
这种情况已经是重复定义,不过由于你并未为其赋值,因此此时也没有实际分配空间。这时就要看编译器了,你说的Keil里面这种情况不报错。我在gcc下试了下,编译是报错的。

  1. bingqi@bingqi-OptiPlex-3020:~$ gcc test.c -o test
  2. test.c: In function ‘main’:
  3. test.c:6:6: error: redeclaration of ‘a’ with no linkage
  4.   int a;
  5.       ^
  6. test.c:5:6: note: previous declaration of ‘a’ was here
  7.   int a;
  8.       ^
复制代码

点评

你把声明放到函数里面了,肯定是要报错的。  详情 回复 发表于 2017-2-10 08:49

回复

6931

帖子

17

资源

版主

经过测试第一种情况keil也会报错
个人签名training

回复

2541

帖子

1

资源

五彩晶圆(初级)

定义还是声明不是关注点。关注点是1,你能看到这个名字,2,其他人/.o能看到这个名字,3,全局里面这个名字的类型你可以到处呐喊,但是所有声音必须一致,4,落实到最后得有人为这个名字出钱买地存粮食。
个人签名人已离开,无事别找,找也找不到。

回复

786

帖子

2

资源

纯净的硅(初级)

另外有小伙伴找到一张图 大家可以参考一下 就像7楼说的 关注点太刻意在这里的意义并不大。
%Q}ORXEVHH5~R1IRO$RCN)S.png
个人签名物致DIY 欢迎你的加入~
QQ群:646461928 公众号:智物知心致成
小店

回复

1298

帖子

4

资源

纯净的硅(高级)

这两个例子应该都报错吧
个人签名天地庄周马;江湖范蠡船。
个性签名还是放QQ号吧,2060347305,添加说明EEworld好友

回复

2541

帖子

1

资源

五彩晶圆(初级)



干货

回复

1459

帖子

0

资源

纯净的硅(高级)

本帖最后由 sint27 于 2017-2-10 09:16 编辑

平时还真没注意到这些情况,在IAR做了以下试验:重复声明在函数外面不会报错;重复声明在函数里面会报错

回复

5865

帖子

209

资源

版主

Bingqi23 发表于 2017-2-9 15:45
int a;
int a;
这种情况已经是重复定义,不过由于你并未为其赋值,因此此时也没有实际分配空间。这时就要 ...

你把声明放到函数里面了,肯定是要报错的。
个人签名

EEWORLD开发板置换群:309018200,——电工们免费装β的天堂,上班摸鱼场,释放压力好地方!商家勿入!加群暗号:喵


回复

669

帖子

1

资源

纯净的硅(中级)

很佩服楼主的观察力和想象力,发现这个问题也是件难得的本事。

在我的世界里,定义和声明区分得很清楚。
那么对我来说这些就都叫重定义,管他是bss还是zeroinit什么的…
至于报错与否,那是编译器的事情,即使能说出道理,在我的世界里也是违法的写法。
如果非要讲理:没有用到的变量,报错做什么?……

我只能说,有句话叫“咬死猎人的狗”,与其讨论到底是谁死了,不如直接定性这句是歧义病句,警示后人不要这样表达。
干我们这行,语法和原理,甚至都不是最重要的技术。

反倒是楼主能发现这个问题这一点,弥足可贵。

回复
您需要登录后才可以回帖 登录 | 注册

关闭
站长推荐上一条 1/6 下一条

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表