strcpy работает нормально, хотя память не выделена

Ниже программа на С++ работает нормально, хотя я не выделил памяти для chr. Я просмотрел google, SO и наткнулся на это Почему это преднамеренно неправильное использование strcpy не приводит к ужасным сбоям?

Здесь программа отлично работает для пункта назначения, который имеет меньше места, чем источник. Применимо ли это и к моему случаю, strcpy записывает в случайное место в куче?

#include<iostream>
using namespace std;

class Mystring
{
    char *chr;
    int a;
    public:
    Mystring(){}
    Mystring(char *str,int i);
    void Display();

};

Mystring::Mystring(char *str, int i)
{
   strcpy(chr,str);
   a = i;
}

void Mystring::Display()
{
    cout<<chr<<endl;
    cout<<a<<endl;
}


int main()
{
    Mystring a("Hello world",10);
    a.Display();
    return 0;
}
output:-
Hello world
10

Я попробовал то же самое с другой программой на С++, с любым классом и членом класса, и я смог увидеть сбой.

#include<iostream>
using namespace std;

int main()
{
    char *src = "Hello world";
    char *dst;
    strcpy(dst,src);
    cout<<dst<<endl;
    return 0;
}

Пожалуйста, помогите мне понять поведение в первой программе на С++. Выделяется ли каким-то образом память или strcpy пишет в какую-то случайную ячейку памяти.


person Rndp13    schedule 24.06.2015    source источник
comment
Это просто неопределенное поведение, ничего не будет выделено автоматически.   -  person πάντα ῥεῖ    schedule 24.06.2015
comment
Это не нормально, вы просто не видите проблемы. Пока что. И это самая ужасная ошибка, потому что вы можете не осознавать такую ​​​​ошибку, пока не произойдет что-то действительно плохое (не в этой игрушечной программе, а в реальном мире... потеря всех данных, что-то не так с деньгами и т. д. и т. д.).   -  person deviantfan    schedule 24.06.2015
comment
Это похоже на запись в какое-то случайное место в памяти? Если это так, что, если chr указывает только на это место? Пожалуйста, помогите мне понять это.   -  person Rndp13    schedule 24.06.2015
comment
Is it like writing into some random memory location? Да.   -  person deviantfan    schedule 24.06.2015


Ответы (4)


Поведение вашей программы не определено стандартом C++.

Это означает, что любая реализация C++ (например, компилятор) может делать все, что захочет. Печать "привет!" на стандартный вывод был бы возможным результатом. Форматирование жесткого диска по-прежнему допустимо, если речь идет о стандарте C++. Однако на практике часть случайной памяти в куче будет перезаписана с непредсказуемыми последствиями.

Обратите внимание, что программе даже разрешено вести себя так, как ожидалось. На данный момент. Если только не пятница. Или до тех пор, пока вы не измените что-то совершенно не связанное. Это то, что произошло в вашем случае, и это одно из худших событий с точки зрения программиста.

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

Из-за этого вы никогда не должны полагаться на неопределенное поведение.

person chi    schedule 24.06.2015
comment
Будет ли доставка пиццы приемлемым результатом? Я знаю, что буду собирать сегодня вечером! - person Mercious; 24.06.2015
comment
@Мерсиус Конечно. Но если это когда-нибудь произойдет, стандарт C++ не дает никаких гарантий того, что будет на пицце. Последствия его употребления в пищу, конечно, не определены. - person chi; 24.06.2015
comment
Я понимаю. Я представляю, что это будет веселая поездка. Кому нужны наркотики, когда у тебя есть УБ на С++! - person Mercious; 24.06.2015
comment
Он также должен иметь неуказанную пищевую ценность. - person Lightness Races in Orbit; 25.06.2015

strcpy() действительно пишет в случайное место.

И это слепая удача, если ваша программа работает нормально и ничего не падает.

Вы создали объект класса MyString в стеке. В этом объекте есть указатель члена chr, указывающий на произвольное место. Заботится ли ваш конструктор об инициализации этого указателя или о выделении памяти для указателя, на который он указывает? -- Нет, это не так. Итак, chr куда-то указывает.

strcpy(), в свою очередь, не заботится о валидности указателя, он доверяет вашему профессионализму в обеспечении корректного ввода. Таким образом, он выполняет свою работу по копированию укусов. К счастью, перезапись памяти в месте, указанном неинициализированным chr, не приводит к сбою вашей программы, но это только "к счастью".

person Igor S.K.    schedule 24.06.2015

Известно, что функция strcpy() может вызывать ошибки переполнения, потому что не выполняется проверка того, поместятся ли данные в новый массив или нет. Результат этого переполнения может быть когда-то никогда не замечен, все зависит от того, куда записываются данные. Однако частым результатом является повреждение кучи и/или программы.

Безопасной альтернативой strcpy() является использование strcpy_s(), для которого также требуется размер массива. Подробнее об использовании strcpy_s() можно прочитать в MSDN или здесь

person A B    schedule 24.06.2015
comment
Не все используют микродерьмо. - person πάντα ῥεῖ; 24.06.2015
comment
@πάνταῥεῖ ῥεῖ Ну, strcpy_s() — это стандартная функция C++11, поэтому она не связана с Microsoft. Однако я добавил дополнительный источник. - person A B; 24.06.2015
comment
@AB - Невозможно подтвердить - откуда взялась эта идея быть стандартной функцией? Ни на cppreference.com, ни на cplusplus.com его нет. - person 2785528; 24.06.2015
comment
en.cppreference.com/w/c/string/byte/strcpy имеет strcpy_s (хотя это C 11) - person Mike Vine; 24.06.2015
comment
Он был представлен в стандарте C++ 11 (ISO/IEC 9899:2011). Дополнительные сведения см. по моей второй ссылке. - person A B; 24.06.2015

На самом деле strcpy() делает что-то вроде этого:

char *strcpy(char *dest, const char *src)
{
  unsigned i;
  for (i=0; src[i] != '\0'; ++i)
    dest[i] = src[i];
  dest[i] = '\0';
  return dest;
}

Таким образом, когда вы передаете указатель на некоторый массив символов в strcpy, он копирует данные из src в dest, пока не достигнет NULL завершающего символа.
Указатель символа не содержит никакой информации о длине строки, поэтому, когда вы передаете указатель dest, он копирует данные, даже если вы не выделили ему память.

Запустите этот пример кода, вы поймете мою точку зрения:

#include <cstring>
#include <iostream>

using namespace std;

int main()
{
    char str1[] = "Hello_World!";
    char str2[5];
    char str3[10];
    strcpy(str2,str1);
    cout << "string 1:" << str1 << endl;
    cout << "string 2:" << str2 << endl;
    cout << "string 3:" << str3 << endl;
    return 0;
}

Это не покажет никакой ошибки, но вы можете понять на моем примере, что это не очень хорошая практика.

person Milan Patel    schedule 24.06.2015