2013-03-11 3 views
5

Я смущен, почему компилятор даетвремени компиляции ошибки против Run Time Error

const char s[]="hello"; 
s[2]='t';     // Compile Time Error 

char *t = "hello"; 
*(t+2)='u';    // Run time Error 

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

+0

* «Думаю, в обоих случаях компилятор должен дать ошибку времени компиляции». * - Это должно, но как это могло быть? Вы назначаете значение неконстантному объекту, что является совершенно законной операцией. Как компилятор знает, что 't' указывает на объект const? –

ответ

6

В первом случае вы пишете const, и компилятор замечает это и может отклонить это.

Во втором случае t является указателем на неконстантный char, поэтому вы можете разыменовать его и написать по адресу *(t+2). Однако, поскольку t инициализируется указателем на сегмент только для чтения, во время выполнения вы получаете нарушение сегментации.

Вы можете болезненно настроить компоновщик, чтобы поместить все данные в записываемые сегменты. Это уродливое и нестандартное.

P.S. Некоторые сложные статические анализаторы (возможно, Frama-C) могут захватывать обе ошибки без запуска программы. Можно также представить, что расширение GCC, например. с MELT, чтобы добавить такие проверки (но это нетривиальная работа, и для нее может быть сложно получить финансирование).

6

Обратная совместимость.

Вы не можете изменить символ const. Это очевидно.

Что не очевидно, так это то, что тип строкового литерала на самом деле является указателем на постоянные символы, а не на указатель на символы. Следовательно, вторая декларация имеет неправильный тип. Однако это поддерживается по историческим причинам.


Обратите внимание, что приведенное выше является ложью. Вместо указателей строковые литералы на самом деле являются типами char[].

В частности, тип строкового литерала - это char[], а не const char[] в C89 и C99 [и я думаю, C11, но не уверен, хотя]. Это не на самом деле неправильно, но данные хранятся в сегменте только для чтения, поэтому это неопределенное поведение, чтобы попытаться записать на него.


Кроме того, для чего это стоит, вы можете использовать -Wwrite-strings с GCC (г ++ уже включает его), чтобы быть предупреждены об этом.


Больше информации here и here.

+0

Это ключевой момент - вторая декларация на самом деле не имеет смысла, но разрешена. –

+0

Я не буду говорить, что второе объявление ошибочно напечатано. Бывают случаи, когда выборка из 'const' в неконстантные данные имеет смысл. –

+2

Только для записи это преобразование больше не поддерживается в C++. –

2

Рассмотрим следующую серию утверждений:

char *t = "hello"; 
char s[5]; 
t = s; 
*(t+2)='u'; 

Эта серия заявлений не даст ошибку во время выполнения, так как оператор *(t+2)='u'; не недействителен. Он пытается изменить местоположение памяти const (только для чтения) в вашем случае, но компилятор не знает, произойдет ли нарушение доступа.

3

Когда вы это сделаете: char *t = "hello"; then t - указатель, указывающий на память, находящуюся в части кода, поэтому вы не можете ее изменить.Поскольку это только для чтения, вы получаете ошибку сегментации во время выполнения.

Когда вы делаете: const char s[]="hello"; то s представляет собой массив символов, которые в стеке, но это const, так что вы не можете изменить его, и вы получите ошибку компиляции (компилятор знает, что это const, поэтому он не позволяет вам его изменять).

Использование const, когда вы не хотите, чтобы ваша строка была изменена, хороша тем, что возникает ошибка компиляции, которая лучше, чем ошибка времени выполнения.

Смежные вопросы