0

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.

3
  • Marlon and Kerrek SB, thanks for the tip. As for tossing the book, I don't think, it's a very good book and I highly recommend it. As I have taken a computer architecture class in the past and know stuff about how memory works in general, I want to test if what I know applies to C++. Commented Nov 6, 2011 at 2:09
  • 1
    @akanevsky: If you're a newcomer to C++ and you're dealing with char pointers, you're Doing It Wrong. If you got the idea from a book, it's time to look for another book. I'm not saying that char pointers don't have their place; they're just not The Right Way to think about C++ in a tutorial or introductory context. Commented Nov 6, 2011 at 2:13
  • As I already explained, I'm doing it wrong on purpose. I wanted to test the preamble to the code that states "If the string address of a string is passed into a pointer parameter variable, it can be assumed that all the characters, from that address up to the byte that holds the null terminator, are part of the string." So basically, what I wanted to do is create a situation where a string would be written beyond its bounds and then terminate it like I did above and check if the calculation still works. I just got another idea - remove the ending terminator and see what happens. Commented Nov 6, 2011 at 2:18

4 Answers 4

2

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...

Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

Right, the compiler can do optimizations and allocate variable in a different order than written. I forgot. Thanks.
1

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.

1 Comment

Right, the compiler can do optimizations and allocate variable in a different order than written. I forgot. Thanks.
1

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!)

2 Comments

Actually, I'm now getting a crash if I enter too many characters! If I don't, the output shows the memory allocated to letter is actually before userString. This also shows that you and the above posters are right, the compiler at first didn't even put the variable on the stack. Which is exactly what I needed to see. Thank you very much.
A void* cast would be vastly more appropriate in this situation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.