Указатель на массив против обычной разницы указателей - PullRequest
0 голосов
/ 01 марта 2019
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *a;
    int *b= malloc(sizeof(int)*100);
    scanf("%d",&a);
    scanf("%d",b);
    printf("%d",a);
    printf("%d",*b);
    return 0;
}

В этом коде, почему указателю на массив не требуется расположение адреса для scanf по сравнению с обычным указателем?Также для printf в случае указателя на массив, почему мы должны давать символ звездочки, а не только переменную указателя?

Ответы [ 3 ]

0 голосов
/ 01 марта 2019

На самом деле в вашем коде есть опечатка, она работает таким образом, только если вы используете "int a", например:

int a;
int *b= (int *)malloc(sizeof(int)*100);
scanf("%d",&a);
scanf("%d",b);
printf("%d",a);
printf("%d",*b);

В этом случае должно быть ясно: scanf всегда нужен указатель,поэтому вы используете оператор & address для a, а не для b, который уже является указателем.Для printf вам нужно разыменовать указатель b, в то время как a уже является int.

, если вы настаиваете на использовании "int * a", вы должны написать

int *a;
int *b= (int *)malloc(sizeof(int)*100);
scanf("%d",a);
scanf("%d",b);
printf("%d",*a);
printf("%d",*b);

, и, как вы видите,указатели обрабатываются точно так же.Однако указатель * не определен (указывает «никуда») и, таким образом, может произойти сбой при использовании в scanf.

0 голосов
/ 01 марта 2019

Прежде чем я отвечу на это, в вашем коде есть ошибка, которую необходимо исправить:

#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.

0 голосов
/ 01 марта 2019

Хотя printf и scanf имеют сходный синтаксис для спецификаторов формата, они не совсем одинаковы.В частности, %d для scanf ожидает int *, так как для него требуется адрес переменной для записи, в то время как %d для printf ожидает int, поскольку для печати просто требуется значение.

Проблема с этим кодом связана не с тем, на что указывают a и b, а с тем, как они используются в вызовах printf и scanf.

Просмотр каждой строки:

scanf("%d",&a);

Это недопустимо, потому что спецификатор формата %d для scanf ожидает int *, но вы передаете int **.

scanf("%d",b);

Это допустимокак b является int *.Он будет записывать int в первый элемент массива.

printf("%d",a);

Это недопустимо, потому что спецификатор формата %d для printf ожидает int, но вы передаете int *.

printf("%d",*b);

Это действительно, так как *b является int.Будет напечатан первый элемент массива.

...