社区导航

 
快捷导航
  • 首页
  • 论坛
  • 查看新帖
  • 最新回复
  • 精华区
  • 社区活动
  • 联系管理员
  • 消灭零回复
  • E金币兑换
搜索
查看: 845|回复: 13

[经验] C语言变量的声明与定义的问题

[复制链接]

574

TA的帖子

2

TA的资源

一粒金砂(高级)

Rank: 3Rank: 3

发表于 2017-2-9 15:18:33 | 显示全部楼层 |阅读模式
本帖最后由 皈依 于 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会报错,提示重复定义,和上面的情况有何区别?
出现此种问题的原因是什么?
接下来让我们掌声请出水群的前辈解答一下,为以后遇到此问题依旧困惑的小伙伴提供思路~


向往不断折腾的人生~

回复

使用道具 举报

4968

TA的帖子

67

TA的资源

版主

Rank: 6Rank: 6

发表于 2017-2-9 23:08:23 | 显示全部楼层
注:以下内容为本人查阅资料整理,并非权威且难免出错,如果有异议欢迎讨论,并且以下内容并不全面,欢迎补充。
要弄清楚这个问题,先要复习几个基本概念,由于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,——电工们免费装β的天堂,商家勿入!加群暗号:喵

回复

使用道具 举报

535

TA的帖子

2

TA的资源

纯净的硅(初级)

Rank: 4

发表于 2017-2-9 15:19:27 | 显示全部楼层

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

回复

使用道具 举报

511

TA的帖子

5

TA的资源

一粒金砂(高级)

Rank: 3Rank: 3

发表于 2017-2-9 15:27:03 | 显示全部楼层
队长,free叔叔,别怂了。出来解答啊啊啊啊啊啊啊啊 啊啊啊啊 啊啊

回复

使用道具 举报

11

TA的帖子

1

TA的资源

一粒金砂(初级)

Rank: 1

发表于 2017-2-9 15:39:06 | 显示全部楼层
放在前面既是定义也是声明,后面那个两次定义并且初值不一样。报错。

回复

使用道具 举报

426

TA的帖子

1

TA的资源

一粒金砂(高级)

Rank: 3Rank: 3

发表于 2017-2-9 15:45:09 | 显示全部楼层
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

回复

使用道具 举报

6312

TA的帖子

17

TA的资源

版主

Rank: 6Rank: 6

发表于 2017-2-9 16:05:36 | 显示全部楼层
经过测试第一种情况keil也会报错
training

回复

使用道具 举报

1258

TA的帖子

2

TA的资源

纯净的硅(高级)

Rank: 6Rank: 6

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

回复

使用道具 举报

574

TA的帖子

2

TA的资源

一粒金砂(高级)

Rank: 3Rank: 3

 楼主| 发表于 2017-2-9 16:50:43 | 显示全部楼层
另外有小伙伴找到一张图 大家可以参考一下 就像7楼说的 关注点太刻意在这里的意义并不大。
%Q}ORXEVHH5~R1IRO$RCN)S.png
向往不断折腾的人生~

回复

使用道具 举报

1184

TA的帖子

4

TA的资源

纯净的硅(中级)

Rank: 5Rank: 5

发表于 2017-2-9 17:37:45 | 显示全部楼层
这两个例子应该都报错吧
天地庄周马;江湖范蠡船。
个性签名还是放QQ号吧,2060347305,添加说明EEworld好友

回复

使用道具 举报

1258

TA的帖子

2

TA的资源

纯净的硅(高级)

Rank: 6Rank: 6

发表于 2017-2-10 07:32:01 来自手机 | 显示全部楼层


干货

回复

使用道具 举报

1247

TA的帖子

0

TA的资源

纯净的硅(高级)

Rank: 6Rank: 6

发表于 2017-2-10 08:39:55 | 显示全部楼层
本帖最后由 sint27 于 2017-2-10 09:16 编辑

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

回复

使用道具 举报

4968

TA的帖子

67

TA的资源

版主

Rank: 6Rank: 6

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

你把声明放到函数里面了,肯定是要报错的。
EEWORLD开发板置换群:309018200,——电工们免费装β的天堂,商家勿入!加群暗号:喵

回复

使用道具 举报

665

TA的帖子

1

TA的资源

纯净的硅(中级)

Rank: 5Rank: 5

发表于 2017-2-12 18:23:14 | 显示全部楼层
很佩服楼主的观察力和想象力,发现这个问题也是件难得的本事。

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

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

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

回复

使用道具 举报

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

本版积分规则

  • 论坛活动 E手掌握

    扫码关注
    EEWORLD 官方微信

  • EE福利  唾手可得

    扫码关注
    EE福利 唾手可得

小黑屋|手机版|Archiver|电子工程世界 ( 京ICP证 060456

GMT+8, 2017-9-22 18:07 , Processed in 0.486316 second(s), 17 queries , Redis On.

快速回复 返回顶部 返回列表