Подтвердить что ты не робот

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

Как эксперт, не являющийся C/С++, я всегда считал квадратные скобки и массивы указателей равными.

т.е.:

char *my_array_star;
char my_array_square[];

Но я заметил, что при использовании в структуре/классе они не ведут себя одинаково:

typedef struct {
   char whatever;
   char *my_array_star;
} my_struct_star;

typedef struct {
   char whatever;
   char my_array_square[];
} my_struct_square;

В приведенной ниже строке 16, whatever занимает 1 байт, my_array_pointer занимает 8 байтов. Из-за заполнения полный размер структуры составляет 16.

printf("my_struct_star: %li\n",sizeof(my_struct_star));

В строке ниже отображается 1, whatever принимает 1 байт, my_array_pointer не учитывается.

printf("my_struct_square: %li\n",sizeof(my_struct_square));

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

my_struct_square  *i=malloc(2);

i->whatever='A';
i->my_array_square[0]='B';

отобразится линия A:

printf("i[0]=%c\n",((char*)i)[0]);

отобразится строка B:

printf("i[1]=%c\n",((char*)i)[1]);

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

4b9b3361

Ответ 1

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

Массивы - это группа смежных элементов, в то время как указатель... ну... указатель на один элемент.

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

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

Одно из мест, которое это происходит, - в вызовах функций. Когда вы передаете массив функции, она распадается на указатель. Вот почему такие вещи, как размер массива, явно не передаются функции. Под этим я подразумеваю:

#include <stdio.h>

static void fn (char plugh[]) {
    printf ("size = %d\n", sizeof(plugh)); // will give char* size (4 for me).
}

int main (void) {
    char xyzzy[10];
    printf ("size = %d\n", sizeof(xyzzy)); // will give 10.
    fn (xyzzy);

    return 0;
}

Другое, что вы обнаружите, состоит в том, что, хотя вы можете plugh++ и plugh-- в своем содержании (до тех пор, пока вы не будете разыменовываться за пределами массива), вы не сможете сделать это с помощью массив xyzzy.

В ваших двух структурах есть большая разница. В версии указателя у вас есть указатель фиксированного размера внутри структуры, который указывает на элемент вне структуры.

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

+----------------+
| 1 char variable|
+----------------+
| 7 char padding |
+----------------+
| 8 char pointer |
+----------------+

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

+----------------+
| 1 char variable|
+----------------+
| 0 char array   |
+----------------+

Но вы можете выделить больше места, например:

typedef struct {
   char whatever;
   char my_array_square[];
} my_struct_square;

my_struct_square twisty = malloc (sizeof (my_struct_square) + 10);

дает вам переменную twisty, которая имеет символ whatever и массив из десяти символов с именем my_array_square.

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

Ответ 2

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

Схема использования такой структуры будет следующей:

my_struct_square *s = malloc(sizeof(my_struct_square) + 5 * sizeof(char));
...
s->my_array_square[4]; // the last element of the array

Во всех остальных случаях размер массива должен быть известен во время компиляции. Даже тип массива совпадает с его размером, т.е. int a[20] имеет тип int[20], а не только int[].

Также важно понимать разницу между массивами и указателями. @paxdiablo достаточно хорошо это рассмотрел.