2017-01-25 13 views
1

В настоящее время я изучаю C, и я смущен о макете памяти и указателях.Stack против указателей кучи в C

В следующем коде я понимаю, что массив выделен в стеке.

#include <stdio.h> 

int main() { 
    int x[4]; 
    x[0] = 3; x[1] = 2; x[2] = 1; 
    printf("%p\n",x); 
    printf("%p\n", &x); 
} 

Вопрос в том, почему два вызова печати выдают одинаковое значение?

Я попробовал аналогичный фрагмент, используя malloc (выделяем в кучу), и значения отличаются.

#include <stdio.h> 
#include <stdlib.h> 

int main() { 
    int *x = malloc(sizeof(int) * 4); 
    x[0] = 3; x[1] = 2; x[2] = 1; 
    printf("%p\n",x); 
    printf("%p\n", &x); 
} 
+3

Возможный дубликат [Как адрес массива равен его значению в C?] (Http://stackoverflow.com/questions/2528318/how-come-an-arrays-address-is-equal-to- его значение-в-в) – Sneftel

+1

C не требует использования стека для автоматических переменных. Это конкретная реализация. И массив не является указателем. – Olaf

+0

@Olaf - это реализация, специфическая, поскольку все реализации специально выделяются в стек ;-). –

ответ

1

Причина в том, что в отличие от вас, вероятно, учили, массивы не указатели. Массивы в C распад в указатели при некоторых обстоятельствах. Когда вы передаете массив функции, он распадается на указатель на первый элемент. Адрес этого элемента совпадает с адресом всего массива (адрес всегда относится к первому байту объекта).

Что вы получаете от malloc - это не массив, а адрес куска памяти. Вы назначаете адрес указателю. Но указатель и кусок являются отдельными объектами. Поэтому печать значения указателя, в отличие от его адреса, дает разные результаты.


(1) Decay - причудливый термин для типа неявного преобразования типов. Когда выражение массива используется в наиболее местах (например, передается как аргумент функции, ожидающей указателя), он автоматически превращается в указатель на свой первый элемент. «Распад» заключается в том, что вы теряете информацию о типе, т. Е. Размер массива.

+0

Не могли бы вы немного рассказать о том, что вы подразумеваете под «разложением в указатели»? Раньше я никогда не слышал об этом. – macsj200

+0

@ macsj200 - см. Мое редактирование. – StoryTeller

+0

На самом деле преобразование не связано с передачей как параметра, но выражение используется массив: http://port70.net/~nsz/c/c11/n1570.html#6.3.2.1p3 – Olaf

2

Два ваших вызова печати печатают одно и то же значение, потому что один пытается напечатать массив, который распадается на указатель на массив, а другой печатает адрес массива. Указатель на массив содержит адрес массива, поэтому они имеют одинаковое значение.

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

Итак, в первом случае все, что у вас есть, это массив (x). Во втором случае у вас есть выделенный блок памяти (неназванный) и указатель на выделенный блок (x).

+0

Исправление: имя arra распадается на указатель ** на первый элемент ** (не на массив) для большинства применений. – Olaf

+1

Я думаю, что название распадов вводит в заблуждение. И нет различия * * * между указателем на массив и указателем на первый элемент массива. Массивы сохраняют все свои объекты в непрерывной памяти. Но есть различие * смысл *, и здесь я хочу, чтобы смысл указывать на весь массив. –

+0

Ну, сам массив не разрушается. Фактически, согласно стандарту, это выражение «массив типа», которое преобразуется. И стандарт не запрещает использование разных представлений для разных типов (они не должны сравнивать одинаковые). Тип всегда является частью выражения вместе со значением. Снова вы путаете стандартные и детали реализации. Хотя они имеют отношение к этому вопросу, это хорошая идея указать на эти различия. Облегчение и разборки вместе не помогают новичкам. – Olaf

2

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

Элемент элемента массива (как и все его элементы) имеет адрес, также являющийся адресом его первого байта. Указатель на его первый элемент - это то, что тип массива «настраивается», когда он передается в качестве аргумента функции.

Эти два байта идентичны и имеют один и тот же адрес. Но они имеют разные типы, что становится очевидным, если вы добавите 1 к ним и распечатаете их снова.

Указатель y, напротив, является его собственным отдельным объектом (возможно, размером 4 или 8 байт, достаточным для хранения в нем адреса). Как и любой объект, он имеет адрес, который может быть получен с помощью оператора &. Возможно, смутно, он также содержит адрес, в этом случае адрес первого байта массива. Эти два, конечно, не равны: объект-указатель находится в другом месте, чем массив (а именно, рядом с ним в стеке, даже если Олафу это не нравится).

Незначительное замечание: вы используете %p для печати указателей, что хорошо. Если вы это сделаете, вы должны строго придерживаться указателя, который вы печатаете, указателем на пустоту: printf("%p\n", (void *)x);.