2

以下两种方法中的一种比另一种有优势吗?

这里首先测试它是否fopen成功,然后发生所有变量声明,以确保它们没有被执行,因为它们必须不必

void func(void) {
    FILE *fd;

    if ((fd = fopen("blafoo", "+r")) == NULL ) {
          fprintf(stderr, "fopen() failed\n");
          exit(EXIT_FAILURE);
    }

    int a, b, c;
    float d, e, f;
    /* variable declarations */

    /* remaining code */
}

这恰恰相反。所有变量声明都会发生,即使fopen失败

void func(void) {
    FILE *fd;
    int a, b, c;
    float d, e, f;
    /* variable declarations */

    if ((fd = fopen("blafoo", "+r")) == NULL ) {
          fprintf(stderr, "fopen() failed\n");
          exit(EXIT_FAILURE);
    }

    /* remaining code */        
}

第二种方法是否会产生任何额外的成本,当fopen失败时?很想听听你的想法!

4

5 回答 5

4

不,它没有任何成本。这两个示例最有可能编译为相同的生成二进制文件。由于没有赋值的变量声明实际上在 C 中没有任何事情,它不会生成任何代码。这些变量的可用空间将在需要时被堆栈指针简单地跳过。

于 2010-06-13T11:49:54.390 回答
1

对于大多数编译器,您示例中的变量声明不会产生额外费用。他们唯一可能的情况是如果他们有初始化值需要任何非平凡的代码来产生。

例如:

void func(const char * filename) {
    FILE *fd;
    int a, b, c;
    size_t z = strlen(filename);
    float d, e, f;
    /* variable declarations */

    if ((fd = fopen(filename, "+r")) == NULL ) {
          fprintf(stderr, "fopen() failed\n");
          exit(EXIT_FAILURE);
    }

    /* remaining code */    
    /* Some code that uses z */
}

在此示例strlen中,可能会在尝试打开文件之前调用它,但它的值从未使用过。strlen在之后调用fopen可能会产生更好的代码。但这并不是最好的例子,因为编译器通常知道像这样的几个函数strlen是纯函数式的(没有副作用,只使用它们的参数来产生结果)并且可以将调用移至自身strlen下方fopen,但您应该明白这一点.

于 2010-06-13T14:49:52.293 回答
1

fopen失败时,应用程序退出,所以没有人真正关心变量是否被初始化。没有成本,因为应用程序已经终止并且静态分配的内存已经释放。如前所述,甚至没有分配内存,但是如果您将它们全部设置为默认值,例如 0,它就会分配。即使您更改此行为,也不会产生额外费用。

此外,第二个代码可能更可取,因为它符合 C89。

于 2010-06-13T11:49:19.377 回答
1

在这两种情况下,同一范围内函数中所有变量的堆栈变量分配都可能发生在函数的顶部,因此任何一种方法都可以。

于 2010-06-13T11:59:13.450 回答
0

它不应该有任何区别,但您始终可以比较两个版本的汇编输出以确保!

堆栈上的空间总是在函数入口处准备好,可能是为了获得一个很好的函数映射变量名到编译器/编译器编写器的基指针+偏移量。至于初始化,它也不应该有任何区别。在将某些内容写入两个版本的变量之前,内容是未定义的。

然而,在 C++ 中,您可能会在跳过初始化时遇到一些问题(您在示例中的代码中没有这样做),但 C++ 的推理要复杂得多。

于 2010-06-13T12:08:41.353 回答