Как массивы C представляются в памяти? - PullRequest
26 голосов
/ 21 октября 2011

Мне кажется, я понимаю, как нормальные переменные и указатели представляются в памяти, если вы используете 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 [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                | 
+---------------------+-------------+----------+----------------------+

Это близко к тому, что происходит, или полностью выключено?

Ответы [ 8 ]

33 голосов
/ 21 октября 2011

Массив - это блок смежных объектов без пробелов между ними. Это означает, что x в вашем втором примере представлена ​​в памяти как:

+---------------------+-------------+---------+
| Variable Name       | Address     | Value   | 
+---------------------+-------------+---------+
| x                   | 10568       | 12      |
|                     |             +---------+
|                     |             | 13      |
|                     |             +---------+
|                     |             | 14      |
|                     |             +---------+
|                     |             | ??      |
|                     |             +---------+
|                     |             | ??      |
+---------------------+-------------+---------+

То есть x имеет размер пять int s и имеет один адрес.

Странная часть о массивах заключается не в том, как они хранятся, а в том, как они оцениваются в выражениях. Если вы используете имя массива где-то, что оно не является субъектом унарных операторов & или sizeof, оно оценивается по адресу первого члена.

То есть, если вы просто напишите x, вы получите значение 10568 с типом int *.

Если, с другой стороны, вы пишете &x, то специальное правило не применяется - поэтому оператор & работает как обычно, то есть он выбирает адрес массива. В примере это будет значение 10568 с типом int (*)[5].

Причина, по которой x == &x состоит в том, что адрес первого члена массива обязательно равен адресу самого массива, поскольку массив начинается с первого члена.

22 голосов
/ 21 октября 2011

Ваша диаграмма верна.Странность вокруг &x не имеет ничего общего с тем, как массивы представляются в памяти.Это связано с массивом-> указатель распада.x само по себе в контексте значения превращается в указатель на свой первый элемент;т.е. это эквивалентно &x[0].&x - указатель на массив, и тот факт, что они численно равны, говорит о том, что адрес массива численно равен адресу его первого элемента.

2 голосов
/ 21 октября 2011

Да, у вас это есть. Массив C находит индексированное значение x[y], вычисляя x + (y * sizeof(type)). x - начальный адрес массива. y * sizeof(type) это смещение от этого. x[0] производит тот же адрес, что и x.

Многомерные массивы выполняются аналогичным образом, поэтому int x[y][z] будет использовать sizeof(int) * y * z памяти.

Из-за этого вы можете делать некоторые глупые трюки с указателями на Си. Это также означает, что получить размер массива (почти) невозможно.

0 голосов
/ 21 октября 2011

Даниэль,

это не сложно.У вас есть основная идея, и нет большой разницы в представлении массивов в памяти.если вы объявляете массив, скажем

     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).Вы увидите, как зазоры между соседними элементами варьируются в зависимости от размера одного элемента.

0 голосов
/ 21 октября 2011

В разделе Массивы и указатели в C FAQ содержится некоторая полезная информация.

0 голосов
/ 21 октября 2011

int x [] дает тот же результат, что и int * x;

это просто указатель

поэтому обозначения x [i] и * (x + i) дают одинаковый результат.

0 голосов
/ 21 октября 2011

Массив в C - это последовательный блок памяти с блоком каждого члена одинакового размера. Вот почему указатели работают, вы ищете смещение на основе адреса первого члена.

0 голосов
/ 21 октября 2011

Массив переменного тока - это просто блок памяти, имеющий последовательные значения одинакового размера.Когда вы вызываете 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...