为什么大多数C的实现都允许以下行为?如果我们使用变量本身来定义它,就像这样:
#include <stdio.h>
int main()
{
int a = 9;
int b = ( a +b );
printf("%d",b);
}
我知道将使用b
的垃圾值,但应该有一些编译时警告。这对我来说听起来有点奇怪。在像Python这样的语言中,做这些事情是非法的:
>>> b = 9
>>> a = a + b
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
a = a + b
NameError: name 'a' is not defined
我认为Python不会将变量放到当前命名空间中,直到定义良好(即它具有当前范围内的值),而在C中,上述语句会像这样断开:< / p>
int b = 9;
int a; // put to namespace or whatever a lookup table with garbage value
a = a + b;
如果有人指出这背后的哲学,那就太好了。
答案 0 :(得分:6)
<强>解释强>
在C函数中,所有局部变量声明都会立即发生。从本质上讲,编译器会将您的代码转换为:
int a;
int b;
// start of function
a = 9;
b = a + b;
// end of function
所以你可以看到,总有一个变量b
,它存在于函数的整个运行时。
在Python中,这并没有发生。 b
在第一次分配之前不存在。
Python解释器在运行a+b
之前运行代码b = <the result of a+b>
。如果你考虑它对中间结果做了什么,你会得到相当于:
a = 9;
intermediate_value = a + b
b = intermediate_value
现在很清楚,您在定义之前使用b
。
<强>哲学强>
这归结为像C这样的静态语言和像Python这样的动态语言之间的区别。在动态语言中,很多以静态语言编译的事情都是在运行时完成的。这些事情甚至可以包括修改正在运行的程序本身的代码。 (理论上,通过不受限制地访问计算机的内存,您也可以在C中执行此操作;但在所有现代操作系统上,这都是不可能的。)
请阅读此页面http://en.wikipedia.org/wiki/Dynamic_programming_language以获取更多信息。
答案 1 :(得分:1)
理念是用C编程的人知道他们在做什么,并且你可以通过允许未定义的行为获得很多效率,在这种情况下使用未初始化的变量。
This article特别提到了未初始化变量的示例以及为什么它可以提高性能。
这通常被称为C程序中的问题来源 有许多工具可以捕获这些:从编译器警告到静态和 动态分析仪。这可以通过不要求全部来提高性能 当变量进入范围时,变量被初始化为零(如Java 一样)。对于大多数标量变量,这会导致很少的开销, 但堆栈数组和malloc'd内存会产生一个memset 存储,这可能是非常昂贵的,特别是因为存储 通常会被完全覆盖。
答案 2 :(得分:1)
实际上,您将获得4个字节的内存(假设int
为4个字节),保留第一个位置的内容,然后将该值添加到b
的值。您可以在此处详细了解C&C的未定义行为:http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
答案 3 :(得分:1)
我很惊讶GCC没有吐出任何关于在那里使用未初始化变量的警告(用-Wall -Wpedantic
编译并没有给我任何东西)。尽管如此,C绝对没有假设你在做什么。
例如,假设您正在为嵌入式系统开发,并且由于某些奇怪的原因,您在初始化之前确切知道堆栈分配的内存的值 - 那么它就是一个功能。如果您的系统以语言开发人员不期望的方式工作,C将不会妨碍您,这是一件好事。这意味着您几乎可以了解所有内容,因此您可以有效地了解代码的性能。
此外,C没有任何命名空间的概念,因此不需要执行命名空间查找。任何关于类似事情的编译器警告都源于编译器开发人员的努力 - 语言中没有区分初始化和未初始化变量的内容。
答案 4 :(得分:1)
由于语义,即名称结束后变量存在,你可以做的一件事是一个很好的循环列表初始化:
struct node {
struct node *next;
void *data;
}
struct node circular = { .next = &circular, .data = NULL };
如果我们无法引用circular
来获取其地址,即使我们在circular
的初始化表达式中,也无法像这样进行初始化!
答案 5 :(得分:0)
附录J(非规范性)表明行为未定义:
具有自动存储持续时间的对象的值被使用 不确定(6.2.4,6.7.9,6.8)。
虽然我无法在任何引用的部分找到明确的语言来支持(然后,我的血液咖啡因含量今天早上有点低,所以我可能会错过它。)
如果行为 未定义,则编译器不需要发出诊断。