malioboro
malioboro

Reputation: 3281

Why I can use array that initiated with 0 size: Char ch[0];

This is my code:

#include<iostream>
using namespace std;
int main(){
    char ch[0];
    cin >> ch;
    cout << ch;
    return 0;
}

input1:

abcdefghijklmnopqrstuvwxyza

output1:

abcdefghijklmnopqrstuvwxyza

(working fine, but I don't know why)

input2:

abcdefghijklmnopqrstuvwxyzab

output2:

abcdefghijklmnopqrstuvwxyzab_

(request an input)

input3:

abcdefghijklmnopqrstuvwxyzabc

output3: (runtime error)

when output2 request an input, and we put input2, the output is same output2 (with request an input again), and output1 or output2 will appear too when we put input1 or input2 in there

Can someone explain this phenomenon? Why it happens?

Upvotes: 1

Views: 1405

Answers (6)

Valeri Atamaniouk
Valeri Atamaniouk

Reputation: 5163

You are getting classical buffer overrun problem. Though it is not compliant to define array of zero size, what is happening, is that you are getting a variable on the stack with the size of default alignment (4 or 8 usually).

So when you start reading into that variable, you put data on the stack, and it starts overriding your stack frame. This is not too visible immediately, however it destroys you returns address.

So the code executes read (fine), and then write (which is is also fine), and then tries to return. If the return address has been destroyed, you get segmentation fault. Otherwise this may continue unnoticed. The reason you don't get output in the last example, is due to buffered output - the program doesn't get a change to flush the buffers before quit (as it is done after return from main).

Here is an example, how you can see your code overrides the stack data:

int main(int argc, const char *argv[])
{
    int dummy1 = 0xCDCDCDCDCDCDCDCD;
    int dummy2 = 0xCDCDCDCDCDCDCDCD;

    char badvar[0];

    cin >> badvar;
    cout << badvar << endl;
    cout << dummy1 << endl;
    cout << dummy2 << endl;
    cout << flush;

    return 0;
} 

Upvotes: 0

cppguy
cppguy

Reputation: 3713

Most compilers will just set the array to have a real start address but will not take any space so subsequent variables on the stack or in a structure would start at the same address they would have if the 0 sized array didn't exist at all. This is quite often used as a last member of a structure where the structure is padded when allocated by n bytes. The array being the last member can then be indexed to access that padding without pointer math.

eg.

struct foo
{
    int a;
    int b;
    char c[0];
};

foo* f = malloc(sizeof(foo) + 50);

for (int i = 0; i < 50; ++i)
    f->c[i] = 57;

sizeof foo is most likely 8 but it doesn't matter because c is the end address of that structure, regardless of how the structure is byte aligned/padded.

Some Win32 API's leverage this.

Upvotes: 1

Nik Bougalis
Nik Bougalis

Reputation: 10613

As stfrabbit points out the standard explicitly forbids the declaration of an array of size zero. But gcc allows this sort of thing as an extension for reasons which we won't get into.

So what's happening? Well, when it's time to look for acceptable overloads of operator>> and operator<< the compiler treats char ch[0] as char[] which then degenerates into char *.

It finds an overload for char * and invokes it. So you are now thrashing random memory (starting at the address of ch which is is who-knows-what). If you weren't in undefined behavior land before, you're in undefined behavior land now.

And once you're in undefined behavior land, anything goes: the program could cause the Universe to start contracting or it could cause one... hundred... million... dollars to magically appear in your bank account.

Or it could just crash.

Upvotes: 2

Tony Hopkinson
Tony Hopkinson

Reputation: 20320

Basically char ch[0]; is a defined address in memory. You pass it to Cin It starts writing at characters from input to it. You then pass that same address and it tries to make sense of it. Essentially print every character until it runs in to a 0.

You'd have to do some deep investigation as to why you are seeing this behaviour, but it would be a complete waste of time. The behaviour is undefined theres no guarantee it's in any way repeatable.

Upvotes: 1

Ibrar Ahmed
Ibrar Ahmed

Reputation: 1089

You need to have array which can store the input value, this definitely cannot handle.

char ch[0];

This definitely a Segmentation fault.

$./a.out 
12345678901234567890
Segmentation fault (core dumped)

You need to define ch as big as you required as input.

Upvotes: 0

Joseph Mansfield
Joseph Mansfield

Reputation: 110698

An array of size 0 is not valid:

If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero.

If your compiler accepts it, it is merely a non-standard extension. GCC accepts it but will issue a diagnostic if you add the -pedantic option:

warning: ISO C++ forbids zero-size array ‘arr’ [-pedantic]

Nonetheless, reading into an non-standard array of zero size will undoubtedly give you undefined behaviour.

Upvotes: 12

Related Questions