C ++ String Overflow - PullRequest
       29

C ++ String Overflow

0 голосов
/ 06 ноября 2011

Вот измененная версия кода со страницы 586 «Начиная с C ++ - от структур управления до объектов, 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;
}

Теперь запустите программу и введите «aaaabba».

Теперь я специально попытался ввести здесь неправильную запись в память. Например. Я заявляю, что размер массива char равен 5, но при появлении запроса введите более 4 (5 минус длина \ 0) символов.

Предполагая, что система выделила память для «буквы» сразу после «userString», из этого следует, что когда я записываю что-то в «letter», она должна перезаписывать соответствующее местоположение в «расширенной» userString.

Итак, память должна выглядеть так: [a] [a] [a] [a] [\ 0] [b] [a] [\ 0].

Затем, когда я запускаю функцию countChars, она, согласно книге, должна останавливаться на символе '\ 0', который находится сразу после первых четырех символов.

По этой логике должно быть выведено, что в строке 4 символа.

На самом деле, программа говорит, что есть 5 а.

Где ошибка в моих рассуждениях?

РЕДАКТИРОВАТЬ # 1: Это НЕ код из книги. Это модифицированный код.

РЕДАКТИРОВАТЬ # 2: Я изменил код специально, чтобы ввести переполнение строки. Я сделал это специально, потому что хочу посмотреть, действительно ли память работает так, как я думаю. Поэтому я хочу услышать убедительное объяснение того, почему ошибка не работает так, как я ожидаю.

РЕДАКТИРОВАТЬ # 3: Компилятор жалуется на поврежденный стек, но я нажимаю "продолжить", потому что я хочу видеть, что происходит.

Спасибо.

Ответы [ 4 ]

2 голосов
/ 06 ноября 2011

Несмотря на то, что вы выделили пространство только для 5 символов, проверки нет, и в результате ваша программа нагло перезаписывает все, что было по адресу после массива.В вашем конкретном случае вы были (не) удачливы и не видели сбоев - но на самом деле это неопределенное поведение.Единственный терминатор NUL находится в конце строки, в которой вы читаете, не в пятой позиции, следовательно, вы видите все a s.Это не правильный способ делать вещи ...

1 голос
/ 06 ноября 2011

Если вам интересно, как ваши переменные в стеке расположены друг относительно друга, почему бы не добавить

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

Как отмечают другие авторы, нет никакой гарантии в отношении какой-либо конкретной компоновки, но приведенное выше, по крайней мере, расскажет вам, как она определяется версией вашего компилятора с использованием ваших настроек оптимизации.

(Предостережение: Zan Lynx совершенно справедливо отмечает, что letter разрешено просто находиться в регистре ЦП, а не в стеке вообще. Однако в приведенной выше строке содержится &letter, что означает, что компилятор должен поместить letter в стек, поскольку регистры не имеют адресов памяти. Таким образом, приведенная выше строка может фактически повлиять на поведение вашей программы, предотвращая оптимизацию компилятора. Вы можете внезапно получить, что всего четыре a!)

1 голос
/ 06 ноября 2011

Компилятор абсолютно не обязан выделять letter после userString.Если вы работаете в режиме отладки, он будет размещать информацию об отладке посередине.Если вы работаете в режиме релиза, он, вероятно, находится в регистре и в стеке может быть что угодно.

1 голос
/ 06 ноября 2011

В C или C ++ не существует правила, согласно которому локальные переменные должны располагаться в любом конкретном порядкеИли вообще быть в стеке вообще.Ваш char может существовать только в регистре процессора.Это может предшествовать массиву.Размер массива может быть дополнен до ближайших 16 байтов, чтобы упростить операции SSE.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...