4

我有一种奇怪的愿望;我不知道是否有任何编译器或语言扩展允许这样做。

我希望能够在函数调用中声明变量,如下所示:

int test(int *out_p) {
    *out_p = 5;
    return 1;
}

int main()
{
    if (int ret = test(int &var)) { // int var declared inside function invocation
        fprintf(stderr, "var = %d\n", var); // var in scope here
    }
    return 0;
}

因为 var 的作用域遵循 ret 的作用域。再举一个例子(来自我现在正在做的一个项目),我有

cmd_s = readline();
int x, y, dX, dY, symA, symB;
if (sscanf(cmd_s, "placeDomino:%d %d atX:%d y:%d dX:%d dY:%d",
                           &symA, &symB, &x,  &y,   &dX,  &dY) == 6) {
    do_complicated_stuff(symA, symB, x, y, dX, dY);
} else if (sscanf(cmd_s, "placeAtX:%d y:%d dX:%d dY:%d", &x, &y, &dX, &dY) == 4) {
    do_stuff(x, y, dX, dY);
    /* symA, symB are in scope but uninitialized :-( so I can accidentally
     * use their values and the compiler will let me */
}

我更愿意写

cmd_s = readline();
if (sscanf(cmd_s, "placeDomino:%d %d atX:%d y:%d dX:%d dY:%d",
                    int &symA, int &symB, int &x, int &y, int &dX, int &dY) == 6) {
    do_complicated_stuff(symA, symB, x, y, dX, dY);
} else if (sscanf(cmd_s, "placeAtX:%d y:%d dX:%d dY:%d", int &x, int &y, int &dX, int &dY) == 4) {
    do_stuff(x, y, dX, dY);
    /* Now symA, symB are out of scope here and I can't
     * accidentally use their uninitialized values */
}

我的问题是,有没有编译器支持这个?如果我以正确的方式摩擦它,gcc 是否支持它?是否有具有此功能的 C 或 C++(草案)规范?

编辑:刚刚意识到在我的第一个代码示例中,我的 int ret 声明在 C99 中也不好;我想我被 for 循环宠坏了。我也想要那个功能;想象

while(int condition = check_condition()) {
    switch(condition) {
        ...
    }
}

或类似的东西。

4

4 回答 4

4

除了块范围声明之外,在 C99 中,基本上还有另外两种声明变量的方法,这些变量根据定义限制在它们出现的语句中:

  • 复合文字的形式是(type name){ initializers }声明一个存在于当前块中的局部变量。例如,对于您可以使用的函数调用test(&(int){ 0 })
  • for范围变量仅具有for语句本身和依赖语句或块的范围。

if的局部变量表达式你可以做一些奇怪的事情,比如

for (bool cntrl = true; cntrl; cntrl = false)
   for (int ret = something; cntrl && test(&ret); cntrl = false) {
      // use ret inside here
   }

小心,这样的事情很快就会变得不可读。另一方面,优化器非常有效地将此类代码简化为基本代码,并且很容易发现块test的内侧for只被评估一次。

于 2012-02-27T10:34:00.727 回答
0
while(int condition = check_condition()) {
    switch(condition) {
        ...
    }
}

或者

if (int ret = test(int &var))

我的问题是,有没有编译器支持这个?如果我以正确的方式摩擦它,gcc 是否支持它?是否有具有此功能的 C 或 C++(草案)规范?

这不是 C。if语句或while语句的子句必须是表达式,不能是声明。

自 C99 以来,您只能for在其第一个子句中声明迭代语句:

for (clause-1; expression-2; expression-3)

子句 1 可以是声明或表达式。

于 2012-02-27T10:33:15.747 回答
0

没有编译器支持这一点。我看不出这在哪里有意义。

减少源代码行数并不一定会带来更高效的程序,这是一个常见的误解。在 C 语言中 99% 的情况下,将这样的语句重写为更紧凑的语句是没有意义的。它只会导致代码的可读性降低,最终你会得到完全相同的机器代码。

你应该做的是:

void some_func (void) // good example
{
  ... lots of code here

  int ret;
  int var;

  ret = test(&var);
  if(ret == SOMETHING)
  {
    fprintf(stderr, "var = %d\n", var); // var in scope here
  }
}

你不应该做的是:

void some_func (void) // bad example
{
  ... lots of code here

  {
    int ret;
    int var;

    if((ret = test(&var))
    {
      fprintf(stderr, "var = %d\n", var); // var in scope here
    }
  }
}

好例子和坏例子将产生完全相同的机器代码。理解这一点非常重要!

首先,坏例子中变量范围的缩小不会导致程序更有效:编译器非常有能力知道一个变量什么时候第一次使用,什么时候不再使用。在这两个示例中,编译器都会将变量 ret 和 var 存储在 CPU 寄存器或堆栈中。

另请注意,变量是在函数中间(仅限 C99/C11)还是在开头(C90 或 C99/C11)声明对于效率而言并不重要。在作用域的中间声明变量只是一种编程风格的特性:你是在告诉代码的读者这个变量从此时开始就很重要了。与阅读代码的人相反,编译器并不关心您在哪里编写了声明。

如果我们省略了 ret 并且只检查了 test() 的结果,那也不会有任何区别 - 函数的结果仍然必须保存在某个地方,要么保存在程序员显式声明的变量中,要么隐式保存在临时变量中由编译器创建。机器代码将是相同的,如果没有 ret 变量,则更难调试。

示例之间的主要区别在于,坏的包含广泛认可的不良编程实践,例如条件内赋值(MISRA-C:2004 13.1)和非布尔变量的针对零的隐式测试(MISRA-C:2004 13.2) . 添加不必要的、晦涩的、额外的本地范围。

所以我的建议是学习一些汇编程序(或反汇编程序)并了解 C 代码实际上是如何转换为机器代码的。当你知道这一点时,你就不会觉得有必要用你正在改进它的错误假设来不必要地混淆代码。

于 2012-02-27T14:29:16.737 回答
0

使用空范围,如下所示:

int test(int *out_p) {
    *out_p = 5;
    return 1;
}

int main()
{
    {
        int var, ret;
        if (ret = test(&var)) {
            fprintf(stderr, "var = %d\n", var); // var in scope here
        }
    }
    // var not in scope
    return 0;
}
于 2012-02-27T10:19:53.703 回答