Почему я могу изменить содержимое const char * ptr? - PullRequest
9 голосов
/ 12 июля 2010

Я передал указатель ptr функции, прототип которой принимает его как const.

foo( const char  *str );

Что, по моему пониманию, означает, что он не сможет изменить содержимое ptr прошло.Как и в случае foo( const int i ).Если foo() пытается изменить значение i, компилятор выдает ошибку.Но здесь я вижу, что он может легко изменить содержимое ptr.Пожалуйста, взгляните на следующий код

foo( const char  *str )
{
        strcpy( str, "ABC" ) ;
        printf( "%s(): %s\n" , __func__ , str ) ;
}

main()
{
        char ptr[  ] = "Its just to fill the space" ;
        printf( "%s(): %s\n" , __func__ , ptr ) ;
        foo( const ptr ) ;
        printf( "%s(): %s\n" , __func__ , ptr ) ;
        return;
}

При компиляции я получаю только предупреждение, без ошибок:

warning: passing argument 1 of ‘strcpy’ discards qualifiers from pointer target type

и когда я его запускаю,Я получаю вывод вместо Segmentation Fault

main (): он просто заполняет пространствоfoo (): ABCmain (): ABC

Теперь мои вопросы 1- Что на самом деле означает const char *str в прототипе?Означает ли это, что функция не может изменить содержимое str?Если это так, то почему вышеприведенная программа меняет значение? 2- Как я могу убедиться, что содержимое указателя, который я передал, не будет изменено?

Из "содержимого указателя" в приведенном выше вопросе я имею в виду "содержимое"памяти, на которую указывает указатель ", а не" адрес, содержащийся в указателе ".

Редактировать

В большинстве ответов говорится, что это из-за strcpy иC неявным преобразованием типов.Но теперь я попробовал это

foo( const char  *str )
{
        str = "Tim" ;
//      strcpy( str, "ABC" ) ;
        printf( "%s(): %s\n" , __func__ , str ) ;
}

На этот раз вывод без предупреждения от компилятора

main (): он просто заполняет пространствоfoo (): Тимmain (): просто заполнить пространство

Таким образом, очевидно, что память, на которую указывает str, изменяется на область памяти, содержащую "Tim", в то время как она находится в foo().Хотя я не использовал strcpy() на этот раз.Разве const не должно это остановить?или мое понимание неверно?

Мне кажется, что даже с const я могу изменить ссылку на память и содержание ссылки на память тоже.Тогда какой смысл?

Можете ли вы привести пример, когда complier выдаст ошибку, что я пытаюсь изменить указатель const?

Спасибо всем вам за ваше время и усилия.

Ответы [ 5 ]

17 голосов
/ 12 июля 2010

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

Проблема в том, что C очень слаб с преобразованиями типов. strcpy принимает указатель на неконстантный символ, и он неявно преобразуется из const char* в char* (как подсказывает вам компилятор). Вы можете так же легко передать целое число вместо указателя. В результате ваша функция не может изменить содержимое, указанное ptr, но strcpy может, потому что она видит неконстантный указатель. Вы не получите сбой, потому что в вашем случае указатель указывает на фактический буфер достаточного размера, а не на строковый литерал только для чтения.

Чтобы избежать этого, ищите предупреждения компилятора или компилируйте, например, с помощью -Wall -Werror (если вы используете gcc).

Это поведение характерно для C. Например, C ++ этого не допускает и требует явного приведения (C-style cast или const_cast) для удаления const квалификатора, как и следовало ожидать.

Ответ на расширенный вопрос

Вы присваиваете строковый литерал в неконстантный символ, что, к сожалению, 1027 * допустимо в C и даже в C ++! Он неявно преобразуется в char*, хотя запись через этот указатель теперь приведет к неопределенному поведению. Это устаревшая функция, и пока только C ++ 0x не позволяет этому случиться.

С учетом сказанного, чтобы прекратить изменять сам указатель , вы должны объявить его как постоянный указатель на char (char *const). Или, если вы хотите, чтобы содержимое, на которое он указывает, и сам указатель не изменялись, используйте указатель const для const char (const char * const).

Примеры:

void foo (
        char *a,
        const char *b,
        char *const c,
        const char *const d)
    {
    char buf[10];
    a = buf; /* OK, changing the pointer */
    *a = 'a'; /* OK, changing contents pointed by pointer */

    b = buf; /* OK, changing the pointer */
    *b = 'b'; /* error, changing contents pointed by pointer */

    c = buf; /* error, changing pointer */
    *c = 'c'; /* OK, changing contents pointed by pointer */

    d = buf; /* error, changing pointer */
    *d = 'd'; /* error, changing contents pointed by pointer */
}

Для всех строк ошибок GCC выдает «ошибка: назначение местоположения только для чтения».

3 голосов
/ 12 июля 2010

«const» действительно во время компиляции, так что не ожидайте segfault, если указатель не укажет на недопустимую память.При использовании указателей const таким образом, который потенциально может изменить то, на что они указывают (в этом случае передача его в strcpy, который принимает неконстантные значения), выдаст предупреждение.

1 голос
/ 12 июля 2010

1 - Что на самом деле означает const char * str в прототипе? Означает ли это, что функция не может изменить содержимое строки?

Да! Это означает, что мы не можем изменить содержимое что-то (или char или array of chars), на которое указывает стр.

Если это так, то почему вышеприведенная программа меняет значение?

Это потому, что прототип strcpy() char * strcpy ( char * destination, const char * source );

В вашем коде есть неявное преобразование типа const char* в char*, потому что strcpy() требует, чтобы его первый аргумент имел тип char*.

Технически говоря, ваш код некорректен и довольно опасен. Если вы попробуете тот же код на C ++, вы наверняка получите ошибку. C ++ не допускает таких неявных преобразований, но C допускает.

1 голос
/ 12 июля 2010

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

Использование const гарантирует, что ссылка на память (адрес) не будет изменена. Однако указанная память может быть изменена. Это то, что здесь происходит. Присвоение нового адреса указателю приведет к ошибке.

ПОБОЧНОЕ ПРИМЕЧАНИЕ
Я бы посчитал ваш foo метод небезопасным. Вам лучше также передать максимальную длину str и выполнить проверку длины, иначе вы будете открыты для переполнения буфера.

EDIT
Я взял следующее с этого сайта :

const char *Str сообщает компилятору что DATA указатель тоже указывает является постоянным Это означает, что Str может быть изменено в Func, но *Str не может. Как копия указателя передается Func, любые изменения, сделанные в Str, не являются видно по основному ....

1 голос
/ 12 июля 2010

const char* совпадает с char const* и не char* const. Так что это означает указатель на то, что вы не можете изменить.

Разработка после вашего редактирования. Первая форма запрещает изменение данных (если вы не явным или явным образом приводите данные), а вторая запрещает изменение самого указателя.

То, что вы делаете в отредактированной версии, это изменение указателя. Это законно здесь. Если вы запретите и то, и другое, вам придется написать char const* const.

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