Am I erasing the stack here?

I am using gcc on Linux and the code below compiles successfully but does not print the variable values ​​correctly i

if a character is entered one time at a time i

jumps or converges to 0. I know what I use %d

for a char

in scanf (I tried to erase the stack). Is this an attempt to erase the stack or something else? (I thought if the stack was removed the program would crash).

#include <stdio.h>

int main()
{
    int i;
    char c;

    for (i=0; i<5; i++) {
        scanf ("%d", &c);
        printf ("%d ", i);
    }
    return 0;
}

      

+3


source to share


4 answers


In addition to the arguments main

, the stack contains int

and char

. Let's assume sizeof(int) == 4

and look only at i

and c

.

(              int i               )(char c )
[   0   ][   1   ][   2   ][ 3 (&i)][ 4 (&c)]

      

So this is actually your stack layout without argc

and *argv

. If i

consumes four times more memory than c

in this case. The stack grows in the opposite direction, so if you write something larger than char

before c

, it will write to [4]

and further to the left instead of to the right. So it [5]

will never be written. Instead, you overwrite [3]

. In the case where you write from int

before c

and int

four times more than what c

you actually write on [1][2][3][4]

, it simply [0]

won't be overwritten, but 3/4 of the memory for int

will be corrupted.



On a big end system, the high byte i

will be stored in [3]

and therefore overwritten by this operation. In little-endian system, the most significant byte is stored in [0]

and will be stored. However, you damage your stack this way.

As Ams mentions, this is not always the case. For efficiency, there may be different alignments, or because the platform only supports consistent access, leaving gaps between variables. Also, the compiler is allowed to perform any optimizations as long as it has no visible side effects as stated in the as-if rule. In this case, the variables can be perfectly stored in the register and won't be stored on the stack at all. But many other compiler and platform dependency optimizations can make it more difficult. So this is just the simplest case without regard to platform and compiler optimizer dependencies, and also seems to happen in your particular case with some minor differences.

+3


source


With the help scanf()

you insert int

inside char

. One is char

usually stored using just 1 byte, so your compiler is probably overflowing, but it may or may not overwrite other values ​​depending on the alignment of the variables.

If your compiler reserves 1 byte for char

, and the memory address int

is right after the address char

(it probably will), then yours scanf()

will just overwrite the first bytes i

. If you are in a small end machine and enter values ​​less than 256, then i

it will always be 0.

But it can increase if you enter a higher value. Try entering 256; i

becomes 1.With 512, i

becomes 2, and therefore one.



But you are not "erasing the stack", you are just overwriting a few bytes (you are actually overwriting sizeof(int)

bytes, one of them matches char

and the rest will probably be all bytes in yours int

but one).

If you really want to "erase the stack", you can do something like:

#include <string.h>

int
main(void) {
    char c;

    memset(&c, 0, 10000);

    return 0;
}

      

+3


source


@foobar gave a very good description of what one compiler does on one architecture. It (?) Also provides an answer to what a hypothetical big-endian compiler / system can do.

To be fair, this is probably the most helpful answer, but still not correct.

There is no rule that says that the stack must grow anyway (although, as with the drive to the left or right, a choice must be made, and the most likely is a top-down stack).

There is no rule that says the compiler should map variables on the stack in any way. Standard C doesn't care. Even the official architecture of the / OS-specific ABI doesn't care about this as the bit needed to unwind is working. In this case, the compiler may choose a different schema for each function in the system (not so).

Undoubtedly it scanf

will try to write something int

-sized on char

and that it is undefined. In practice, there are several possible outcomes:

  • It works great, nothing is overwritten, and you're in luck. (Possibly int

    the same size as char

    , or there was an addition on the stack).
  • It overwrites the data following char

    in memory, whatever it is.
  • It overwrites the data immediately before and / or after char

    . (This can happen on architectures where the aligned storage instruction ignores the lower bits of the write address.)
  • It fails with an unusual exception.
  • It detects a line string, prints a message and reports the incident .

Of course, none of this will happen, because you are compiling with it enabled -Wall -Wextra -Werror

, and GCC tells you that your scanf format does not match your variable types.

+3


source


As Kerrek SB commented , the behavior is undefined .

As you know, you are passing char *

to a function scanf

, but you have told the function to handle it as int *

.

It can (albeit very unlikely) overwrite something else on the stack, such i

as the previous stack pointer or return address.

It might just override unused bytes, for example if the compiler uses padding to align the stack.

This can fail, for example, if the address is c

not 4- or 8-byte aligned and the platform requires the de-reference to int to be 4 or 8-byte aligned.

And that might do something else.

But the answer is still - in this case, anything is possible. The behavior is simply undefined.

+2


source







All Articles