Прежде чем я отвечу на это, в вашем коде есть ошибка, которую необходимо исправить:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a; // <<< remove * from declaration, such that a is a normal int
int *b= malloc(sizeof(int)*100);
scanf("%d",&a);
scanf("%d",b);
printf("%d",a);
printf("%d",*b);
return 0;
}
Итак ...
В этом коде почему указатель намассив не нуждается в расположении адреса для scanf по сравнению с обычным указателем?
При вызове scanf
спецификатор %d
ожидает, что соответствующий аргумент будет иметь тип int *
, чтоместоположение объекта, в которое scanf
ожидается запись целочисленного значения.Вы объявили b
как int *
, поэтому выражение b
уже имеет правильный тип, и вы установили его так, чтобы он указывал на допустимую область памяти, в которуюscanf
может записать целочисленное значение.
В приведенном выше фиксированном коде a
имеет тип int
, поэтому мы должны использовать выражение &a
, чтобы получить значение int *
.Это значение является location из a
, где scanf
записывает целочисленное значение ввода.
Также для printf в случае указателя на массив, почему мы должны давать символ звездочки, а не только переменную указателя?
В printf
При вызове спецификатор преобразования %d
ожидает, что соответствующий аргумент будет иметь тип int
, то есть целое число значение , которое вы хотите отформатировать и записать в выходной поток.Мы объявили a
как простое int
, поэтому выражение a
уже имеет правильный тип и значение выраженияформатируется и записывается в выходной поток.
Мы объявили b
как int *
, поэтому он хранит значение адреса, а не целое число.Мы должны разыменовать b
, используя унарный оператор *
, чтобы получить целочисленное значение, на которое он указывает. выражение *b
имеет тип int
, и его значением является целочисленное значение, сохраненное в местоположении, на которое указывает b
.
И, чтобы быть досадно педантичным, есть некоторая терминология, которую я хочу прояснить.b
не указывает на массив - он указывает на отдельный объект int
, который просто оказывается первым элементом массива (или в данном случае, динамически размещенным буфером).Указатель на массив выглядит следующим образом:
int a[N];
int (*b)[N] = &a;
или
int (*b)[N] = malloc( sizeof *b );
Мы не будем вдаваться в это здесь, потому что вы небудет иметь дело с указателями на массивы, которые часто.
Вы можете переписать свои scanf
и printf
вызовы выше как:
scanf( "%d", &b[0] ); // note & operator in this case
...
printf( "%d", b[0] );
Подстрочное выражение a[i]
равно , определено как *(a + i)
- даноадрес a
, смещение i
элементов (не байт!) от этого адреса и разыменование результата.
Из-за неявной операции разыменования выражение b[0]
имеет тип int
, а не int *
, поэтому нам необходим оператор &
в scanf
и не нужен оператор *
в вызове printf
.