Сходства и различия между массивами и указателями через практический пример

System.Reflection.PropertyInfo aProp = typeof(System.Windows.Forms.Control)
    .GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic |
    System.Reflection.BindingFlags.Instance);
aProp.SetValue(ListView1, true, null);

Ian имеет еще некоторую информацию об использовании этого на терминальном сервере.

9
задан corto 23 November 2009 в 10:51
поделиться

5 ответов

В большинстве случаев выражение типа массива будет неявно преобразовано из "N-элементного массива T "to" указатель на T, и его значение будет установлено так, чтобы указывать на первый элемент массива. Исключение из этого правила - когда массив является операндом & или sizeof , или если массив является строковым литералом, используемым для инициализации другого массива в объявлении.

Итак, как все это связано с вашим кодом?

В строке

scanf("%d", &a);

вы применяете к массиву оператор & . Это подавляет неявное преобразование из «массива T» в «указатель на T» и возвращает значение типа «указатель на массив T» или T (*) [N] (отсюда ваше первое предупреждение ). Теперь оказывается, что значение указателя на массив и значение указателя на первый элемент массива совпадают, просто они имеют разные типы. Предположим, что a находится по адресу 0x0001000:

expression      type          value         note
----------      ----          -----         ----
         a      int *         0x0001000     implicitly converted to pointer
        &a      int (*)[N]    0x0001000     
     &a[0]      int *         0x0001000

Вот почему ваш первый вызов scanf () «работает»; ты' при передаче значения правого указателя , но компилятор жалуется, потому что тип выражения не соответствует тому, что ожидает функция. Если бы вы написали

scanf("%d", a);

, вы бы не получали никаких предупреждений, так как тип a будет принят как int * , что и есть scanf () ] ожидает. Обратите внимание, что это идентично вызову

scanf("%d", &a[0]);

. Что касается b ...

Вы явно объявляете b как указатель на int и назначаете ему блок памяти. Когда вы применяете к нему оператор и , вы получаете адрес переменной b с типом int ** (отсюда второй предупреждение), а не адрес, на который указывает b .

expression      type          value         note
----------      ----          -----         ----
         b      int *         0x0040000     value contained in b
        &b      int **        0x0001000     address of b

В этом случае вы просто передаете неразмеченный b :

scanf("%d", b);
10
ответ дан 4 December 2019 в 11:05
поделиться

Это нормально ...

Во-первых, scanf требует указателя. "a" и "b" уже являются указателями! Итак:

/* 1 */
scanf("%d", a);
printf("%d\n", a[0]);

/* 2 */ 
scanf("%d", b);
printf("%d\n", b[0]);

Будет работать.

Обычно / * 1 * / не должно работать. Но gcc преобразует "& a" в "a", потому что "& a" не имеет никакого смысла.

printf("&a = %p\n", &a);
printf("a = %p\n", a);
printf("&b = %p\n", &b);
printf("b = %p\n", b);

&a = 0x7ffff6be67d0
a = 0x7ffff6be67d0
&b = 0x7ffff6be67c8
b = 0xb0b010

Вы не можете принять адрес a. Но b - это «обычная переменная» указателя типа, и поэтому вы можете взять ее адрес с помощью «& b».

On / * 2 * / вы помещаете значение, введенное пользователем, в b и, таким образом, * b (или b [0]) произойдет сбой, если пользователь не введет действительный адрес читаемой памяти.

1
ответ дан 4 December 2019 в 11:05
поделиться

В вашем случае происходит то, что вы передаете обе переменные a и b с оператором & в функцию scanf. Этот оператор «запрашивает» адрес памяти переменной и передает этот адрес функции scanf. Но поскольку обе ваши переменные являются указателями, они действительно имеют адрес памяти, поэтому, когда вы передаете & a или & b, вы передаете память указателя, а не адрес памяти, который он содержит.

Пример:

int x;
int *ptr;

x = 10;

предположим, что адрес памяти x равен 1000. Вы сохраняете число 10 по адресу памяти 1000. Теперь вы делаете следующее:

ptr = &x;

Вы сохраняете адрес 1000 в указателе. Но 1000, помимо адреса, представляет собой само число, поэтому указателю, как и x, по-прежнему нужен адрес памяти для хранения этой информации. Предположим, что ячейка памяти указателя - 1004. Теперь посмотрим на пример:

*ptr == 10;  //x content
ptr == 1000 //x memory address
&ptr == 1004 // ptr memory address. 

Итак, если вы хотите передать сканированию переменную x, но используя указатель, вам нужно передать сохраненный в ней адрес x

scanf("%d", ptr);

Просто чтобы проиллюстрировать другой пример указателей и векторов

int main
{
    int vet[5];
    int *ptr;

    ptr = vet;

    for(int i = 0; i < 5; ++i)
    {
        scanf("%d", (ptr+i) );
    }
}

Здесь вы можете прочитать вектор с помощью указателя. Также, используя арифметику указателей, вы можете перебирать адреса памяти вектора.

1
ответ дан 4 December 2019 в 11:05
поделиться

Массив a помещается в стек, адрес первого элемента совпадает с адресом a. & a [0] и & a - это тот же адрес

Массив b выделяется с помощью malloc, адрес хранилища находится в куче, тогда как адрес указателя b находится в стеке. & b [0] - это не тот же адрес, что и & b .

Вот почему первый scanf и printf работают, а второй нет.

C-FAQ объясняет это в гораздо подробнее.

5
ответ дан 4 December 2019 в 11:05
поделиться

В первом сканировании вы передаете ссылку на массив. В массивах C указатели на блок памяти выделенного типа, в вашем случае int * и выражение типа a [0] преобразуется в * (a + 0 ) (что, кстати, приводит к забавному варианту 0 [a] , который будет фактически компилироваться.) Этот массив размещается в стеке. Второй массив размещается в куче, а стек содержит переменную-указатель на этот массив.

В обоих случаях вы передаете указатель не на первую запись массива, а на массив и указатель на массив соответственно.

Ваш первый scanf перезаписывает массив, поскольку он размещен в стеке, ваше значение попадает (по счастливой случайности) в массив.

Второй scanf перезаписывает указатель на массив, тем самым изменяя указатель на адрес памяти, который, вероятно, не существует в вашем сегменте данных. Это приводит к ошибке выполнения.

2
ответ дан 4 December 2019 в 11:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: