Понимание различий между различными указателями C - PullRequest
0 голосов
/ 20 февраля 2020

Я пытаюсь понять разницу между двумя типами назначения указателей.

uint8_t pInputByte[byteCount];
uint8_t* pNextInputBytes = pInputByte;

В этом случае, поскольку pInputByte [byteCount] является массивом, записывает ли вторая строка адрес этого массива в pNextInputByte? Чтобы уточнить, просто вызывая имя переменной массива без индекса, верните ее адрес?

Другой мой вопрос: как будет отличаться операция, если мы напишем ее следующим образом.

uint8_t pInputByte[byteCount];
uint8_t* pNextInputBytes;
pNextInputBytes = &pInputByte;

Ответы [ 3 ]

2 голосов
/ 20 февраля 2020

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

Из стандарта C (6.3.2.1 L-значения, массивы и указатели функций)

3 За исключением случаев, когда это операнд оператора sizeof или унарный оператор &, или строковый литерал, используемый для инициализации массива, выражение с типом '' массив типа '' преобразуется в выражение с типом '' указатель на тип '', который указывает на начальный элемент объекта массива и не является lvalue. Если объект массива имеет класс хранения регистров, поведение не определено.

Таким образом, в этом объявлении

uint8_t* pNextInputBytes = pInputByte;

массив, используемый в качестве инициализатора, преобразуется в указатель на свой первый элемент и имеет тип int *. Это значение присваивается объявленному указателю.

В этом операторе

pNextInputBytes = &pInputByte;

применяется унарный оператор & для объекта типа массива. Таким образом, тип выражения &pInputByte равен uinmt8_t ( * )[byteCount]. Но в левой части задания есть объект типа uint8_t *. Не существует неявного преобразования из одного типа в другой. Таким образом, компилятор выдаст ошибку.

Вместо этого вы можете написать

uint8_t pInputByte[byteCount];
uint8_t (*pNextInputBytes )[byteCount];
pNextInputBytes = &pInputByte;

Чтобы было более понятно, введите имя typedef для типа массива

typedef uint8_t T[byteCount];

Затем Вы можете объявить массив и указатель на массив следующим образом:

T pInputByte;
T *pNextInputBytes = &pInputByte;

Теперь, если подставить имя typedef T в объявлении указателя для определенного типа, вы получите

uint8_t ( *pNextInputBytes )[byteCount] = &pInputByte;

Чтобы увидеть разницу между двумя указателями, один из которых имеет тип uint8_t *, а другой uint8_t ( * )[byteCount], напишите следующую демонстрационную программу.

#include <stdio.h>
#include <stdint.h>

int main(void) 
{
    size_t byteCount = 10;
    uint8_t pInputByte[byteCount];
    uint8_t *pNextInputBytes1 = pInputByte;
    uint8_t ( *pNextInputBytes2 )[byteCount] = &pInputByte;

    printf( "sizeof( *pNextInputBytes1 ) = %zu\n", sizeof( *pNextInputBytes1 ) );
    printf( "sizeof( *pNextInputBytes2 ) = %zu\n", sizeof( *pNextInputBytes2 ) );

    return 0;
}

Вывод программы:

sizeof( *pNextInputBytes1 ) = 1
sizeof( *pNextInputBytes2 ) = 10

Обратите внимание, что префикс p, используемый в имени массива, может сбить читателей с кода.

1 голос
/ 20 февраля 2020

1.

uint8_t pInputByte[byteCount];
uint8_t* pNextInputBytes = pInputByte;

В этом случае, поскольку pInputByte[byteCount] является массивом, записывает ли вторая строка адрес этого массива в pNextInputByte?

Не адрес массива, адрес первого элемента массива. Но в принципе вы правы.

Чтобы уточнить, просто вызов имени переменной массива без индекса возвращает ее адрес?

Массив, когда он не используется с элементом индекса [i] затухает до указателя на первый элемент и возвращает адрес этого первого элемента, за исключением того, что пользователь ex nihilo упоминается в комментариях, когда он является операндом sizeof (), унарным & оператор или строковый литерал, используемый для инициализации другого массива; выражение с типом '' массив типа '' преобразуется в выражение с типом '' указатель на тип '', которое указывает на начальный элемент объекта массива и не является lvalue. . Цитата из ISO / IEC 9899: 2011 (C11).


2.

Мой другой вопрос: как будет отличаться операция, если мы напишем ее следующим образом:

uint8_t pInputByte[byteCount];
uint8_t* pNextInputBytes;
pNextInputBytes = &pInputByte;

Третья строка - проблема. Вам нужно опустить оператор амперсанда &, чтобы правильно скомпилировать его без каких-либо предупреждений:

pNextInputBytes = pInputByte;

Но если вы смешаете эту опечатку, оба кода эквивалентны, да.


Кстати, pInputByte не должно предшествовать p, когда p будет означать «указатель» (я могу только строить догадки).

0 голосов
/ 20 февраля 2020
  1. uint8_t pInputByte[byteCount]; uint8_t* pNextInputBytes = pInputByte;

В этом примере мы присваиваем адрес массива, который указывает на первый элемент в массиве. Например, когда вы пытаетесь получить доступ к первому элементу массива, вы можете сказать * pNextInputBytes, а для следующего элемента * (pNextInputBytes + 1) - +1 приведет вас к адресу второго элемента в массиве.

uint8_t pInputByte[byteCount]; uint8_t* pNextInputBytes; pNextInputBytes = &pInputByte;

В этом примере мы берем адрес массива, но он указывает на весь массив. т.е. если pInputByte [5], то & pInputByte будет указывать весь массив из 5 элементов. Поэтому, когда вы пытаетесь увеличить его как (pNextInputBytes + 1), тогда адрес фактически переходит на 5 адресов. Это полезно в многомерных массивах. Предположим, что двойной массив [5] [4] является двумерным массивом. Здесь «массив» - указатель на массив из 4 int, а «& массив» - указатель на массив из 5 строк, массив из 4 int »

...