Почему объект типа array не изменяется?
указано здесь это
термин modifiable lvalue используется, чтобы подчеркнуть, что lvalue позволяет изменять назначенный объект, а также исследовать. Следующие типы объектов являются lvalues, но не изменяемые lvalues:
- тип массива
- неполного типа
- const-квалифицированный тип
- тип структуры или объединения с одним из его членов квалифицированным как const тип
поскольку эти значения не изменяются, они не могут отображаться в левой части инструкции присваивания.
почему объект типа массива не изменяется? Разве не правильно писать
int i = 5, a[10] = {0};
a[i] = 1;
?
А также, что такое неполный тип?
5 ответов
принять декларацию
int a[10];
тогда верно все следующее:
- тип выражение
a
является " 10-элементный массивint
"; кроме тех случаев, когдаa
является операндомsizeof
или унарный&
операторы, выражение будет преобразовано в выражение типа "указатель наint
" и его стоимость будет адрес первого элемента в массиве; - тип выражение
a[i]
иint
; он относится к целочисленному объекту, хранящемуся какi
' - й элемент массива; - на выражение
a
может не быть целью назначения, потому что C не обрабатывает массивы, как другие переменные, поэтому вы не можете написать что-то вродеa = b
илиa = malloc(n * sizeof *a)
или что-то в этом роде.
вы заметите, что я продолжаю подчеркивать слово "выражение". Есть разница между куском память мы выделяем для хранения 10 целых чисел и символов (выражений), которые мы используем для обозначения этого куска памяти. Мы можем обратиться к нему с выражением a
. Мы также можем создать указатель на этот массив:
int (*ptr)[10] = &a;
выражение *ptr
также имеет тип " 10-элементный массив int
", и это относится к тому же куску памяти, что a
относится.
C не обрабатывает выражения массива (a
, *ptr
) как и выражения других типов, и одно из различия в том, что выражение массив тип не может быть объектом присвоения. Вы не можете переназначить a
для ссылки на другой объект массива (то же самое для выражения *ptr
). Вы мая присвоить новое значение a[i]
или (*ptr)[i]
(измените значение каждого массива элемент), и вы можете назначить ptr
чтобы указать на другой массив:
int b[10], c[10];
.....
ptr = &b;
.....
ptr = &c;
что касается второго вопроса...
An неполное type не хватает информации о размере; объявления, такие как
struct foo;
int bar[];
union bletch;
все создают неполные типы, потому что компилятору не хватает информации, чтобы определить, сколько места для хранения объекта этого типа. Вы не можете создавать объекты неполного типа; например, вы не можете объявить
struct foo myFoo;
если вы не выполните определение struct foo
. Однако, вы можете создать указатели для неполных типов; для пример, ты мог бы объявить
struct foo *myFooPtr;
без завершения определения для struct foo
потому что указатель хранит адрес объекта, и вам не нужно знать размер, тип для этого. Это позволяет определить самореферентные типы, такие как
struct node {
T key; // for any type T
Q val; // for any type Q
struct node *left;
struct node *right;
};
определение типа struct node
не полное пока мы не достигли этого закрытия }
. Поскольку мы можем объявить указатель на неполный тип, мы в порядке. Однако мы не могли определить структуру как
struct node {
... // same as above
struct node left;
struct node right;
};
потому что тип не является полным, когда мы объявляем left
и right
членов, а также потому, что каждый left
и right
каждый член будет содержать left
и right
собственные члены, каждый из которых будет содержать left
и right
члены их собственный, и так далее и так далее.
это отлично подходит для структур и союзов, но как насчет
int bar[];
???
мы объявили символ bar
и указал, что это будет типа массива, но размер неизвестен на данный момент. в конце концов мы должны определить его с размером, но таким образом символ может использоваться в контекстах, где размер массива не имеет смысла или необходимости. У меня нет хорошего, не придуманного примера с моей головы, чтобы проиллюстрировать это, хотя.
EDIT
отвечая на комментарии здесь, так как в разделе комментариев не будет места для того, что я хочу написать (я в многословном настроении сегодня вечером). Вы спросили:
значит все переменные выражения?
это означает, что любая переменная может быть выражением или частью выражения. Вот как стандартный язык определение термина выражение:
6.5 выражения
1 элемент выражение - это последовательность операторов и операндов, которая задает вычисление значение, или обозначающее объект или функцию, или создающее побочные эффекты, или выполняет их комбинацию.
например, переменная a
все само по себе считается выражением; он обозначает объект массива, который мы определили для хранения 10 целых значений. Он также вычисляет адрес первого элемента массива. Переменная a
также может быть частью большего выражения вроде a[i]
; оператор-это оператор subscript []
и операнды являются переменными a
и i
. Это выражение обозначает один элемент массива и вычисляет значение, хранящееся в этом элементе. Это выражение, в свою очередь, может быть частью большего выражения вроде a[i] = 0
.
и пусть мне ясно, что в объявлении int a[10] A [] означает тип массива
Да, точно.
в C объявления основаны на типах выражений, а не на типах объектов. Если у вас есть простая переменная с именем y
что хранит int
значение, и вы хотите получить доступ к этому значению, вы просто использовать y
в выражении, как
x = y;
тип выражение y
is int
, так что объявление y
написано
int y;
если, с другой стороны, у вас есть массив of int
значения, и вы хотите получить доступ к определенному элементу, вы должны использовать имя массива и индекс вместе с оператором индекса для доступа к этому значению, например
x = a[i];
тип выражение a[i]
и int
, поэтому объявление массива записывается как
int arr[N]; // for some value N.
в "int
-ность" arr
задается спецификатором типа int
; "массивность"arr
задается декларантом arr[N]
. Декларатор дает нам имя объявляемого объекта (arr
) вместе с некоторой дополнительной информацией о типе, не заданной спецификатором типа ("является массивом N-элементов"). Декларация "читается" как
a -- a
a[N] -- is an N-element array
int a[N]; -- of int
EDIT2
и после всего этого, я еще не сказал вам реальные причина, по которой выражения массива являются немодифицируемыми lvalues. Итак, вот еще одна глава этой книги ответов.
C не полностью сформировался из разума Денниса Ричи; он был получен из более раннего языка, известного как B (который был получен из BCPL).1 B был" безтипным " языком; у него не было разных типов для целых чисел, поплавков, текста, записей и т. д. Вместо этого все было просто словом фиксированной длины или "ячейкой" (по сути, целым числом без знака). Память рассматривалась как линейный массив ячеек. Когда вы выделили массив в B, например
auto V[10];
компилятор выделил 11 ячеек; 10 смежных ячеек для самого массива, плюс ячейка, которая была привязана к V, содержащая местоположение первой ячейки:
+----+
V: | | -----+
+----+ |
... |
+----+ |
| | <----+
+----+
| |
+----+
| |
+----+
| |
+----+
...
когда Ричи добавить struct
типы В C, он понял, что эта схема вызывает у него некоторые проблемы. Например, он хотел создать тип структуры для представления записи в файле или таблица каталогов:
struct {
int inumber;
char name[14];
};
он хотел, чтобы структура не просто описывала запись абстрактно, но и представляла биты в фактической записи таблицы файлов, в которой не было дополнительной ячейки или слова для хранения местоположения первого элемента в массиве. Поэтому он избавился от него-вместо того, чтобы выделить отдельное место для хранения адреса первого элемента, он написал C таким образом, чтобы адрес первого элемента был вычислен, когда выражение массива было оцененный.
этой почему вы не можете сделать что-то вроде
int a[N], b[N];
a = b;
так как a
и b
возвращать указатель значения в этом контексте, это эквивалентно записи 3 = 4
. В памяти нет ничего, что на самом деле магазинах адрес первого элемента в массиве; компилятор просто вычислить его на этапе перевода.
1. Это все взято из газеты развитие языка C
термин "lvalue типа массива" буквально относится к объекту массива как lvalue типа массива, т. е. объект массива в целом. Это lvalue не модифицируется в целом, так как нет никакой юридической операции, которая может изменить его в целом. Фактически, единственные операции, которые вы можете выполнить на lvalue типа массива: unary &
(адрес), sizeof
и неявное преобразование в тип указателя. Ни одна из этих операций не изменяет массив, поэтому объекты массива не являются поддающийся изменению.
a[i]
не работает с lvalue типа массива. a[i]
обозначает
неполный тип-это тип, который объявлен, но не определен, например struct Foo;
.
вы всегда можете назначить индивидуальные массива элементов (предполагая, что они не const
). Но вы не можете назначить что-то всему массиву.
C и c++ довольно запутаны в том, что что-то вроде int a[10] = {0, 1, 2, 3};
не является назначением, а инициализацией, хотя это выглядит в значительной степени как назначение.
Это нормально (инициализация):
int a[10] = {0, 1, 2, 3};
Это не работает в C/с++:
int a[10];
a = {0, 1, 2, 3};
предполагая, что a
представляет собой массив ints,a[10]
Не массив. Это int
.
a = {0}
будет незаконным.
помните, что значение массива на самом деле является адресом (указателем) его первого элемента. Этот адрес нельзя изменить. Так что
int a[10], b[10];
a = b
является незаконным.
это, конечно, не имеет ничего общего с изменением контент массива как в a[1] = 3