Почему объект типа 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