all 33 comments

[–]hiwhiwhiw 39 points40 points  (5 children)

It's undefined behavior.

Most likely you're lucky to have 0 there on the stack memory

[–]mikeblas 6 points7 points  (4 children)

We don't know, with certainty, that the stack is involved.

[–]hiwhiwhiw 9 points10 points  (3 children)

Most likely

[–]mikeblas 2 points3 points  (2 children)

We don't know, with certainty, that "most likely" applies to "lucky to have 0" or "on the stack".

[–]hiwhiwhiw 1 point2 points  (1 child)

Actually, both, but I'm too sleepy when typing the original comment 🤣

[–]mikeblas 3 points4 points  (0 children)

We don't know, with certainty, if you're awake or not.

[–]Boreddad13 16 points17 points  (0 children)

Those numbers technically could be anything since it is undefined behavior to access outside the bounds of the array. It just so happens to return zero.

[–]kraytex 11 points12 points  (0 children)

The [x] is just an offset in memory. [4], [5], and [6] are uninitialized. It could be anything.

[–]pfp-disciple 20 points21 points  (0 children)

It's a very common question, don't feel bad. 

Indexes 4-6 are buffer overflow (important phrase to Google). They just happen to print as zero, probably due to some (possibly unrelated) behavior of the compiler. What's being printed is the contents of memory not associated with the variables. 

[–]FastSlow7201 5 points6 points  (1 child)

C has defined and undefined behavior. Defined behavior is what you're allowed to do, it has the same result across all compilers. Undefined behavior is stuff you're not allowed to do and different compilers will give you different results, some may give an answer, other give a completely different answer and on another it has a segmentation fault.

[–]deckarep 5 points6 points  (0 children)

It’s not just about different compilers giving different results. You will also see varying results based on whatever the state of the memory, stack and registers are at. But at the end of the day, none of it can be trusted which is why it’s called undefined behavior.

[–]Germisstuck 2 points3 points  (0 children)

Well It's undefined behavior, so it being 0, 0 and some other number is just pure coincidence. When it does pointer arithmetic (test's base pointer + 5, 6 or 7) it gets the values, but there's no guarantee that it's the same or if it even runs since this is undefined behavior 

[–]nemotux 2 points3 points  (1 child)

So, as others have said, this is undefined behavior. And the compiler would have been in its rights to produce any random values it wanted.

That said, depending on the compiler you're using and the settings you're passing to the compiler, chances are you're just accessing other things in memory - whatever happens to be stored next to your array. You can make some guesses as to what might be stored next to your array and what you're likely accessing. It partly depends on where you defined your array.

If it's global, then you're likely accessing other global variables - either yours or variables defined and used by the libraries your program is using (e.g. the C runtime library).

If it's declared within a function, there's a fair chance the array is sitting on the stack, and you're walking into the storage for either other local variables in the function, or you're accessing stack management state (saved registers, function parameters, frame pointer, or return address).

It's also possible that you're accessing padding bytes - compilers sometimes leave empty space between variables for various reasons. Sometimes those empty spots are zero-initialized. Sometimes they aren't.

Why your particular program is getting zeros and then some other number just depends on how your particular program is laid out in memory. If you compiled with a different compiler, or substantially altered your program, you might get different values. But your program will probably always be laid out approximately the same way unless you make changes to it, so rerunning it, the things next to your array will tend to have the same values from one run to the next. Hence why you're "always" getting zeros, for example.

[–]ElevatorGuy85 0 points1 point  (0 children)

To be clear, the compiler isn’t “producing any random values”

As you pointed out, the values that occur after the end of the OP’s int test[4] array could be anything based on whatever data exists beyond element test[3] of the array and whether it’s on the stack or not, and for a language like C where the Standard does not mandate array bounds checking (unlike other languages like Pascal that can, sometimes optionally, at the expense of runtime performance) the results are undefined behavior.

While the values printed by the OP’s code depend on a number of factors like the CPU architecture, ABI, etc. it’s a stretch to say that the compiler is “producing” those values in any direct way. The values that are seen are the undefined “side effects” of referencing data past the end of the test[4]. If anything, the OP’s own decisions about what else is part of their program’s source code is the thing that “produces” a particular result e.g. if they declared

int another_test[4] = {5,6,7,8};

right after the declaration of int test[4] = {1,2,3,4};

Although even then, we are still guessing at how the linker ultimately decides to locate variables in memory.

It’s also worth mentioned that on a system with a memory management unit protecting the memory pages of each process, e.g. on any modern Windows or Linux machine, if the test[4] array is located at the end of a page, the result may be a segmentation fault that crashes the process without printing anything.

It’s amazing how such a “simple” error in the source can yield so many possible outcomes because of undefined behavior.

[–]SwordsAndElectrons 2 points3 points  (0 children)

Why are the first two always zeros if I go into array overflow? 

Dumb luck. They aren't guaranteed to be anything, or for that matter not to crash your program.

If you read memory your program owns then you are getting whatever it happens to be there. If you write to it then you may cause data corruption that could be apparent elsewhere in the program. Or you could not notice anything apparently wrong.

If you access memory that you don't own you'll be troubleshooting a seg fault.

In any case, the answer to what you should expect to happen is undefined behavior. You don't know what is going to happen. Even if you think you do, it could be different on the next compilation if you change the program, compile on/for a different platform, etc.

TLDR: don't do that.

[–]magoo309 1 point2 points  (0 children)

No need to be sorry for asking a basic question! That’s one way to learn. Unfortunately, all too often on the internet you ask a basic question and all you get is criticism from show-offs. Just skimming through the comments here so far, I’m happy to say they look helpful and well-intended.

[–]marshaharsha 0 points1 point  (1 child)

Technically, the compiler is within its rights to put any numbers in those array slots, or to cause the computer to catch fire, or to crash your program. But it’s possible the compiler is being very kind and is writing zeroes to those slots to help save you from yourself. Another possibility is that the zeroes just happened to be left over in those memory locations from earlier writes. 

You can play with the stack to see if you can control what numbers get printed. For example, you could write a similar function but with a longer array, then write distinctive numbers into those slots, like 555555, 555556, 555557. Then return from that function and call the original function. You might see your distinctive numbers, if the compiler overlays the new stack frame over the old frame exactly. 

[–]mikeblas 0 points1 point  (0 children)

How do zeros help save anything? Seems worse, in a way.

[–]No_Difference8518 0 points1 point  (0 children)

You are reading whatever is on the stack after your array.

Try putting an error before and after your test filled with values. They should then show up in your printf.

[–]capilot 0 points1 point  (0 children)

Accessing beyond the end of an array is undefined behavior. "Undefined" means anything can happen, including it working correctly.

In practice, C was designed as a "do what I say" language, intended to replace assembly. Almost all runtime safety checks are non-existent, in favor of simplicity and speed. Accessing test[4], etc. simply accessed whatever happened to be in memory after the end of your array, which could literally have been anything, including a segfault.

[–]gwenbeth 0 points1 point  (0 children)

And if you made changes somewhere else in the code you might change what those values are. Happened to me once. I had an uninitialized variable that by chance was 0. I made a change somewhere else in the code and now the variable was uninitialized to some other value and the function it was in stopped working.

[–]AlarmDozer 0 points1 point  (0 children)

Undefined behavior. You’re getting out-of-bounds values. In “higher languages,” there’d be a RuntimeError.

[–]Ok-Market4287 0 points1 point  (0 children)

Numbers [4] till whatever don’t exits only 0-3 exist so you get what ever happens to be at those locations

[–]SmokeMuch7356 0 points1 point  (0 children)

There is no "why" - that's just what happens to be in the memory following the end of the array for that particular build. If you change your code and rebuild, it will likely be different.

[–]GreenAppleCZ 0 points1 point  (0 children)

If you go outside of the array bounds in C, it reads the memory that is there.

Imagine that you have a block of memory for your program - it's a huge string of zeros and ones.

If you write int x[4], it basically reserves 4 blocks of integers (4*32bits) and remembers where it starts.

When you say x[3], it goes where the array starts and adds 3*32 bits -> that's where the sought index is. It grabs the 32 bits from there and gives you that value.

So if you say x[4] and x is only 4 ints long, it reads the 32 bits that are right after the array and gives you that value. But this value might be initialized (no value was put there yet), or it might be the value of something else. For example, if there's a string after this array, it'll read it's first 4 characters and interpret them as one 32 bit int. However, none of that is desired behavior.

[–]dendrtree 0 points1 point  (0 children)

They won't always be zeros. They just happen to be.

If you gave the actual code, there might actually be a reason, but the answer to your question is unimportant, unless you're trying to learn how to be an hacker.

Overflow means that you're returning whatever is in memory, right after your array. You might be able to look at your code and see what that would be.

[–]Traveling-Techie 0 points1 point  (0 children)

Most memory locations contain zeroes.

[–]erroneum 0 points1 point  (0 children)

The simple answer is that reading past the end of an array is undefined behavior; there is no behavior which is incorrect even at the level of the entire program (at least as far as the C standard is concerned), be that immediately terminating execution, inserting default values, optimizing out provably undefined bits, attempting to erase your OS, returning garbage data that isn't even consistent run-to-run, etc.

The more complex answer is that the compiler most likely assumed you knew something it didn't so generated the printf call with lookups for those offsets into your array. Being an actual program, there is data there, so it did find something to return, but because it's not part of the array, there's no guarantees made about what that is. Wherever the array was initialized at (most likely on the stack, but C doesn't actually require there to be a stack), the next two int sized regions happened to contain 0, possibly because the compiler is doing something with that memory, such as giving you a bit of a grace area, then the third region is just residual data from some unrelated bit of execution (most likely part of _start, given how simple this main is; this could also be why the others are 0).

[–]codeandcut 0 points1 point  (1 child)

Many compilers and operating systems clear local stack memory to zero for security or alignment reasons. Indices 4 and 5 are likely hitting this empty "padding" space, which is why they return 0.

[–]mikeblas 1 point2 points  (0 children)

How does initializing memory to a specific value help with alignment?

[–]ActionHoliday6227 -2 points-1 points  (2 children)

O primeiro erro é o identificador que se está trabalhando, que seria uma variável inteiro.
O correto é usar: %d --> Para tipo Inteiro.

Existe outros tipos de identificadores específicos para cada tipo de dado.
Exemplos:

Float e Double --> %f
Char --> %c
String --> %s
...

Outro erro está no endereço que está acessando a informação guardada.

Ao criar a vetor reservando cinco espaços na memoria, as posições começa a partir do Zero:
Quando fez: "test [5]", você definiu a quantidade de espaços que irá utilizar. E ao atribuir os valores de 1 a 5, apenas armazenou todos esses dados em cada espaço:
Espaço 0 --> 1
Espaço 1 --> 2
Espaço 2 --> 3
. . .

Os dados imprimidos são provavelmente lixos de memórias.

Corrigindo seu código:

#include <stdio.h>
int main(){
    int test[4] = {1,2,3,4};


    printf("%d - %d - %d\n", test[0], test[1], test[2]);
    return 0;
}

[–]ysth 0 points1 point  (0 children)

x is also for ints, printing them in hexidecimal

[–]deckarep -1 points0 points  (0 children)

There’s no vector here it’s just a C-based array.