为什么在C中允许以下内容?

时间:2014-06-27 07:48:50

标签: python c

为什么大多数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;

如果有人指出这背后的哲学,那就太好了。

6 个答案:

答案 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)。

虽然我无法在任何引用的部分找到明确的语言来支持(然后,我的血液咖啡因含量今天早上有点低,所以我可能会错过它。)

如果行为 未定义,则编译器不需要发出诊断。