akanevsky
akanevsky

Reputation: 1039

C++ String Overflow

Here's a modified version of code from page 586 of "Starting Out With C++ - From Control Structures through Objects, 6e":

#include <iostream>
using namespace std;

int countChars(char *, char);

int main()
{
    const int SIZE = 5;
    char userString[SIZE];
    char letter;

    cout << "Enter a string: ";
    cin.getline(userString, 10);

    letter = '\0';
    cout << "a appears ";
    cout << countChars(userString, 'a') << " times.\n";

    cin >> letter;
    return 0;
}

int countChars(char *strPtr, char ch)
{
    int times = 0;
    while (*strPtr != '\0')
    {
        if (*strPtr == ch)
            times++;
        strPtr++;
    }
    return times;
}

Now run the program and enter "aaaabba".

Now, I have specifically tried to introduce incorrect writing to memory here. E.g. I declare that the char array size is 5, but enter more than 4 (5 minus the length of \0) characters when prompted.

Assuming that the system allocated memory for "letter" right after "userString", then it follows that when I write something to "letter" it should overwrite the corresponding location in the "extended" userString.

So the memory should look like this: [a][a][a][a][\0][b][a][\0].

Then when I run the countChars function, it, according to the book, should stop at the '\0' character, which is right after the first four a's.

By this logic, it should output that there are 4 a's in the string.

In reality, the program says there are 5 a's.

Where is the error in my reasoning?

EDIT #1: This is NOT a code from the book. It's MODIFIED code.

EDIT #2: I changed the code specifically to introduce a string overflow. I did this on purpose because I want to see if the memory actually works the way I think it does. So what I want to hear is a solid explanation about why the bug doesn't work as I expect it to.

EDIT #3: The compiler does complain about corrupted stack, but I press "continue" because I want to see what happens.

Thank you.

Upvotes: 0

Views: 307

Answers (4)

ruakh
ruakh

Reputation: 183564

If you're wondering how your stack variables are laid out relative to each other, why not throw in a

cout << ((int)userString) << endl << ((int)&letter) << endl;

?

As other answerers have pointed out, there's no guarantee of any of specific layout, but the above will at least tell you how it's laid out by your compiler version using your optimization settings.

(Caveat: Zan Lynx mentions, quite rightly, that letter is allowed to just be in a CPU register, and not on the stack at all. However, the above line includes &letter, which means that the compiler has to put letter on the stack, since registers don't have memory addresses. So the above line could actually affect the behavior of your program, by preventing that compiler optimization. You might suddenly get that there are only four a's!)

Upvotes: 1

Puppy
Puppy

Reputation: 147036

The compiler has absolutely no obligation to allocate letter after userString. If you're running in debug mode, it'll allocate debugging information in the middle. If you're running in release mode, it's probably in a register and there could be anything on the stack.

Upvotes: 1

Zan Lynx
Zan Lynx

Reputation: 54363

There is no rule in C or C++ that local variables be allocated in any particular order. Or even be on the stack at all. Your char may exist only in a CPU register. It might come before the array. The array size might be padded to the nearest 16 bytes to make things easier for SSE operations.

Upvotes: 1

Nim
Nim

Reputation: 33655

Even though you've only allocated space for 5 characters, there is no check, and as a result your program brazenly overwrites whatever was at the address after your array. In your particular case, you were (un)lucky and did not see a crash - but in reality this is undefined behaviour. The only NUL terminator is at the end of the string your read in not at the fifth position, hence you see all the as. This is not the correct way to do things...

Upvotes: 2

Related Questions