Массив [] против указателя * - почему первый код потерпит неудачу в 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];
- За исключением строковых литералов, таких как
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 = ...", но ни одна из этих функций никогда не вызывалась из приложения.
Доступ к переменной обычно не имеет никакого отношения к тому, готов ли компилятор принять код.
Есть ли какая-то разница между [] и *, о которой я не знаю?
Видимо, так как вопрос, кажется, предполагает, что нет никакой разницы.
Эти две формы могут использоваться взаимозаменяемо для объявления параметров функции. В этом контексте оба объявляют параметр как указатель. Это удобство нотации и ясности кода стало возможным благодаря тому факту, что значения массивов, появляющиеся как аргументы функций, распадаются на указатели. Вы никогда не сможете передать массив в качестве аргумента функции - когда аргумент обозначает массив, вместо него передается указатель.
Как описано выше, однако, эти две формы не эквивалентны для объявления обычной переменной.