Являются ли массивы const типов данных - PullRequest
0 голосов
/ 15 сентября 2011
#include<stdio.h>
#include<string.h>
int main()
{
        char a[]="aaa";
        char *b="bbb";
        strcpy(a,"cc");
        printf("%s",a);
        strcpy(b,"dd");
        printf("%s",b);
        return 0;
}

Мы не смогли изменить содержимое массива, но вышеприведенная программа не выдает никакой ошибки времени компиляции. При запуске она печатает cc и завершается.Я думаю, что содержимое массива будет храниться в разделе «только для чтения» сегмента данных, поэтому невозможно изменить значение массива как его const. Но здесь, в приведенной выше программе, значение было изменено на cc и программупрекращено. Значение изменилось здесь, почему это так. Пожалуйста, помогите мне понять.

Ответы [ 6 ]

3 голосов
/ 15 сентября 2011

Массив - это не постоянный тип данных, а литеральная строка типа "aaa".Вы не можете изменять его содержимое.

1 голос
/ 15 сентября 2011

Вот гипотетическая карта памяти, показывающая, как строковые литералы, массив и указатель все связаны друг с другом:

Item              Address            0x00  0x01  0x02  0x03
----              -------            ----  ----  ----  ----
"aaa"             0x00040000          'a'   'a'   'a'  0x00
"bbb"             0x00040004          'b'   'b'   'b'  0x00
"cc"              0x00040008          'c'   'c'   0x00 ???
"dd"              0x0004000C          'd'   'd'   0x00 ???
...
  a               0x08000000          'a'   'a'   'a'  0x00
  b               0x08000004          0x00  0x04  0x00 0x00

Это ситуация в строке 6 в вашем коде после того, как a и b были объявлены и инициализированы. Строковые литералы "aaa", "bbb", "cc" и "dd" все находятся где-то в памяти, так что они существуют на протяжении всей жизни программы. Они хранятся в виде массивов char (const char в C ++). Попытка изменить содержимое строкового литерала (в случае этого гипотетического, попытка записи в любую область памяти, начиная с 0x0004) вызывает неопределенное поведение. Некоторые платформы хранят строковые литералы в постоянной памяти, другие хранят их в доступной для записи памяти, но во всех случаях они должны обрабатываться , как если бы они были недоступны для записи.

Объект a является массивом char, и он был инициализирован с содержимым строкового литерала "aaa". Объект b является указателем на char, и он был инициализирован с адресом строкового литерала "bbb". В строке

strcpy(a, "cc");

вы копируете содержимое строкового литерала "cc" в a; после выполнения строки ваша карта памяти выглядит так:

Item              Address            0x00  0x01  0x02  0x03
----              -------            ----  ----  ----  ----
"aaa"             0x00040000          'a'   'a'   'a'  0x00
"bbb"             0x00040004          'b'   'b'   'b'  0x00
"cc"              0x00040008          'c'   'c'   0x00 ???
"dd"              0x0004000C          'd'   'd'   0x00 ???
...
  a               0x08000000          'c'   'c'   0x00 0x00
  b               0x08000004          0x00  0x04  0x00 0x00

Поэтому, когда вы печатаете a в стандартный вывод, вы должны увидеть строку cc. Примечание: printf буферизуется, поэтому возможно, что вывод не будет записан в терминал сразу - либо добавьте символ новой строки в строку формата (printf("%s\n", a);), либо вызовите fflush(stdout); после printf, чтобы убедиться ваш вывод обнаруживается.

В строке 9 вы пытаетесь скопировать содержимое строкового литерала "dd" в место, на которое указывает b; к сожалению, b указывает на другой строковый литерал, который, как упоминалось выше, вызывает неопределенное поведение. На этом этапе ваша программа может буквально делать все, от запуска, как ожидалось, до полного сбоя, до всего, что между Это может быть причиной того, что вы видите только вывод для cc.

1 голос
/ 15 сентября 2011

char* != char[]!

В этом случае a является буфером для области записи, которая сначала заполняется содержимым литеральной строки, которая хранится в области только для чтения, тогда как b является указателем, который непосредственно указывает на область только для чтения! Вот пример кода, который поможет вам понять:

#include <stdio.h>
#include <string.h>

#define literal "test"

int main() {
  char a[] = literal
  char b[] = literal;
  char* c = literal;
  char* d = literal;

  printf("%s (%p)\n", a, a);
  printf("%s (%p)\n", b, b);
  printf("%s (%p)\n", c, c);
  printf("%s (%p)\n", d, d);

  return 42;
}

Выполнив этот код, вы увидите, что даже если все 4 напечатанные строки одинаковы, a и b обращаются к различным областям памяти, тогда как c и d указывают на уникальную третью область адрес. Кроме того, вы должны увидеть большую разницу в диапазоне адресов между первыми двумя и последними: здесь расположение в различных областях памяти (чтение / запись и только для чтения) становится очевидным.

Редактировать : просто для того, чтобы настаивать, пятый printf (printf("%s (%p)\n", literal, literal);) будет печатать такие же строки, как c и d.

1 голос
/ 15 сентября 2011

У вас есть несколько массивов в вашей программе.Некоторые из них могут быть изменены, другие нет.Итак, ваш исходный вопрос («Являются ли массивы const datatypes?») Не очень-то значимым образом отвечает.

Строковые литералы (например, "aaa") являются массивами, но они не могут быть изменены.Обратите внимание, что строковые литералы на языке C на самом деле не const (например, массив "aaa", имеет тип char[4], а не const char[4]).Однако язык все еще явно запрещает попытки модифицировать строковые литералы.Компилятор не обязан отлавливать такие попытки.Кроме того, ошибка при выполнении не гарантируется, когда вы делаете такую ​​попытку.Поведение просто undefined .Когда вы делаете strcpy(b,"dd"), вы пытаетесь изменить немодифицируемый массив - поведение не определено.Все может случиться.

Что касается обычного массива a в вашем примере кода, он объявлен как модифицируемый.Таким образом, вы можете изменить его столько, сколько хотите.Когда вы делаете strcpy(a, "cc"), вы копируете строку "cc" в свой массив a.Итак, именно то, что вы наблюдаете в своем эксперименте, когда вы печатаете содержание a.Здесь нет ничего необычного.

0 голосов
/ 15 сентября 2011

b указывает на строку bbb, которая хранится в постоянной памяти. Которая не может быть изменена strcpy() или чем-либо еще.

0 голосов
/ 15 сентября 2011

Массивы не являются константными типами данных, но литералы есть.Строковые литералы «aaa» и «bbb» не могут быть изменены (или могут, но результат не определен).

Как указывал Пол R, char a[] = "aaa"; хорошо, потому что он будет использовать строковый литерал дляинициализировать массив (который, как я уже говорил, не является типом данных const).Это char *b = "bbb"; проблема здесь, так как вы позже попытаетесь изменить содержимое самого строкового литерала, а не его копию.

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