Размер массива назначения strcat

Возьмите следующую программу:

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    char a[8] = "Hello, ";
    char b[7] = "world!";

    strcat(a, b);

    cout << a;

    return 0;
}

Обратите внимание, что a и b имеют тот же размер, что и назначенные им строки.

В документации указано, что для работы strcat(a, b) размер a должен быть большим. достаточно, чтобы содержать конкатенированную результирующую строку.

Тем не менее, cout << a отображает "Hello, world!". Я вхожу в неопределенное поведение?


person wjm    schedule 07.10.2013    source источник


Ответы (5)


«Я вхожу в неопределенное поведение?»

Да. Область в конце [] была перезаписана. На этот раз это сработало, но могло принадлежать чему-то другому.

Здесь я использую структуру для управления расположением памяти и демонстрирую это:

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    struct S {
        char a[8];
        char b[5];
        char c[7];
    };

    S s;
    strcpy( s.a , "Hello, " );
    strcpy( s.b , "Foo!" );
    strcpy( s.c , "world!" );


    strcat(s.a, s.c);

    cout << s.a << endl;
    cout << s.b << endl;

    cin.get();

    return 0;
}

Это выводит:

Hello, world!
orld!

Вместо:

Hello, world!
Foo!

Функция strcat() растоптала b[].

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

РЕДАКТИРОВАТЬ: Могу ли я также порекомендовать вам вместо этого использовать strcat_s? Или, что еще лучше, std::strings:

#include <string>
#include <iostream>

using namespace std;

int main()
{
    string a = "Hello, ";
    string b = "world!";
    a = a + b;
    cout << a;
}
person Arkady    schedule 07.10.2013
comment
@Nawaz, какой у тебя хороший вопрос? Способствует ли оно развитию ваших знаний? В любом случае... спасибо, Аркадий, за несамовлюбленный, обстоятельный ответ. - person wjm; 07.10.2013
comment
@JosuéMolina: Спросите себя: что для вас не очень хороший вопрос? - person Nawaz; 07.10.2013
comment
@ Наваз, я не хочу вступать с тобой в субъективный спор. - person wjm; 07.10.2013
comment
@JosueMolina: Отлично. Теперь вы знаете, что это субъективно. :-) - person Nawaz; 07.10.2013
comment
@Nawaz, тогда держи это при себе в следующий раз. - person wjm; 07.10.2013
comment
@JosuéMolina: Сначала скажи это себе. (Обратите внимание: несмотря на то, что вопрос не очень хороший, я проголосовал за него в знак поддержки, но, читая ваши комментарии, кажется, вам нравится, когда вас хвалят за все, что вы делаете.) - person Nawaz; 07.10.2013
comment
@Nawaz, я проголосовал за ваш ответ, потому что он поучительный; это сделало меня лучшим программистом на C/C++. Тем не менее, ваш последующий комментарий порицал мою неопытность. Я хочу учиться и чувствовать поддержку; если бы я искал похвалы, у меня было бы больше ответов, чем вопросов на этом сайте. - person wjm; 07.10.2013

Я вхожу в неопределенное поведение?

Да.


Если в документации сказано "a должен быть достаточно большим, чтобы вместить объединенную результирующую строку", почему бы вам просто не поверить этому? Что тут сомневаться?

person Nawaz    schedule 07.10.2013

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

person nickie    schedule 07.10.2013
comment
Это может работать, на некоторое время. Я видел, как эта ошибка работала нормально, пока вызов std::vector‹int›::push_back() не терпел неудачу на полпути цикла в совершенно другом исходном файле. Оказывается, проблема была именно в этом типе ошибки: часть вектора перезаписывалась плохой функцией strcat(). - person Arkady; 07.10.2013

strcat делает то, что написано на банке, а именно. копирует b в конец a, не заботясь о том, какие данные там уже есть. Поскольку обе переменные находятся в стеке одна за другой, все работает. Но попробуй

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    char a[8] = "Hello, ";
    int i =  10;
    char b[7] = "world!";

    strcat(a, b);

    cout << a << i;

return 0;
}

И вы, вероятно, получите неожиданный результат, так как ваш стек был поврежден strcat

person doron    schedule 07.10.2013

Это правильно... Поведение не определено. Тот факт, что вы получили этот ответ, не означает, что в следующий раз он не рухнет, поскольку массив a слишком мал.

person Jim    schedule 07.10.2013