如何在C语言的for (;;)循环中声明多个变量?

45

我曾认为可以在一个 for 循环中声明多个变量:

for (int i = 0, char* ptr = bam; i < 10; i++) { ... }

但我刚刚发现这是不可能的。GCC 给出以下错误:

error: expected unqualified-id before 'char'

你真的不能在 for 循环中声明不同类型的变量吗?


1
我知道在SO上有一个非常相关的问题,但我找不到它... - Andreas Rejbrand
4
жҲ‘еҸҜд»ҘжӢ…д»»дёӯж–Үзҝ»иҜ‘пјҢд»ҘдёӢжҳҜзҝ»иҜ‘з»“жһңпјҡ@mswпјҢжҲ‘зңҹзҡ„дёҚи®Өдёәfor(int i = 0, char* ptr = bam)жҜ”int i; char* ptr; for(i = 0, ptr = bam)жӣҙйҡҫйҳ…иҜ»гҖӮж¶ҰиүІеҗҺзҡ„еҶ…е®№еә”дҝқжҢҒеҺҹж„ҸпјҢ并且жҳ“дәҺзҗҶи§ЈгҖӮиҜ·жіЁж„ҸпјҢжӯӨеӨ„дёҚжҸҗдҫӣйҷӨзҝ»иҜ‘з»“жһңд№ӢеӨ–зҡ„д»»дҪ•дҝЎжҒҜгҖӮ - Michael Mrozek
2
同意,像ptrbam这样的名称,很难使其更易读。 - msw
4
@msw...很可能是为了提问方便而简化的。 - Michael Mrozek
显示剩余5条评论
7个回答

59

你可以(但通常不应该)使用局部结构体类型。

for ( struct { int i; char* ptr; } loopy = { 0, bam };
      loopy.i < 10 && * loopy.ptr != 0;
      ++ loopy.i, ++ loopy.ptr )
    { ... }
自 C++11 起,只要这些部分不依赖于局部变量,就可以更加优雅地初始化它们的各个部分。
for ( struct { int i = 0; std::string status; } loop;
      loop.status != "done"; ++ loop.i )
    { ... }

这几乎可以读懂,但还不够实用。


C++17通过 结构化绑定解决了这个问题:

using namespace std::literals::string_literals;

for ( auto [ i, status ] = std::tuple{ 0, ""s }; status != "done"; ++ i )

20
丑陋无比,但有效。 - Stephen Canon
24
哇,我以前从没见过这个。我敢肯定如果我用它,我的队友们会骂死我,但我实在是心动了。 - Michael Mrozek
+1:非常兼容。虽然这不是我会做的事情,但它很好地展示了底层语言语义。 - Amardeep AC9MF
10
当人们说“仅仅因为你能够做某事并不意味着你应该去做”,他们讨论的是像这样的事情。虽然这样做可行,但这种说法确实有道理。 - John Bode
3
它有一定的风格,有点像深海鱼。它们非常丑陋,但人们却着迷于它们。 - JeremyP
显示剩余2条评论

28

确实无法同时声明和初始化不同类型的声明符。但这并不是特定于for循环的。如果您这样做,将会收到一个错误消息:

int i = 0, char *ptr = bam;

第一个for循环的子句可以是(C99 §6.8.5.3)"声明"或"void表达式"。请注意,您可以这样做:

int i = 0, *j = NULL;
for(int i = 0, *j = NULL;;){}

因为i*j都是int类型。声明的确切语法在§6.7中给出。


小心!你声明了4个变量。两个叫i,两个叫j。循环内的'i'和'j'与循环外的'i'和'j'不是同一个。我的编译器警告我循环外的'i'和'j'没有被使用过。 - shrewmouse

17

如果您真的需要变量保留在循环的作用域内,您可以编写以下代码:

{ char* ptr = bam; for (int i = 0; i < 10; i++) { ... } }

虽然有些丑陋,但是可以使用。


9
这是最佳答案...不幸的是,当编写扩展为for (...)的宏时它无法工作(因此宏的使用者需要为其添加自己的花括号来表示循环体)。 - Alex D

10

试试这个:

int i;
char* ptr;
for (i = 0, ptr = bam; i < 10; i++) { ... }

0

你还可以这样做:

for (int i = 0; i < 10; i++) {
    static char* ptr = bam;
}

如果bam不是编译时常量,或者在循环内部修改了ptr并且循环被执行了第二次,那么这种方法就行不通。此外,静态内存速度较慢,并且在循环结束时不会被释放,增加了应用程序的内存占用。因为这些原因,我对这个答案进行了负评。 - undefined

-2

1
链接失效了,你应该发布重要的片段。不管怎样,你分析了一个 "cpp语法"的BNF……用于一个C问题? - hmijail

-10

我认为现在教给孩子们的编程语言是为了限制你的思维,让你只能按照既定方式拼凑乐高积木,从而构建平庸的东西。C语言的美妙之处在于你可以遵循规则,巧妙地达到自己想要的效果。下面是如何使用额外的初始化器编写循环的方法。这里有一个工作示例,展示了如何将扩展循环桥接到第一个循环上。你可以使用第一个循环来获取其变量并保持其范围内。你可以使用一个虚拟变量使外部循环运行一次。聪明的编译器会注意到这个事实,并使用循环展开器来优化循环。所以对你来说只有好处。第二个数组然后使用第一个声明和第二个声明中的一些变量,并运行到完成。这只是一个简单的例子,旨在让你理解如何做到这一点,而不需要使用一些复杂的作用域技巧。因为这种技术可以与宏一起使用,以创建美丽的下一代结构枚举,例如“for value in array do”,我有很多这样的例子。

#include "stdio.h"
int
main(int argc, char **argv)
{
    const int max=7;
    const char *array[7] = {
    "hello","you","kids","who","don't","know","malloc\n"
    };

    for(int i=0,count=max,$=1;$;$=0)
    for(const char **p=array;count<max;i++)
    {
        printf("%s ",p[i]);
    }
}

这里没有任何遗漏。这种使用for循环连接一个一次性的for循环的技术已经被用来嵌入调用获取此哈希对象的枚举器,并开始枚举,以获取提取的键和值,同时通过方便地将它们创建为用户的void指针,他只需要给它们命名。然后它们就会被填充,枚举将继续,直到所有键和值都完成。如果用户在迭代中中断,整个级联的一次性for循环将会崩溃,因为它们可能根本不存在,因为它们是由编译器可以看到将展开循环的简单const操作构造的。所以基本上它让你扩展语法来做这样的事情,而不会有任何惩罚。

但是你需要了解一些C语言,并至少让自己的头脱离出这些玩具语言的机器人盒子,这是学校现在正在灌输给他们的。


有趣的是,你的例子并不起作用 - 假设你想要它实际上打印任何东西。事实上,你甚至没有完全回答这个问题:如果你必须初始化三个不同类型的变量,你会使用两个“一次性for”吗?啊。Axel Gneiting的答案更简单,解决了所有问题。 - hmijail
"玩具语言" - bg117
C语言的美妙之处在于你可以遵循规则,并以巧妙的方式获得你想要的结果。你刚刚描述了存在的几乎每一种编程语言。如果我可以完全按照自己的意愿去做,而不用过多担心指针和内存分配,我宁愿选择放弃C语言,不浪费我的时间。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接