0

I have noticed that if you pass a local variable, it can change it's value even though you didn't pass it by adress.

I have 2 questions: is for() a function or a macro? I want to see how it was written.

How can it change the value, without an adress?

int main()
{
    int i = 0;
    for(; i < 5; i++);
    /* i is 5 now. How? */
    return 0;
}
  • 2
    If you do `i=3;` that also changes its value. How can that work, given that you didn’t pass an address? – Sneftel Oct 04 '22 at 07:53
  • 4
    Do you see that little `i++`... Do you understand its consequences? – Fe2O3 Oct 04 '22 at 07:53
  • 5
    "is for() a function or a macro?" Neither. – Avi Berger Oct 04 '22 at 07:54
  • You didn't understand my question. If you write: int i =0; for(int i = 0; i<5; i++); , it won't change your local i. – noname delete Oct 04 '22 at 07:55
  • 4
    "How `for` works" is extremely basic and should be covered by whatever textbook or course material you're using. And no, you don't want to "see how [a keyword] was written" because those are compiler internals that would not make any sense to you at this point. – DevSolar Oct 04 '22 at 07:56
  • 2
    cppreference.com: [`for` statement](https://en.cppreference.com/w/c/language/for) – DevSolar Oct 04 '22 at 07:57
  • 1
    Despite the similar syntax, `for(; i < 5; i++);` is not a function call. `for` is a keyword, which has special meaning. – Andreas Wenzel Oct 04 '22 at 07:58
  • 1
    `for (int i=0;...)` defines a new scoped variable. – bereal Oct 04 '22 at 07:59
  • 1
    @nonamedelete Regarding your comment, you have *two different* "i" there, an outer one (`int i = 0;`) and an inner (redeclared) one (`for ( int i = 0; ... )`) that "shadows" your outer one. The loop works on the inner one, which goes out of scope at the end of the loop, leaving the outer (unchanged) one. If your compiler did not warn you about that shadowing redeclaration, check your compiler warning setttings because it absolutely should have. – DevSolar Oct 04 '22 at 07:59
  • 1
    Parameters to a function are separated with comma... The semicolon should be a "tip-off" that this is NOT a function call... – Fe2O3 Oct 04 '22 at 07:59
  • @DevSolar, I didn't ask how to use for(). I've used it thousands of times. I just wonder what it does internally. (I'm not native English speaker, it is hard for me to express what I mean) – noname delete Oct 04 '22 at 08:01
  • When you wonder how c / c++ function works, you can [use gcc to get assembly code](https://stackoverflow.com/questions/137038/how-do-you-get-assembler-output-from-c-c-source-in-gcc), you'll have to get notions of assembly of course... You can also [look for the libc source files](https://stackoverflow.com/questions/6481291/libc-source-location-for-download-or-online-viewing) of course – Hollyol Oct 04 '22 at 08:02
  • 1
    @nonamedelete And we told you that it isn't a function or a macro, but a keyword of the language that "just works" the way it is documented to work. If you don't really understand that about keywords, you wouldn't understand the lexing / parsing / code generation the compiler does, either. – DevSolar Oct 04 '22 at 08:03
  • @DevSolar: Most compilers, including gcc and clang, do not warn about variable shadowing by default, even if you compile with `-Wall -Wextra`. It is necessary to add `-Wshadow` with these two compilers. However, without `-Wshadow`, but with `-Wall -Wextra`, they do warn about unused variables and about setting the value of a variable, but not using it. – Andreas Wenzel Oct 04 '22 at 08:03
  • @AndreasWenzel That is exactly what I was hinting at. `-Wall` is not "all warnings", and `-Wextra` is not all warnings either. Those two options are *very* badly named. Your compiler warning settings should be a **lot** stricter than that, because `-Wall -Wextra` is merely a very basic default. – DevSolar Oct 04 '22 at 08:05
  • @Fe2O3 parameters to functions indeed separated with comma, but how can it do what it does? How can you explain the fact, that if you wrote int i inside of for() it doesn't change the outer one? – noname delete Oct 04 '22 at 08:05
  • @nonamedelete AGAIN, that has been explained already. You shadowed your outer variable by redeclaring another with the same name. This has nothing to do with `for`, either. – DevSolar Oct 04 '22 at 08:06
  • 1
    DevSolar already answered " if you wrote int i inside of for() it doesn't change the outer one"... It's called "shadowing" (and it's a BAD practice.) Using the same token to identify two different memory objects, You want to learn about "scope of names"... The 'outer' variable has scope in the function. The 'for()' variable only has scope within the loop (and it _shadows_ the 'outer' variable.) – Fe2O3 Oct 04 '22 at 08:09
  • 1
    `for ( init-clause ; cond-expression ; iteration-expression ) {. . . }` <==> `{ init-clause; while(cond-expression) { . . . iteration-expression; } }` – Avi Berger Oct 04 '22 at 08:09
  • @DevSolar, I understand that I shadowed the outer var i, but if I "shadow" not inside for(), it will change i's value. – noname delete Oct 04 '22 at 08:10
  • I didn't find what I was looking for(), but thanks for your time and answers. – noname delete Oct 04 '22 at 08:13
  • @nonamedelete It's unclear at this point whether you are referring to the code in your question (which doesn't shadow) or in your comment (which *does* shadow). Please have a look at my answer. – DevSolar Oct 04 '22 at 08:19
  • 1
    "If you write: int i =0; for(int i" This is not a part of the question as posted. Do not assume everyone understands your confusion. Please [edit] the question and add necessary clarifications. – n. m. could be an AI Oct 04 '22 at 08:21
  • 1
    *"but if I "shadow" not inside for(),"* Uhh, can you show the code? – HolyBlackCat Oct 04 '22 at 08:41

8 Answers8

2

You are confusing two things, "how for works" (which isn't your actual problem), and variable scope (which is).

Consider:

#include <stdio.h>

int main()
{
    int i = 0;

    printf( "Address of i outside the loop: %p\n", &i );

    for ( int i = 0; i < 5; ++i ) // this "shadows" i
    {
        printf( "Address of i inside the loop: %p\n", &i );
    }
}

You will see that the i inside the loop is at a different address. There are two variables named i here, and inside the for loop, the one declared at the beginning of the program is not visible.

This has nothing to do with how for works; it would be the same here:

#include <stdio.h>

int main()
{
    int i = 0;

    printf( "Address of i outside the block: %p\n", &i );

    {
        int i = 0; // this "shadows" i
        printf( "Address of i inside the block: %p\n", &i );
    }
}

You are looking at a "shadowed" variable: Inside the for loop / code block, i means something other than outside.

If you write this:

#include <stdio.h>

int main()
{
    int i = 0;

    printf( "Address of i outside the loop: %p\n", &i );

    for ( i = 0; i < 5; ++i ) // does NOT "shadow" i
    {
        printf( "Address of i inside the loop: %p\n", &i );
    }
}

...you don't redeclare i -- no int in the for statement, so there is only one declaration, and only one variable named i in the program.

for is not a function or a macro, but a keyword of the language. There is no convenient bit of code that would show you its inner workings, as what it does would be spread out across the various compiling stages (lexing, parsing, code generation). But as I showed above, what confuses you has nothing to do with for in the first place, but with variable scope. I hope the example snippets help you understanding the issue better.

DevSolar
  • 67,862
  • 21
  • 134
  • 209
  • Thank you, I see where my confusion comes from. I still wonder how ` for keyward was programmed though. – noname delete Oct 04 '22 at 08:25
  • @nonamedelete Compiler architecture is a difficult thing that's not for the faint of heart. If you're really interested, I would suggest [Crafting Interpreters](https://craftinginterpreters.com/), a very good tutorial on how to build a language interpreter. From the "how is it done" standpoint, there is little difference between an interpreter and a compiler. – DevSolar Oct 04 '22 at 08:28
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248543/discussion-between-devsolar-and-fe2o3). – DevSolar Oct 04 '22 at 14:13
  • @DevSolar Back again. The debate has been 'sanitised' in the interim, so probably pointless. Trying to think of an analogy, and, so far, this is it: "My 'son' is a 'man', but a 'man' is not necessarily my son." Especially for beginners to learn, my feeling is that it is important to highlight the difference between a variable/function definition and what could be no more than a (promissory) declaration. Many of newbie questions about that difference; far fewer about the correct term for function v. block scope. Jus' sayin'. Que sera sera... – Fe2O3 Oct 05 '22 at 04:34
2

I have noticed that if you pass a local variable, it can change it's value

That is because if we can't change values of local variables, it would be impossible to write functioning programs.

is for() a function or a macro?

It is neither, it is an iteration statement, one of the core building blocks of the language.

I want to see how it was written.

That doesn't make any sense, for the above reason. A for loop can generate all manner of machine code, including the compiler performing a complete loop unrolling and/or removing the loop entirely. For example if I add a printf("%d\n", i) at the bottom of your program, the whole loop when compiled on x86 gets replaced with mov esi, 5 = move the value 5 into a register (then print it). No loop is necessary.

A loop doesn't exist as written code, other than as a lexical element that the compiler treats in a certain way, according to the C standard.

How can it change the value, without an adress?

This has nothing to do with the loop as such but the i++ expression, which is guaranteed to update the variable "as per assignment", similar to i=i+1. No address is needed, the variable is written to directly.

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

You have to understand the concept of scope for a variable. Scope associates an identifier with an object. In less fancy words: a name with some memory.

In this code:

int main(void) {
    for (int i = 0; i < 5; ++i) {
      ...
    }
    return 0;
}

the scope of i is the block of the for block (after the second {). This is a bit unusual in that normally the scope of an identifier starts after its declaration inside a block and ends at the enclosing }.

In this code:

int main(void) {
    int i = 0;
    for (;i < 5; ++i) {
    }
    return 0;
}

i has block scope of the main's function block (the first {).

And then there's the case where the same identifier is used in different nested blocks (in C jargon this is called shadowing):

int main(void) {
    int i = 0;                    /* This i is in main's block scope. */
    for (int i = 0;i < 5; ++i) {  /* This i shadows main's i. */
       frob(i);                   /* Uses i of the inner scope. */
    }
    /* Here i is from main's scope again. */
    return 0;
}
Jens
  • 69,818
  • 15
  • 125
  • 179
0

Judging by the comment you attached to your question, it seems that the real problem is that you don't understand what variable scope and shadowing are. So here is a short demonstration program:

#include <stdio.h>

int main( void )
{
    int i = 1000;

    printf( "Test 1: i now has the value: %d\n", i );

    for ( int i = 0; i < 5; i++ )
    {
        printf( "Test 2: i now has the value: %d\n", i );
    }

    printf( "Test 3: i now has the value: %d\n", i );
}

This program has the following output:

Test 1: i now has the value: 1000
Test 2: i now has the value: 0
Test 2: i now has the value: 1
Test 2: i now has the value: 2
Test 2: i now has the value: 3
Test 2: i now has the value: 4
Test 3: i now has the value: 1000

In this program, I have declared two distinct int variables, both with the same name i. The second variable only exists inside the loop and when I use the identifier i inside the loop, it refers to this second variable. This means that the second variable i shadows the first variable, as long as it exists. As soon as you exit the loop, the second variable ceases to exist, so that it no longer shadows the first variable. That is why "Test 3" prints the value of the first variable again, which is 1000.

is for() a function or a macro?

It is neither a function nor a macro. It is a keyword, which means that it has a special meaning to the compiler. For this reason, it also does not have its own source code.

Fe2O3
  • 6,077
  • 2
  • 4
  • 20
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • You, too, Andreas... You didn't "**declare** two distinct int variables,", the code **defined** two variables... Fun when it turns out that some technical terms are allowed to be misused but not others... – Fe2O3 Oct 04 '22 at 09:40
  • 1
    @Fe2O3: [§6.7 ¶5 of the ISO C11 standard](http://port70.net/~nsz/c/c11/n1570.html#6.7p5) states the following: "A definition of an identifier is a declaration for that identifier that[...]". Therefore, every definition is a declaration (but not the other way around). I this case, both terms are correct. – Andreas Wenzel Oct 04 '22 at 11:18
0

your loop is an equivalen of:

int main(void)
{
    int i = 0;

    loop:

    if(!(i < 5)) goto loop_exit;
    i++;
    goto loop;

    loop_exit:

    printf("%d\n", i);
}
0___________
  • 60,014
  • 4
  • 34
  • 74
0

is for() a function or a macro?

Neither - it is a statement. Here's the syntax:

for ( expr1opt ; expr2opt ; expr3opt ) statement

Don't let the parentheses fool you into thinking it's a function or macro, they're just there to separate the control expressions from the body of the loop.

Here's how it works:

  • expr1, if present, is evaluated once - it usually initializes whatever condition expr2 is testing;

  • expr2, if present, is evaluated before each loop iteration - if true, statement (the loop body) is executed, otherwise we exit the loop;

  • expr3, if present, is evaluated after each loop iteration - it usually updates whatever condition expr2 is testing.

Let's walk through an example to see how it works;

int i = 0;
for ( i = 0; i < 10; i++ )
  printf( "i = %d\n", i );
  1. Set i to 0
  2. Evaluate i < 10; if false, goto 6
  3. Execute the printf statement
  4. Increment i
  5. goto 2
  6. Exit loop

Now, if you write it as

int i = 0;
for ( int i = 0; i < 10; i++ )
  printf( "i = %d\n", i );

this will create a new object named i that "shadows" the variable i declared before the loop. This new object i is local to the loop body, and any changes to it do not affect the variable i declared outside of the loop.

Each of the control expressions is optional - you could write a loop as

for ( ;; )
  do_something();

which will loop "forever" - expr2 is assumed to be true in this case.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

As John Bode said in his answer, it's a statement, but you can think of it as a macro that does this:

#define for(A;B;C){STUFF} A; while(B){STUFF C;}

Because this:

for(int i = 0; i < 10; i++){puts("howareyou");}

is identical to this:

int i = 0;
while (i < 10){
    puts("howareyou");
}

which is identical to this:

int i = 0;
loopstart:

puts("howareyou");

if(i < 10) goto loopstart;

TLDR: for loops are shortcuts for while loops, and while loops are shortcuts for "goto loops". Switch-case statements and functions are also just shortcuts for goto statements in a way.

-1

Consider this:

int main() {
    int i = 1;  // function scope
    int j = 5;
    printf( "i = %d j = %d\n", i, j );

    {   // Begin local scope
        int i = 3; // local scope within "{}"
        printf( "i = %d j = %d\n", i, j );
        // There is no legitimate way to access the 'other' i in this scope.
        // The 'function scope' i stands in the shadows...

    }   // end local scope

    printf( "i = %d j = %d\n", i, j ); // function scope

    return 0;
}
i = 1 j = 5
i = 3 j = 5 // j is not being "shadowed"
i = 1 j = 5
Fe2O3
  • 6,077
  • 2
  • 4
  • 20