Массив [] против указателя * - почему первый код потерпит неудачу в C?

Я использую компилятор Keil C51 для программирования микроконтроллера 8051. По какой-то причине мой код не запустился - мне удалось отследить ошибку, но мне все еще трудно ее понять. Почему первый код неправильный по сравнению с другим? Стоит отметить, что компилятор не выдавал никакой ошибки, код просто не запускался на микроконтроллере.

Неправильный код:

file1.h

extern STRUCT_TYPEDEF array_var[];

file2.c

// Global variable initialization
STRUCT_TYPEDEF array_var[] = some_struct.array2_var;

После изменения их на:

file1.h

extern STRUCT_TYPEDEF *array_var;

file2.c

// Global variable initialization
STRUCT_TYPEDEF *array_var = &some_struct.array2_var[0];

это начало работать.

Кроме того, эта часть кода упоминалась только в таких функциях, как "array_var[0].property = ...", но ни одна из этих функций никогда не вызывалась из приложения.

Переменная some_struct объявлена ​​в еще одном модуле.

Почему он может так себя вести? Есть ли какая-то разница между [] и *, о которой я не знаю?

РЕДАКТИРОВАТЬ 1: Говорят, что указатели и массивы разные вещи... но тогда, как синтаксис "[]" отличается от "*"? Я думал, что компилятор просто конвертирует его в указатель в случае, если квадратные скобки пусты (как это происходит с аргументами функции). Я также подумал, что предоставление массива даст мне адрес первого элемента.

Теперь все говорят, что указатели и массивы разные, но я не могу найти никакой информации о том, что именно в них отличается. Как компилятор видит это, когда я даю массив как значение вместо указателя на его первый элемент?

2 ответа

STRUCT_TYPEDEF array_var[] = some_struct.array2_var;

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

T arr[] = { init1, init2, init3 };

Вы не можете инициализировать массив другим массивом 1 и не можете назначить один массив другому таким образом:

T foo[] = { /* list of initializers */ }
T bar[] = foo; // not allowed
T bar[N];
...
bar = foo; // also not allowed

Если вы хотите скопировать содержимое some_struct.array2_var в array_var, вы должны использовать библиотечную функцию, такую ​​как memcpy:

memcpy( array_var, some_struct.array2_var, sizeof some_struct.array2_var );

Вы также должны объявить array_var с размером; Вы не можете оставить его неполным, если хотите его использовать. Если вы заранее знаете, насколько большим он должен быть, это легко:

STRUCT_TYPEDEF array_var[SIZE];
...
mempcy( array_var, some_struct.array2_var );

Если вы заранее не знаете, насколько большим он должен быть, то вам придется либо объявить его как массив переменной длины (который не будет работать, если он должен находиться в области видимости файла, либо иначе static продолжительность хранения), или вы можете объявить память динамически:

STRUCT_TYPEDEF *array_var = NULL;
...
array_var = malloc( sizeof some_struct.array2_var );
if ( array_var )
{
  memcpy( array_var, some_struct.array2_var, sizeof some_struct.array2_var );
}

Это все предполагает, что some_struct.array2_var массив объявлен как

STRUCT_TYPEDEF array2_var[SIZE]; 

Если это также просто указатель, то вам придется отслеживать размер массива другим способом.

РЕДАКТИРОВАТЬ

Если ты хочешь array_var просто указать на первый элемент some_struct.array2_var, вы бы сделали следующее:

STRUCT_TYPEDEF *array_var = some_struct.array2_var;

За исключением случаев, когда это операнд sizeof или одинарный & операторы, выражение типа "N-элемент массива T "будет преобразован (" распад ") в выражение типа" указатель на T ", а значением выражения будет адрес первого элемента массива. Код выше в точности эквивалентен

STRUCT_TYPEDEF *array_var = &some_struct.array2_var[0];


  1. За исключением строковых литералов, таких как char message[] = "Hello";; строковый литерал "Hello" является выражением массива, но язык рассматривает его как особый случай.

Это...

extern STRUCT_TYPEDEF array_var[];

... является объявлением массива неизвестного размера и с внешней связью. Поскольку размер не указан, то объявление выходит array_var с "неполным типом"; это предотвращает некоторое использование этой переменной до тех пор, пока ее тип не будет завершен другим объявлением в том же модуле перевода. Например, это не может быть операндом sizeof оператор.

Это...

STRUCT_TYPEDEF array_var[] = some_struct.array2_var;

... утверждает, что является определением array_var, за счет предоставления инициализатора. Однако инициализатор имеет неправильную форму для переменной типа массива. Инициализатор массива состоит из разделенной запятыми последовательности одного или нескольких элементов массива внутри обязательных фигурных скобок ({}). Так же, как C не поддерживает присваивание целого массива, он не поддерживает значения массива как инициализаторы массива.


В отличие от этого...

extern STRUCT_TYPEDEF *array_var;

... это объявление указателя с внешней связью. Он имеет полный тип. И это...

STRUCT_TYPEDEF *array_var = &some_struct.array2_var[0];

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

STRUCT_TYPEDEF *array_var = some_struct.array2_var;

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

Кроме того, эта часть кода упоминалась только в таких функциях, как "array_var[0].property = ...", но ни одна из этих функций никогда не вызывалась из приложения.

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

Есть ли какая-то разница между [] и *, о которой я не знаю?

Видимо, так как вопрос, кажется, предполагает, что нет никакой разницы.

Эти две формы могут использоваться взаимозаменяемо для объявления параметров функции. В этом контексте оба объявляют параметр как указатель. Это удобство нотации и ясности кода стало возможным благодаря тому факту, что значения массивов, появляющиеся как аргументы функций, распадаются на указатели. Вы никогда не сможете передать массив в качестве аргумента функции - когда аргумент обозначает массив, вместо него передается указатель.

Как описано выше, однако, эти две формы не эквивалентны для объявления обычной переменной.

Другие вопросы по тегам