Указатели на массив в C

#c #arrays #pointers

#c #массивы #указатели

Вопрос:

 #include <stdio.h>

void main()
{
 int a[]={1,2,3,4};

 printf("%u %u ",a ,amp;a);           //a
 printf("%d %d ", *a ,*amp;a);        //b
}
  

В строке ath вывод a и amp;a тот же адрес, но в строке bth *amp;a не дает мне ответа as 1 .

Я знаю, что amp;a это означает указатель на массив целых чисел, но поскольку адрес тот же, он должен печатать 1 правильно?

Комментарии:

1. Прочитайте раздел 6 comp.lang.c . И void main() должно быть int main(void) . И ты пропал #include <stdio.h> .

2. Использование %u in printf для печати значений указателей является неопределенным поведением (и на практике это может легко привести к бессмысленным результатам). Есть %p специально для печати указателей. И вы должны преобразовать указатели в void * , прежде чем отправлять их printf .

Ответ №1:

a распадается до указателя на первый элемент массива.
amp;a является указателем на массив из 4 int сек.

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

Тип a (после того, как он распадается на указатель) равен int* .
Тип amp;a — это указатель на массив из 4 int s — int (*)[4] .

Тип *a — это an int .
Тип *amp;a — это массив из 4 целых int [4] чисел , который распадается до указателя на первый элемент в вашем выражении.

Вызов

 printf("%d %d ", *a ,*amp;a);
  

эквивалентно:

 printf("%d %d ", *a , a);
  

Кстати, вы должны использовать %p для указателей. В противном случае вы вызываете неопределенное поведение. Увеличьте уровень предупреждения вашего компилятора, чтобы избежать подобных ошибок.

Ответ №2:

Берем более простую версию вашего кода следующим образом:

 #include <stdio.h>

void main()
{
 int a[]={1,2,3,4};

 printf("%d %d ", *a ,*amp;a);        //b
}
  

Если я скомпилирую этот код, я получу это предупреждение:

 test1.c
D:Temptest1.c(7): warning C4477: 
    'printf' : format string '%d' requires an argument of type 'int', but variadic 
               argument 2 has type 'int *'
Microsoft (R) Incremental Linker Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.
  

Это предупреждающее сообщение дает вам причину, по которой это работает не так, как вы ожидаете.

Теперь я могу изменить этот код, чтобы удалить эти предупреждения, и в итоге получаю такой код:

 #include <stdio.h>

void main()
{
 int a[]={1,2,3,4};

 int (*p)[4] = amp;a;

 printf("n%u %u ", *a ,*p[0]);        //b
}
  

Этот чистый код компилируется, и при его запуске вы получаете ожидаемый результат:

 1 1 
  

Ответ №3:

Последний проект стандарта C предполагает, что

Если операнд является результатом унарного оператора *, ни этот оператор, ни оператор amp; не вычисляются, и результат будет таким, как если бы оба были опущены

Однако приведенный выше абзац относится к случаю, когда это amp;* , а не *amp; . Попробуйте подумать об этом так:

   amp;a   // pointer to array[4] of integers
*(amp;a)  // array[4] of integers
------------------------------------------
   a   // array[4] of integers
  

Обратите внимание, что оператор * удаляет один слой указателей. Следовательно, *(amp;a) a семантически идентично. В этом случае a преобразуется в выражение с типом pointer to an integer , потому что в другом абзаце предполагается, что

За исключением случаев, когда это операнд оператора sizeof, оператора _Alignof или унарного оператора amp; или строковый литерал, используемый для инициализации массива, выражение, имеющее тип ‘массив типа’, преобразуется в выражение с типом ‘указатель на тип’, который указывает на начальный элементобъекта массива и не является значением lvalue…

В заключение, тот факт, что выводится адрес a (а не элемент внутри), является естественным.

Ответ №4:

Пожалуйста, сделайте еще одну вещь в своем коде:

printf("%u %u ",a 1 ,amp;a 1); //a1