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

Тип данных массива в C

Читая некоторые подробности о указателе и массивах в C, я немного смутился. С одной стороны, массив можно рассматривать как тип данных. С другой стороны, массив стремится к немодифицируемому lvalue. Я предполагаю, что компилятор сделает что-то вроде замены идентификатора массива на постоянный адрес и выражение для вычисления позиции, заданной индексом во время выполнения.

myArray[3] -(compiler)-> AE8349F + 3 * sizeof(<type>)

Говоря о том, что массив является типом данных, что это означает? Надеюсь, вы можете помочь мне прояснить мое запутанное понимание того, что такое массив и как он обрабатывается компилятором.

4b9b3361

Ответ 1

Говоря о том, что массив является типом данных, что это означает?

Тип данных представляет собой набор данных со значениями, имеющими предопределенные характеристики. Примеры типов данных: целое число, число единиц с плавающей запятой, символ, строка и указатель

Массив - это группа мест памяти, связанная с тем, что все они имеют одно и то же имя и один и тот же тип.


Если вам интересно, почему массив не модифицируется, лучшее объяснение, которое я когда-либо читал;

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

Более подробно вы можете прочитать этот ответ.


EDIT: Для большей ясности; Разница между изменяемыми значениями l, немодифицируемыми значениями l и r-значением (короче);

Разница между этими выражениями такова:

  • Модифицируемое l-значение адресуется (может быть операндом унарного &) и присваиваемым (может быть левым операндом =).
  • Немодифицируемое значение l является адресным, но не назначается.
  • Значение r не адресуется и не присваивается.

Ответ 2

Массив - это непрерывный блок памяти. Это означает, что он выкладывается в памяти последовательно. Пусть говорят, что мы определяем такой массив, как:

int x[4];

Где sizeof(int) == 32 бит.

Это будет выложено в памяти, как это (выбор произвольного начального адреса, скажем 0x00000001)

0x00000001 - 0x00000004
[element 0]
0x00000005 - 0x00000008
[element 1]
0x00000009 - 0x0000000C
[element 2]
0x0000000D - 0x00000010
[element 3]

Вы правы, что компилятор заменяет идентификатор. Помните (если вы это узнали. Если нет, то вы изучаете что-то новое!), Что массив по существу является указателем. В C/С++ имя массива является указателем на первый элемент массива (или указатель, указывающий на адрес 0x00000001 в нашем примере). Сделав это:

std::cout << x[2];

Вы говорите компилятору добавить 2 к этому адресу памяти, который является арифметикой указателя. Скажем, вместо этого вы используете переменную для индексации:

int i = 2;
std::cout << x[i];

Компилятор видит это:

int i = 2;
std::cout << x + (i * sizeof(int));

Он в основном умножает размер типа данных на данный индекс и добавляет его к базовому адресу массива. Компилятор в основном принимает индекс-оператора [] и преобразует его в дополнение с указателем.

Если вы действительно хотите повернуть голову вокруг этого, рассмотрите этот код:

std::cout << 2[x];

Это вполне справедливо. Если вы можете понять, почему, тогда у вас есть концепция.