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

Как включить динамический массив INSIDE a struct в C?

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

 #include <stdlib.h>

struct my_struct {
    int n;
    char s[]
};

int main()
{
    struct my_struct ms;
    ms.s = malloc(sizeof(char*)*50);
}

и вот ошибка gcc дает мне: error: недопустимое использование элемента гибкого массива

Я могу заставить его скомпилировать, если я объявляю объявление s внутри структуры

char* s

и это, вероятно, превосходная реализация (арифметика указателя быстрее, чем массивы, да?) но я думал, что в c декларации

char s[]

совпадает с

char* s
4b9b3361

Ответ 1

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

#include <stdlib.h>

struct my_struct {
    int n;
    char s[];
};

Когда вы выделяете пространство для этого, вы хотите выделить размер структуры плюс объем пространства, который вы хотите для массива:

struct my_struct *s = malloc(sizeof(struct my_struct) + 50);

В этом случае элемент гибкого массива представляет собой массив из char и sizeof (char) == 1, поэтому вам не нужно умножать его размер, но, как и любой другой malloc, d нужно, если это был массив другого типа:

struct dyn_array { 
    int size;
    int data[];
};

struct dyn_array* my_array = malloc(sizeof(struct dyn_array) + 100 * sizeof(int));

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

Ответ 2

Вам нужно решить, что именно вы пытаетесь сделать в первую очередь.


Если вы хотите иметь структуру с указателем на [независимый] массив внутри, вы должны объявить его как

struct my_struct { 
  int n; 
  char *s;
}; 

В этом случае вы можете создать фактический объект структуры любым способом (например, как автоматическая переменная)

struct my_struct ms;

а затем выделять память для массива независимо

ms.s = malloc(50 * sizeof *ms.s);  

На самом деле нет общей необходимости динамически распределять память массива

struct my_struct ms;
char s[50];

ms.s = s;

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

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


Совершенно другой подход заключается в использовании идиомы "struct hack". В этом случае массив становится неотъемлемой частью структуры. Оба находятся в одном блоке памяти. В C99 структура будет объявлена ​​как

struct my_struct { 
  int n; 
  char s[];
}; 

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

struct my_struct *ms = malloc(sizeof *ms + 50 * sizeof *ms->s);

Размер блока памяти в этом случае вычисляется для размещения элементов структуры и конечного массива времени выполнения.

Обратите внимание, что в этом случае у вас нет возможности создавать такие объекты структуры, как статические или автоматические объекты. Структуры с гибкими элементами массива в конце могут динамически выделяться только в C.


Ваше предположение о том, что арифметика указателя быстрее, чем массивы, абсолютно неверна. Массивы работают по указателю арифметики по определению, поэтому они в основном одинаковы. Более того, подлинный массив (не распадающийся на указатель), как правило, немного быстрее, чем объект-указатель. Значение указателя должно считываться из памяти, а расположение массива в памяти "известно" (или "вычислено" ) из самого массива.

Ответ 3

Массивы будут разрешать указатели, и здесь вы должны определить s как char *s. Структура в основном представляет собой контейнер и должна (IIRC) быть фиксированным размером, поэтому наличие внутри нее массива динамического размера просто невозможно. Так как вы все равно malloc с памятью, это не должно иметь никакого значения в том, что вам нужно.

В основном вы говорите, s укажет место памяти. Обратите внимание, что вы можете получить доступ к этому позже, используя обозначение типа s[0].

Ответ 4

Арифметика указателя быстрее, чем массивы, да?

Совсем нет - они на самом деле одинаковы. массивы переводят на арифметику указателя во время компиляции.

char test[100];
test[40] = 12;

// translates to: (test now indicates the starting address of the array)
*(test+40) = 12;

Ответ 5

Использование массива неопределенного размера допускается только в конце структуры и работает только в некоторых компиляторах. Это нестандартное расширение компилятора. (Хотя я думаю, что я помню, что С++ 0x разрешит это.)

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

Что я делаю, просто дайте массиву небольшой, но ненулевой размер. Обычно 4 для массивов символов и 2 для массивов wchar_t для сохранения 32-битного выравнивания.

Затем вы можете принять объявленный размер массива во внимание при распределении. Я часто не полагаюсь на теорию о том, что slop меньше, чем гранулярность, с которой менеджер кучи работает в любом случае.

Кроме того, я думаю, вы не должны использовать sizeof (char *) в своем распределении.

Это то, что я сделал бы.

struct my_struct {
    int nAllocated;
    char s[4]; // waste 32 bits to guarantee alignment and room for a null-terminator
};

int main()
{
    struct my_struct * pms;
    int cb = sizeof(*pms) + sizeof(pms->s[0])*50;
    pms = (struct my_struct*) malloc(cb);
    pms->nAllocated = (cb - sizoef(*pms) + sizeof(pms->s)) / sizeof(pms->s[0]);
}

Ответ 6

Я подозреваю, что компилятор не знает, сколько места ему потребуется выделить для s [], если вы решите объявить с ним автоматическую переменную.

Я согласен с тем, что сказал Бен, объявляя вашу структуру

struct my_struct {
    int n;
    char s[1];
};

Кроме того, чтобы прояснить его комментарий о хранении, объявление char *s не будет помещать структуру в стек (поскольку оно динамически выделено) и выделять s в куче, то, что он будет делать, это интерпретировать первый sizeof(char *) байтов вашего массива в качестве указателя, поэтому вы не будете работать с данными, которые, по вашему мнению, являетесь, и, вероятно, будут фатальными.

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

Ответ 7

Существует множество ответов относительно гибкого массива C99.

Я хотел бы прокомментировать ответ Александра Гесслера относительно указателей, которые совпадают с массивами.

Это не так; Массивы - это выражение, указатели - переменная.

У них есть тонкие отличия, особенно при пересечении БОЛЬШИХ объемов данных. Иногда вам нужно выжимать каждый mSec (я работаю над встроенными графическими системами).

Ответ 8

генерируемый код будет идентичным (array и ptr). Помимо того факта, что массив не будет компилироваться, это

и BTW - сделайте это С++ и используйте вектор