我非常抱歉。我不知道我的不完整的代码附件会造成如此混乱。我很高兴看到这么多真诚的帮助。
此代码将编译:
int myadd(int, int);
static int main_stat = 5;
int main()
{
int i, j;
main_stat = 13;
j = myadd(-1,7);
i = main_stat;
cout << j << i; // 3 and 13
return 0;
}
myadd.cpp
extern int main_stat = -3;
int myadd(int x,int y)
{
int t = main_stat;
t = x + y;
y = t +main_stat;
return y; // will return 3
}
参见我定义和extern链接main_stat。为什么合法?我以为你只能链接而不能定义。
是否在myadd函数调用的堆栈帧中分配了存储空间?我相信全局静态是在堆上分配的,对吧?
对不起,我想这次我会缩小我的问题:
来自C ++ Primer 4ed
extern 声明可能包含初始值设定项(合并时) 只有当它出现在函数之外时才变成定义。
我很清楚单一定义规则。
Q1。 myadd(int,int)在调用时使用了main_stat的哪个副本?与主要相同的副本,但具有不同的值(我可以测试)?或者每个函数都有自己的静态全局副本吗?
Q2。是否在堆上为这些全局静态变量分配了内存?我知道很多事情都要实现,但是不是用于静态变量的堆?
Q3。我知道以下两个是有效的
extern int x; // means int x is defined elsewhere
extern int x = 3; // declared and defined
如果我们可以在myadd的命名空间中声明一个静态全局变量,为什么我们需要第二个?如何 让事情变得清晰 就像aschepler所说的那样?
答案 0 :(得分:9)
所有带初始值设定项的变量声明也是定义;
这是一个压倒一切的规则。无论extern
。甚至有
在定义上需要extern
的情况:您只能这样做
使用具有外部链接的变量实例化模板。和
const
变量默认具有内部链接,因此您需要
类似的东西:
extern int const i = 42;
如果您想用它来实例化template<int const*>
。
答案 1 :(得分:3)
以下是声明和定义:
int x;
添加extern
说“请将其作为声明,请”。
但是当你提供一个值时,行就是一个定义,所以变量获得了extern
存储类,你恰好正好恰当地定义了它:
extern int x = 3;
链接语义与extern
通常的一样,存储位置与正常定义int x = 3
一样 - 即在命名空间范围内的TU中。 myadd
根本不相关。
“证明”很难,因为这是“没有规则反对”的案例。
这是最好的报价:
[n3290: 3.1/2]:
声明是一个定义,除非它声明 它没有指定函数体(8.4)的函数 包含extern
说明符(7.1.1)或链接规范25(7.5),既不是初始值也不是 功能体 ,[..]
以及其他一些相关信息:
[n3290: 3.5/2]:
当一个名称可能表示相同的对象,引用,函数,类型,模板,名称空间或值作为另一个名称引入的名称时,称该名称具有链接范围:
- 当名称具有外部链接时,其表示的实体可以通过其他翻译单元的范围或同一翻译单元的其他范围中的名称来引用。
- 当名称具有内部链接时,其表示的实体可以通过同一翻译单元中其他范围的名称来引用。
- 当名称没有链接时,其表示的实体不能通过其他范围的名称引用。
[n3290: 3.5/12]:
块作用域中声明的函数的名称以及块作用域extern
声明所声明的变量的名称 有联系。如果有实体的可见声明 具有相同名称和类型的链接,忽略声明的实体 在最里面的封闭命名空间范围之外,块范围 声明声明同一个实体并接收该链接 以前的声明。如果有多个这样的匹配实体, 该计划格式不正确。否则,如果找不到匹配的实体, 块范围实体接收外部链接。 [..]
答案 2 :(得分:2)
extern int main_stat=-3;
声明并定义main_stat
,而:
extern int main_stat;
只声明变量main_stat
。
您可以拥有任意数量的声明,但只能有一个定义。
关键字extern
表示外部链接。没有它main_stat
将是静态的并且具有内部链接,您不能使用来自其他翻译单元的main_stat
。
是否在myadd的堆栈框架中分配了存储空间?
绝对不会在add
的堆栈框架上
分配内存的是实现定义的,但是您可以确保该对象在整个程序期间都处于活动状态。
答案 3 :(得分:2)
问题显然源于一些误解。
有些人认为extern
关键字总是将定义转换为非定义声明。这是不正确的。
关键字extern
只是给出声明的实体外部链接。它可以应用于声明。它可以应用于 definitions (并且记住定义也是声明)。
因此,说无法定义extern
实体是完全错误的。一罐。完全没问题。
混淆通常是由于extern
应用于
int x; // no initializer
该定义突然变成了非定义的声明。这是事实,但这不过是一个必须记住的extern
关键字的一次性怪癖。如果你采用像
int x = 42;
然后将extern
关键字应用于它仍然会将其保留为定义,即在这种情况下没有怪癖。
答案 4 :(得分:1)
首先,根据您的评论,包含main函数的文件具有定义static int main_stat = 10;
。您应该知道这是not
与您在包含myadd
的文件中定义的变量相同的变量,因为作为静态变量,其范围仅限于该文件。实际上,由于具有相同名称的静态变量,main
无法访问您在此文件中定义的变量。
但这并不意味着在堆栈上创建了任何变量。两者都是单独的全局变量,只是包含main_stat
的文件中的变量main
(我简称该文件主文件,这个myadd文件)在任何其他文件中都不可用,而此处定义的变量main_stat
可以从任何包含声明extern main_stat;
的文件中访问(注意:没有初始化程序!)。但是,主文件不能包含此声明,因为它会与同名的静态变量冲突。
请注意,给定一个初始化程序会使变量的声明成为一个定义,也就是说,它与省略extern一样(注意,如果变量声明为常量,则extern可能不是省略因为常量是默认的静态)。唯一不是定义的全局外部声明是 with extern,但是没有初始化器。
答案 5 :(得分:0)
其他人已经很好地介绍了这一点,但只是为了在一个地方展示变种:
int x; // #1
是一个声明和定义。 x
的初始值为零。
int x = 3; // #2
是一个声明和定义。
const int cx; // #3
在C ++中是非法的。
const int cx = 3; // #4
是一个声明和定义,但cx
有内部链接,如果这是它在翻译单元中的第一个声明。
extern int x; // #5
是声明,但不是定义。在程序的其他地方必须有x
的定义。
extern int x = 3; // #6
是声明和定义。 extern
是不必要的,但要明确。
extern const int cx; // #7
是声明,但不是定义。在程序的其他地方必须有cx
的定义。
extern const int cx = 3; // #8
是声明和定义。除非已经看到上述声明,否则需要extern
。