Как представлены массивы C в памяти?
Я считаю, что понимаю, как обычные переменные и указатели представлены в памяти, если вы используете C.
Например, легко понять, что указатель Ptr будет иметь адрес, а его значение будет отличаться от адреса, который является пространством в памяти, на которое он указывает. Следующий код:
int main(){
int x = 10;
int *Ptr;
Ptr = &x;
return 0;
}
Имеет следующее представление в памяти:
+---------------------+-------------+---------+
| Variable Name | Address | Value |
+---------------------+-------------+---------+
| x | 3342 | 10 |
+---------------------+-------------+---------+
| Ptr | 5466 | 3342 |
+---------------------+-------------+---------+
Однако мне трудно понять, как массивы представлены в памяти. Например, код:
int main(){
int x[5];
x[0]=12;
x[1]=13;
x[2]=14;
printf("%p\n",(void*)x);
printf("%p\n",(void*)&x);
return 0;
}
выводит один и тот же адрес дважды (для простоты 10568). Это означает, что x == & x. Тем не менее * x (или x [0] в записи массива) равно 12, * (x + 1) (или x [1] в записи массива) равно 13 и т.д. Как это можно представить? Один из способов может быть следующим:
+---------------------+-------------+----------+----------------------+
| Variable Name | Address | Value | Value IF array |
+---------------------+-------------+----------+----------------------+
| x | 10568 | 10568 | 12 |
+---------------------+-------------+----------+----------------------+
| | 10572 | | 13 |
+---------------------+-------------+----------+----------------------+
| | 10576 | | 14 |
+---------------------+-------------+----------+----------------------+
| | 10580 | | trash |
+---------------------+-------------+----------+----------------------+
| | 10584 | | trash |
+---------------------+-------------+----------+----------------------+
Это близко к тому, что происходит или полностью отключено?
Ответы
Ответ 1
Массив - это блок смежных объектов без пробелов между ними. Это означает, что x
в вашем втором примере представлен в памяти как:
+---------------------+-------------+---------+
| Variable Name | Address | Value |
+---------------------+-------------+---------+
| x | 10568 | 12 |
| | +---------+
| | | 13 |
| | +---------+
| | | 14 |
| | +---------+
| | | ?? |
| | +---------+
| | | ?? |
+---------------------+-------------+---------+
То есть, x
имеет пять int
больших и имеет один адрес.
Странная часть массивов заключается не в том, как они хранятся, а в том, как они оцениваются в выражениях. Если вы где-то используете имя массива, это не относится к унарным операторам &
или sizeof
, он оценивает адрес своего первого члена.
То есть, если вы просто напишете x
, вы получите значение 10568 с типом int *
.
Если, с другой стороны, вы пишете &x
, тогда специальное правило не применяется, поэтому оператор &
работает так, как обычно, это означает, что он выбирает адрес массива. В этом примере это будет значение 10568 с типом int (*)[5]
.
Причина, по которой x == &x
заключается в том, что адрес первого элемента массива обязательно равен адресу самого массива, так как массив начинается с его первого члена.
Ответ 2
Ваша диаграмма верна. Странность вокруг &x
не имеет ничего общего с тем, как массивы представлены в памяти. Это связано с распадом указателя array- > . x
сам по себе в контексте значений распадается на указатель на его первый элемент; т.е. он эквивалентен &x[0]
. &x
является указателем на массив, а тот факт, что эти два являются численно равными, просто говорит, что адрес массива численно равен адресу его первого элемента.
Ответ 3
Да, у тебя это есть. Массив C находит индексированное значение x[y]
, вычисляя x + (y * sizeof(type))
. x
- начальный адрес массива. y * sizeof(type)
является смещением от этого. x[0]
создает тот же адрес, что и x.
Аналогично выполняются многомерные массивы, поэтому int x[y][z]
будет потреблять память sizeof(int) * y * z
.
Из-за этого вы можете делать какие-то глупые трюки с указателем на C. Это также означает, что получение размера массива (почти) невозможно.
Ответ 4
Массив C - это всего лишь блок памяти, который имеет последовательные значения того же размера. Когда вы вызываете malloc(), он просто предоставляет вам блок памяти. foo[5]
совпадает с *(foo + 5)
.
Пример - foo.c:
#include <stdio.h>
int main(void)
{
int foo[5];
printf("&foo[0]: %tx\n", &foo[0]);
printf("foo: %tx\n\n", foo);
printf("&foo[3]: %tx\n", &foo[3]);
printf("foo: %tx\n", foo + 3);
}
Вывод:
$ ./foo
&foo[0]: 5fbff5a4
foo: 5fbff5a4
&foo[3]: 5fbff5b0
foo: 5fbff5b0
Ответ 5
Массив в C является последовательным блоком памяти с каждым блоком-членом того же размера. Вот почему указатели работают, вы ищете смещение на основе первого адреса элемента.
Ответ 6
раздел "Массивы и указатели" в разделе "Часто задаваемые вопросы по С" ) содержит некоторую полезную информацию.
Ответ 7
Даниил,
это не сложно. У вас есть основная идея, и нет большой разницы в представлении массивов в памяти. если вы объявите массив, скажите
void main(){
int arr[5]={0,1,2,3,4};
}
вы инициализировали (определили) массив. Таким образом, пять элементов будут сохранены в пяти соседних местах в памяти. вы можете наблюдать это, ссылаясь на адрес памяти каждого элемента.
Подобно другим примитивным типам данных в C, идентификатор массива (здесь arr) сам представляет собой его указатель. Идея кажется туманной, если вы новичок, но вы будете чувствовать себя комфортно, когда будете продолжать.
printf("%d",arr);
эта строка покажет вам адрес памяти первого элемента arr [0]. Это похоже на ссылку на адрес первого элемента.
printf("%d",&arr[0]);
теперь вы можете просматривать ячейки памяти всех элементов. Следующий фрагмент кода выполнит эту работу.
int i;
for(i=0;i<5;i++){
printf("location of %d is %d\n",arr[i],&arr[i]);
}
вы увидите, что каждый адрес увеличивается с интервалами в четыре раза (если ваши целые числа составляют 32 бита).
Таким образом, вы можете легко понять, как массивы хранятся в памяти.
вы также можете попробовать одно и то же, используя другой метод.
int i;
for(i=0;i<5;i++){
printf("location of %d is %d\n",*(a+i),a+i);
}
вы получите тот же набор ответов в обоих случаях и попытайтесь получить эквивалентность.
попробуйте один и тот же эксперимент с использованием разных типов данных (char, float и struct types). Вы увидите, как промежутки между соседними элементами изменяются в зависимости от размера одного элемента.
![]()
Ответ 8
int x [] производит тот же результат, что и int * x;
это просто указатель
поэтому обозначения x [i] и * (x + i) дают тот же результат.