Почему могут я для изменения содержания символа константы *ptr?

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

foo( const char  *str );

Который согласно моему пониманию означает, что оно не сможет изменить содержание ptr переданный. Как в случае foo( const int i ). Если foo() попытки к chnage значение 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

основной (): Его только для заполнения пространства
нечто (): ABC
основной (): ABC

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

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

Править

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

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

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

основной (): Его только для заполнения пространства
нечто (): Tim
основной (): Его только для заполнения пространства

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

Мне это кажется этим даже с const, Я могу изменить ссылку памяти и содержание ссылки памяти также. Затем, каково использование?

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

Благодаря всем Вам в течение Вашего времени и усилия.

9
задан Andrew-Dufresne 12 July 2010 в 15:47
поделиться

5 ответов

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

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

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

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

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

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

Учитывая это, чтобы перестать изменять сам указатель сам, вы должны объявить его как const указатель на 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 выдает "error: assignment of read-only location".

17
ответ дан 4 December 2019 в 09:12
поделиться

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

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

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

Потому что прототипом strcpy() является char * strcpy ( char * destination, const char * source );

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

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

1
ответ дан 4 December 2019 в 09:12
поделиться

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

3
ответ дан 4 December 2019 в 09:12
поделиться

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

Уточняю после вашей правки. Первая форма запрещает изменять данные (если только вы не приводите неявно или явно), вторая запрещает изменять сам указатель.

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

1
ответ дан 4 December 2019 в 09:12
поделиться

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

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

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

РЕДАКТИРОВАТЬ
Я взял следующее из этого сайта :

const char * Str сообщает компилятору что ДАННЫЕ, на которые указывает указатель, тоже является константой. Это означает, что Str может быть изменено в Func, но * Str не может. Поскольку копия указателя передается в Func, любые изменения, внесенные в Str , не видно по main ....

1
ответ дан 4 December 2019 в 09:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: