C89,混合变量声明和代码

发布于 2024-11-17 12:59:57 字数 588 浏览 1 评论 0原文

我很好奇为什么当你尝试混合变量声明和代码时,C89 编译器会向你转储,例如:

rutski@imac:~$ cat test.c
#include <stdio.h>

int
main(void)
{
    printf("Hello World!\n");
    int x = 7;
    printf("%d!\n", x);
    return 0;
}
rutski@imac:~$ gcc -std=c89 -pedantic test.c
test.c: In function ‘main’:
test.c:7: warning: ISO C90 forbids mixed declarations and code
rutski@imac:~$ 

是的,你可以通过远离 -pedantic 来避免这种事情。但随后您的代码不再符合标准。正如任何能够回答这篇文章的人可能已经知道的那样,这不仅仅是一个理论上的问题。像 Microsoft 的 C 编译器这样的平台在任何情况下都可以在标准中快速执行此操作。

考虑到 C 语言的古老性,我想这个功能是由于一些可以追溯到 70 年代非凡的硬件限制的历史问题造成的,但我不知道细节。或者我完全错了?

I'm very curious to know why exactly C89 compilers will dump on you when you try to mix variable declarations and code, like this for example:

rutski@imac:~$ cat test.c
#include <stdio.h>

int
main(void)
{
    printf("Hello World!\n");
    int x = 7;
    printf("%d!\n", x);
    return 0;
}
rutski@imac:~$ gcc -std=c89 -pedantic test.c
test.c: In function ‘main’:
test.c:7: warning: ISO C90 forbids mixed declarations and code
rutski@imac:~$ 

Yes, you can avoid this sort of thing by staying away from -pedantic. But then your code is no longer standards compliant. And as anybody capable of answering this post probably already knows, this is not just a theoretical concern. Platforms like Microsoft's C compiler enforce this quick in the standard under any and all circumstances.

Given how ancient C is, I would imagine that this feature is due to some historical issue dating back to the extraordinary hardware limitations of the 70's, but I don't know the details. Or am I totally wrong there?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

ζ澈沫 2024-11-24 12:59:57

C 标准说“你不应该”,因为在 C89 标准标准化的早期 C 编译器中不允许这样做。创建一种可用于编写操作系统及其实用程序的语言是一个足够激进的步骤。这个概念可能根本没有被考虑——当时没有其他语言允许它(Pascal、Algol、PL/1、Fortran、COBOL),所以 C 也不需要。这可能会使编译器稍微难以处理(更大),并且原始编译器受到 PDP 11 系列(大机器;小机器只允许 64 KiB 用于代码和数据,据我所知)。因此,额外的复杂性并不是一个好主意。

C++ 允许声明和变量交错,但 C++ 不是、也从来不是 C。不过,C99 最终赶上了 C++(这是一个有用的功能)。但遗憾的是,微软从未实现 C99。

The C standard said "thou shalt not", because it was not allowed in the earlier C compilers which the C89 standard standardized. It was a radical enough step to create a language that could be used for writing an operating system and its utilities. The concept probably simply wasn't considered - no other language at the time allowed it (Pascal, Algol, PL/1, Fortran, COBOL), so C didn't need to either. And it probably makes the compiler slightly harder to handle (bigger), and the original compilers were space-constrained by the 64 KiB code and 64 KiB data space allowed in the PDP 11 series (the big machines; the littler ones only allowed 64 KiB for both code and data, AFAIK). So, extra complexity was not a good idea.

It was C++ that allowed declarations and variables to be interleaved, but C++ is not, and never has been, C. However, C99 finally caught up with C++ (it is a useful feature). Sadly, though, Microsoft never implemented C99.

凉薄对峙 2024-11-24 12:59:57

它可能从未以这种方式实施,因为它从未被需要。

假设您想用纯 C 语言编写如下内容:

int myfunction(int value)
   {
   if (value==0)
      return 0;
   int result = value * 2;
   return result;
   }

那么您可以轻松地用有效的 C 语言重写此内容,如下所示:

int myfunction(int value)
   {
   int result;
   if (value==0)
      return 0;
   result = value * 2;
   return result;
   }

首先声明变量,然后设置其值,绝对不会影响性能。

然而,在 C++ 中,情况不再如此。
在下面的示例中,函数 2 将比函数 1 慢:

double function1(const Factory &factory)
   {
   if (!factory.isWorking())
      return 0;
   Product product(factory.makeProduct());
   return product.getQuantity();
   }

double function2(const Factory &factory)
   {
   Product product;
   if (!factory.isWorking())
      return 0;
   product = factory.makeProduct();
   return product.getQuantity();
   }

在函数 2 中,即使工厂未运行,也需要构造产品变量。
后来,工厂制造了产品,然后赋值运算符需要复制产品(从 makeProduct 的返回值到产品变量)。在function1中,product仅在工厂工作时才被构造,即使如此,也会调用复制构造函数,而不是普通的构造函数和赋值运算符。

然而,我现在期望一个好的 C++ 编译器会优化这段代码,但在第一个 C++ 编译器中,情况可能并非如此。

第二个示例如下:

double function1(const Factory &factory)
   {
   if (!factory.isWorking())
      return 0;
   Product &product = factory.getProduct();
   return product.getQuantity();
   }

double function2(const Factory &factory)
   {
   Product &product;
   if (!factory.isWorking())
      return 0;
   product = factory.getProduct();    // Invalid.  You can't assign to a reference.
   return product.getQuantity();
   }

在此示例中,function2 完全无效。引用只能在声明时赋值,而不能稍后赋值。
这意味着在此示例中,编写有效代码的唯一方法是在变量真正初始化时编写声明。不早了。

这两个示例都说明了为什么 C++ 中确实需要允许在其他可执行语句之后声明变量,而不是像 C 中那样在块的开头声明变量。
这就解释了为什么它被添加到 C++ 中,而不是添加到实际上不需要它的 C(和其他语言)中。

It was probably never implemented that way, because it was never needed.

Suppose you want to write something like this in plain C:

int myfunction(int value)
   {
   if (value==0)
      return 0;
   int result = value * 2;
   return result;
   }

Then you can easily rewrite this in valid C, like this:

int myfunction(int value)
   {
   int result;
   if (value==0)
      return 0;
   result = value * 2;
   return result;
   }

There is absolutely no performance impact by first declaring the variable, then setting its value.

However, in C++, this is not the case anymore.
In the following example, function2 will be slower than function1:

double function1(const Factory &factory)
   {
   if (!factory.isWorking())
      return 0;
   Product product(factory.makeProduct());
   return product.getQuantity();
   }

double function2(const Factory &factory)
   {
   Product product;
   if (!factory.isWorking())
      return 0;
   product = factory.makeProduct();
   return product.getQuantity();
   }

In function2 the product variable needs to be constructed, even when the factory is not working.
Later, the factory makes the product and then the assignment operator needs to copy the product (from the return value of makeProduct to the product variable). In function1, product is only constructed when the factory is working, and even then, the copy constructor is called, not the normal constructor and assignment operator.

However, I would expect nowadays that a good C++ compiler would optimize this code, but in the first C++ compilers this probably wasn't the case.

A second example is the following:

double function1(const Factory &factory)
   {
   if (!factory.isWorking())
      return 0;
   Product &product = factory.getProduct();
   return product.getQuantity();
   }

double function2(const Factory &factory)
   {
   Product &product;
   if (!factory.isWorking())
      return 0;
   product = factory.getProduct();    // Invalid.  You can't assign to a reference.
   return product.getQuantity();
   }

In this example, function2 is simply invalid. References can only be assigned a value at declaration time, not later.
This means that in this example, the only way to write valid code is to write the declaration at the moment where the variable is really initialized. Not sooner.

Both examples show why it was really needed in C++ to allow variable declarations after other executable statements, and not in the beginning of the block like in C.
This explains why this was added to C++, and not to C (and other languages) where it isn't really needed.

暮凉 2024-11-24 12:59:57

它类似于要求在使用函数之前声明函数 - 它允许头脑简单的编译器从上到下一次性操作,并在运行过程中发出目标代码。

在这种特殊情况下,编译器可以遍历声明,累加所需的堆栈空间。当它到达第一条语句时,它可以在函数代码开始之前输出调整堆栈的代码,为局部变量分配空间。

It is similar to requiring functions to be declared before they are used - it allows a simple-minded compiler to operate in one pass, from top to bottom, emitting object code as it goes.

In this particular case, the compiler can go through the declarations, adding up the stack space required. When it reaches the first statement, it can output the code to adjust the stack, allocating space for the locals, immediately before the start of the function code proper.

难理解 2024-11-24 12:59:57

为需要在函数开始时声明所有变量的语言编写编译器要容易得多。有些语言甚至要求在函数代码之外的特定子句中声明变量(例如 Pascal 和 Smalltalk)。

原因是如果这些变量是已知的并且不会改变,则更容易将这些变量映射到堆栈(或寄存器,如果您的编译器足够智能)。

任何其他语句(尤其是函数调用)都可能修改堆栈/寄存器,使变量映射更加复杂。

It is much easier to write a compiler for language which requires all variables to be declared at the start of function. Some languages even require variables to be declared in specific clause outside of function code (Pascal and Smalltalk come to mind).

The reason is it's easier to map this variables to stack (or registers if your compiler is smart enough) if they are known and don't change.

Any other statements (esp. function calls) may modify stack/registers, making variable mapping more complex.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文