37

I thought one could declare several variables in a for loop:

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

But I just found out that this is not possible. GCC gives the following error:

error: expected unqualified-id before 'char'

Is it really true that you can't declare variables of different types in a for loop?

bodacydo
  • 63,809
  • 83
  • 206
  • 303
  • 1
    I know there is a very closely-related question somewhere at SO, but I cannot quite find it... – Andreas Rejbrand Jul 27 '10 at 23:32
  • why would you do that to the next person who has to read your code (which may be you)? – msw Jul 27 '10 at 23:37
  • 4
    @msw I really don't think `for(int i = 0, char* ptr = bam)` is any harder to read than `int i; char* ptr; for(i = 0, ptr = bam)` – Michael Mrozek Jul 27 '10 at 23:50
  • 2
    agreed, with names like `ptr` and `bam`, it is pretty hard to make it less readable – msw Jul 28 '10 at 00:18
  • 4
    @msw ...it's most likely simplified for question-asking purposes – Michael Mrozek Jul 28 '10 at 00:20
  • @Michael: http://stackoverflow.com/questions/3337126/in-c-why-cant-i-write-a-for-loop-like-this-for-int-i-1-double-i2-0 – Jacob Jul 28 '10 at 02:56
  • @Jacob I don't know why you directed that at me; were you looking for @Andreas? In any case, that appears to be a duplicate; you should vote to close this one as a duplicate of that one or the one it's marked as a possible duplicate of ( [Multiple Counter Problem In For Loop](http://stackoverflow.com/questions/2340073/multiple-counter-problem-in-for-loop) ) – Michael Mrozek Jul 28 '10 at 03:36
  • @Michael: You're right! That was supposed to be @Andreas. – Jacob Jul 28 '10 at 09:51
  • 1
    possible duplicate of [Is it possible to declare two variables of different types in a for loop?](http://stackoverflow.com/questions/2687392/is-it-possible-to-declare-two-variables-of-different-types-in-a-for-loop) – ks1322 Apr 02 '15 at 14:53
  • It's curious that almost until 10 years (2 days LOL) after this question was made no one noticed that the question title and asking was wrong. OP didn't ask for initialization (as I think everyone is clear that it is possible), OP ask for declaration. If someone in the future wonders, I edited the question to fit properly. – RobertS supports Monica Cellio Jul 25 '20 at 15:24

7 Answers7

49

You can (but generally shouldn't) use a local struct type.

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

Since C++11, you can initialize the individual parts more elegantly, as long as they don't depend on a local variable:

for ( struct { int i = 0; std::string status; } loop;
      loop.status != "done"; ++ loop.i )
    { ... }

This is just almost readable enough to really use.


C++17 addresses the problem with structured bindings:

for ( auto [ i, status ] = std::tuple{ 0, ""s }; status != "done"; ++ i )
Potatoswatter
  • 126,977
  • 21
  • 238
  • 404
  • 14
    Ugly as hell, but effective. – Stephen Canon Jul 28 '10 at 00:12
  • 21
    Wow; I've never seen that before. And I'm sure teammates would kill me if I ever used it, but I'm tempted – Michael Mrozek Jul 28 '10 at 00:13
  • +1: very eclectic. Not something I would do but it reveals the underlying language semantics very nicely. – Amardeep AC9MF Jul 28 '10 at 00:22
  • 9
    When people say, "just because you *can* do it doesn't mean you *should* ", they're talking about stuff like this. It works, though. – John Bode Jul 28 '10 at 14:09
  • 3
    @Potatoswatter: It has a certain style, a bit like Angler fish. They're really ugly, but people are fascinated by them. – JeremyP Jul 29 '10 at 07:44
  • the syntax is wrong for the C++17 example, you would need to create a tuple or pair or something else to destructure. `auto [i, status] = std::tuple{0, ""s}`. I have c++ covered [on this question](https://stackoverflow.com/a/18514815/1013719) – Ryan Haining Aug 08 '20 at 07:53
  • @RyanHaining Thanks for the tip. I've updated this answer, probably to the chagrin of the users of plain C who will read it :P – Potatoswatter Aug 08 '20 at 15:39
21

It's true that you can't simultaneously declare and initialize declarators of different types. But this isn't specific to for loops. You'll get an error if you do:

int i = 0, char *ptr = bam;

too. The first clause of a for loop can be (C99 §6.8.5.3) "a declaration" or a "void expression". Note that you can do:

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

because i and *j are both of type int. The exact syntax for a declaration is given in §6.7

Matthew Flaschen
  • 255,933
  • 45
  • 489
  • 528
17

If you really need the variables to stay in the scope of the loop you could write

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

It's a bit ugly, but works.

Axel Gneiting
  • 5,083
  • 23
  • 30
  • 8
    This is the best answer... unfortunate that it doesn't work when writing a macro which expands to `for (...)` (so the user of the macro adds their own curly brackets for the body). – Alex D May 17 '16 at 09:09
7

Try this:

int i;
char* ptr;
for (i = 0, ptr = bam; i < 10; i++) { ... }
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Gary
  • 5,392
  • 1
  • 19
  • 41
2

You can also do:

for (int i = 0; i < 10; i++) {
    static char* ptr = bam;
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Ladon
  • 81
  • 3
-3

According to http://linuxsoftware.co.nz/cppgrammar.html#for-init-statement you can get only simple declaration or an expression (which is not permitted to contain declaration) in the for-init statement. That means the answer is no (if I analyzed the BNF correctly :) )

Karel Petranek
  • 14,540
  • 4
  • 39
  • 66
-6

I think the languages they teach you kids these days are meant to handcuff you and rot your brain so you just be quiet and put the lego blocks together in the very limited form in which they are meant to snap together so you build mediocre stuff. The beauty of C is that you can follow the rules, and in a clever way, to get what you want. Here is how you write this loop with extra initialzers. Here is a working example that shows you how to bridge an extended loop onto a first. You use the first to pirate its variables and they remain in scope. You use a dummy variable to make the outer loop run once. A smart compiler will note the fact and nuke the loop with the loop unroller. So for you it is just benefit. The second array then uses some variables from the first declaration and the second declaration and runs to completion. It is a trivial example just meant you to be able to understand how to do it without the heavy handed move of throwing in some scoping. Because this technique can be used with macros when written like this in order to create beautiful next-generation like structure enumeration, like "for value in array do", of which I have many.

#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]);
    }
}

There is nothing missing here. This technique of for loop joining with a one shot for loop has been used to embed in a call to get an enumerator for this hash object, and start the enumeration, to get the values extracted for the key and value, and also by convenience create them as void pointers for the user, he just has to name them. Then they are filled, and the enumeration will continue until all keys and values are done. If the user breaks in the iteration the whole cascade of oneshot forloops will fall apart as we'd want because they're probably not even there since they were constructed with simple const ops that the compiler can see will unroll the loop. So basically it lets you extend the syntax to do things like this with no penalty.

But you need to know a bit of C and at least have your head out of the robot box that the schools are putting them in these days with these toy languages.

nobody
  • 195
  • 1
  • 6
  • Interestingly, your example doesn't work - assuming that you wanted it to actually print anything. In fact you're not even fully answering the question: if you had to initialize 3 vars of different types, would you be using 2 "one-shot for"? Urgh. Axel Gneiting's answer is so much simpler and solves it all. – hmijail mourns resignees Jun 29 '17 at 23:18