Подтвердить что ты не робот

Если char * s только для чтения, зачем их переписывать?

Мой курс научил меня, что char * s являются статическими/только для чтения, поэтому я думал, что это означает, что вы не можете редактировать их после того, как вы их определили. Но когда я запускаю:

char* fruit = "banana";
printf("fruit is %s\n", fruit);
fruit = "apple";
printf("fruit is %s\n", fruit);

Затем он компилируется и дает мне:

fruit is banana
fruit is apple

Почему? Разве я неправильно понял, что значит быть только для чтения? Извините, если это очевидно, но я новичок в кодировании, и я не могу найти ответ в Интернете.

4b9b3361

Ответ 1

Представленный фрагмент кода не изменяет сами литералы строки. Он изменяет только значения, сохраненные в указателе fruit.

Вы можете представить эти строки

char* fruit = "banana";
fruit = "apple";

следующим образом

char unnamed_static_array_banana[] = { 'b', 'a', 'n', 'a', 'n', 'a', '\0' };
char *fruit = &unnamed_static_array_banana[0];
char unnamed_static_array_apple[]  = { 'a', 'p', 'p', 'l', 'e', '\0' };
fruit = &unnamed_static_array_apple[0];

Эти инструкции не меняют массивы, соответствующие строковым литералам.

С другой стороны, если вы попытались написать

char* fruit = "banana";
printf("fruit is %s\n", fruit);
fruit[0] = 'h';
^^^^^^^^^^^^^^
printf("fruit is %s\n", fruit);

то есть если вы попытались изменить строковый литерал с помощью указателя, указывающего на него (для первого символа строкового литерала), то программа имела поведение undefined.

Из стандарта C (6.4.5 Строковые литералы)

7 Неизвестно, являются ли эти массивы различными, если их элементы имеют соответствующие значения. Если программа пытается изменить такой массив, поведение undefined.

Ответ 2

В вашей программе выражение "banana" обозначает строковый литерал в образ программы, массив символов. Значение выражения имеет тип char * или "указатель на символ". Указатель указывает на первый байт этого массива, символ 'b'.

Ваша переменная char *fruit также имеет тип "указатель на символ" и берет свое начальное значение из этого выражения: оно инициализируется копией указателя на данные, а не самих данных; он просто указывает на b.

Когда вы назначаете "apple" - fruit, вы просто заменяете его значение указателя на другое, поэтому теперь оно указывает на другой массив литералов.

Чтобы изменить сами данные, вам понадобится выражение, например:

char *fruit = "banana";
fruit[0] = 'z';  /* try to turn "banana" into "zanana" */

В соответствии со стандартом ISO C поведение этого не определено. Он мог быть в том, что массив "banana" доступен только для чтения, но это не требуется.

Реализации C могут делать строковые литералы доступными для записи или сделать это опцией.

(Если вы можете изменить строковый литерал, это не значит, что все в порядке. Во-первых, ваша программа по-прежнему недостаточно четко определена в соответствии с ISO C: она не переносима. Во-вторых, разрешен компилятор C для объединения литералов, которые имеют общий контент в одном хранилище.Это означает, что два вхождения "banana" в программе на самом деле могут быть точно такими же массивами. Кроме того, строковый литерал "nana", встречающийся где-то в программе, может быть суффиксом из массива "banana", встречающегося в другом месте, другими словами, совместного использования одного и того же хранилища. Модификация литерала может иметь удивительные эффекты, модификация может появляться в других литералах.)

Также "статические" и "только для чтения" не являются синонимами. Большинство статических хранилищ в C фактически модифицируются. Мы можем создать модифицируемый статический массив символов, который содержит следующую строку:

/* at file scope, i.e. outside of any function */
char fruit[] = "banana";

Или:

{
  /* in a function */
  static fruit[] = "banana";

Если мы не учитываем размер массива, он автоматически выбирается из инициализирующего строкового литерала и включает в себя пространство для нулевого завершающего байта. В функции нам нужно static поставить массив в статическую память, иначе мы получим локальную переменную.

Эти массивы могут быть изменены; fruit[0] = 'z' - корректное поведение.

Кроме того, в этих ситуациях "banana" не обозначает массив символов. Массив - это переменная fruit; выражение "banana" - это просто фрагмент синтаксиса, который указывает начальное значение массива:

char *fruit = "banana";  // "banana" is an object in program image
                         // initial value is a pointer to that object

char fruit_array[] = "apple"; // "apple" is syntax giving initial value

Ответ 3

Объект fruit доступен для записи - он может быть установлен для указания на другой строковый литерал.

Строковые литералы "banana" и "apple" недоступны для записи. Вы можете изменить fruit, чтобы указать на строковый литерал, но если вы это сделаете, вы не должны пытаться изменить вещь, на которую указывает fruit:

char *fruit = "banana"; // fruit points to first character of string literal
fruit = "apple";        // okay, fruit points to first character of different string literal
*fruit = 'A';           // not okay, attempting to modify contents of string literal
fruit[1] = 'P';         // not okay, attempting to modify contents of string literal

Попытка изменить содержимое строкового литерала приводит к поведению undefined - ваш код может работать как ожидалось, или вы можете получить ошибку времени выполнения или что-то совершенно неожиданное может произойти. Для безопасности, если вы определяете переменную, указывающую на строковый литерал, вы должны объявить ее const:

const char *fruit = "banana";  // can also be written char const *

Вы все же можете назначить fruit для указания на разные строки:

fruit = "apple";

но если вы попытаетесь изменить то, что указывает fruit, компилятор будет кричать на вас.

Если вы хотите определить указатель, который может указывать только на один конкретный строковый литерал, вы можете const -qualify указатель:

const char * const fruit = "banana"; // can also be written char const * const

Таким образом, если вы попытаетесь либо написать, на что указывает fruit, либо попытаться установить fruit, чтобы указать на другой объект, компилятор будет кричать на вас.

Ответ 4

В основном, когда вы выполняете

char* fruit = "banana";

Вы указали указатель fruit на первую букву "банан". Когда вы печатаете его, C в основном начинается с буквы "b" и продолжает печатать буквы до тех пор, пока они не достигнут нулевого символа \0 в конце.

Затем говоря

fruit = "apple";

Вы изменили указатель fruit, чтобы теперь указать на первую букву "apple"

Ответ 5

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

char * const  cpfruit = "banana";
cpfruit = "apple";        // error

char const * cpfruit = "banana";
cpfruit[0] = 'x';        // error

char * ncfruit = "banana";
ncfruit[0] = 'x';        // compile will allow, but may cause run-time error.

Ответ 6

Что ваш курс научил вас правильно!

Когда вы определили char* fruit = "banana", в первую очередь у вас есть fruit как указатель на постоянный символ. 7 байтов (включая нулевое завершение) строки находятся в разделе .ro объектного файла (название раздела, очевидно, будет меняться в зависимости от платформы).

Когда вы reset указатель char указателя на "яблоко" , он просто указал на другое место памяти в разделе только для чтения, которое содержит "яблоко"

По существу, когда вы говорите, что плод является константой, он ссылается на fruit как указатель на память const. Если бы вы определили его как const pointer to a const string: -
char* const fruit = "banana";
 Компилятор остановил бы вас от сброса его на "яблоко"

Ответ 7

Вы указываете свою переменную fruit на другую строку. Вы только переписываете адрес (местоположение). Компилятор увидит вашу постоянную строку "банан" и "яблоко" и сохранит их отдельно в памяти программы. Пусть говорят, что строка "banana" поступает в ячейку памяти, расположенную по адресу 1, а "apple" сохраняется в памяти 2. Теперь, когда вы делаете:

fruit = "banana";

компилятор просто назначит 1 переменной fruit, что означает, что он указывает на адрес 1, который содержит строку banana. Когда вы выполните:

fruit = "apple";

компилятор назначит переменную 2 fruit, что означает, что она указывает на addess 2, где сохраняется строка apple.

Ответ 8

При использовании char *p="banana"; банановая строка сохраняется в ячейке памяти, доступной только для чтения. После чего при вводе p="apple"; строка apple хранится в каком-то другом месте памяти, а указатель теперь указывает на новое место в памяти.

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

#include<stdio.h>
int main(void)
{
    char *p = "Banana";
    printf("p contains address of string constant 'Banana' at 0x%p\n", p);

    p="Apple";
    printf("p contains address of string constant 'Apple' at 0x%p\n", p);

}