Если скомпилировать код ниже в Visual Studio x86 (Visual Studio Tools - командная строка разработчика для VS, команда "cl test.c" ) программа скопилируется, успешно выполнится и на консоль будет выведено: Result: Goodbye! (проверялось в 2010 и 2015 версии)
Но если этот код скомпилировать в gcc x86-64 под Linux, то исполняемая программа будет падать в Segmentation Fault.
Убедитесь в этом сами и попробуйте ответить, почему в Linux программа падает? Почему в Windows программа успешно работает? Для поиска ответа могут понадобиться средства отладки.
#include <string.h>
#include <stdio.h>
void copystring(char *s)
{
strcpy (s, "Goodbye!");
printf ("Result: %s\n", s);
};
int main()
{
copystring ("Hello, world!\n");
};
Но если этот код скомпилировать в gcc x86-64 под Linux, то исполняемая программа будет падать в Segmentation Fault.
Убедитесь в этом сами и попробуйте ответить, почему в Linux программа падает? Почему в Windows программа успешно работает? Для поиска ответа могут понадобиться средства отладки.
#include <string.h>
#include <stdio.h>
void copystring(char *s)
{
strcpy (s, "Goodbye!");
printf ("Result: %s\n", s);
};
int main()
{
copystring ("Hello, world!\n");
};
Вопрос в том, почему этот код вызывает ошибку при компиляции gcc и не вызывает ошибку в mcvs! Почему разное поведение? В чем разница подходов компилирования, или, может быть, в способе исполнения бинарного кода?
Другими словами, это не случайно, потому что обладая определенными знаниями, можно совершенно точно предсказать, что в Linux этот код упадет, а в Windows - нет. И человек, который писал этот познавательный учебный пример, именно на такое поведение и рассчитывал.
Да нет. Именно что случайно.
Я согласен, что это вполне вероятная причина такого поведения и это очень похоже на правильный ответ
Кстати, если создать типовой win32 консольный проект в MSVC, и вставить функцию copystring и вызов её в tmain, то программа начинает падать.
У меня вот другой вопрос возник, в связи с этим, возможно глупый, но раз уж тут собрались умные дядья... Зачем вообще UB компилируется, а не выдает ошибку? (в тех случаях, когда это возможно отследить, как в примере, понятно что сложные случаи статическим анализом не отлавливается)
Как говорят другие умные дядьки (которые занимаются этими вопросами в стандарте), если появляется возможность перевести UB из разряда UB в разряд ошибок компиляции - это делается. Но тут тоже надо понимать - совмещать компилятор ещё и со статическим анализатором кода? Гм...
Как говорят другие умные дядьки (которые занимаются этими вопросами в стандарте), если появляется возможность перевести UB из разряда UB в разряд ошибок компиляции - это делается. Но тут тоже надо понимать - совмещать компилятор ещё и со статическим анализатором кода? Гм...
По отдельности код функции легальный, что передача указателя на строку в некоторую функцию, что некоторые действия со строкой в функции, который помечен как char *.
Статические анализаторы выпускаются отдельно сторонними фирмами, и вроде успешно.
> ты лучше объясни: какой смысл искать ответ на вопрос
Не поверишь, но на свете есть люди, которым это может интересно и это достаточная причина, чтобы поискать ответ на этот вопрос.
Собственно, пример взят из этой книги: beginners.re/ . Её целевая аудитория - как раз те, которым интересно.
4.7 тоже не ловит.
Не поверишь, но на свете есть люди, которым это может интересно и это достаточная причина, чтобы поискать ответ на этот вопрос.
Только вот зачем для этого отладчик то использовать? Генерируем из исходника ассемблерный текст и видим (под Linux):
Bingo!
На уровне компилятора отловится разве что преобразование строкового литерала в char* (и то, только крестовым компилятором, где эти литералы const char*).
Большего ему знать и не надо.
То, что строки кладутся в .rodata - это не более, чем поведение конкретного линковщика на конкретной платформе. А если, к примеру, платформа специфическая и там никому не нужна защита страниц от записи, то в сегмент данных будет всё замечательно записываться.
Если предположить, что кому-то не лень будет писать статический анализатор на это самое поведение линковщика на какой-то платформе, то и он отловит только самые тривиальные случаи (попытка записи по известному на этапе компиляции адресу, который находится в защищённой от записи секции). А в общем виде эта задача будет решаться только в рантайме.
Кодогенератора для конкретной платформы.
Энивей, всё что можно было показать (предупреждение на каст) уже было показано. А внутри функции уже корректный указатель, потенциально доступный для записи, а потому кидать предупреждения или ошибки мы не имеем права.
В "крестовом" компиляторе это не const char*, а const char[]. Немного разные вещи. А в С (начиная с C99) строковый литерал определяется как массив char'ов с static-модификатором.