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

Динамический массив и готовые данные

В C я пытаюсь сделать следующее:

typedef struct {
    int length;
    int items[];     /* 1 */
} wchararray_t;

typedef struct {
    long hash;
    wchararray_t chars;   /* 2 */
} string_t;

static string_t s1 = {
    617862378,
    { 5, { 'H', 'e', 'l', 'l', 'o' } }  /* 3 */
};

В полных словах мне бы хотелось, чтобы тип string_t заканчивался другим типом wchararray_t, который сам по себе имеет динамический размер - его размер хранится в length. Более того, я также хотел бы написать предварительно построенную конкретную строку, как статические данные, здесь s1 длины 5.

В приведенном выше коде предполагается поддержка C99 для /* 1 */. Включение субструктуры в большую структуру при /* 2 */, насколько я понимаю, не поддерживается даже стандартом C99, но GCC принимает его. Однако при /* 3 */ GCC отказывается:

error: initialization of flexible array member in a nested context

В качестве обходного пути идеальный код выше до сих пор был написан как следующий хак, который "работает":

typedef struct { int length; int items[1]; } wchararray_t;
typedef struct { long hash; wchararray_t chars; } string_t;

typedef struct { int length; int items[5]; } wchararray_len5_t;
typedef struct { long hash; wchararray_len5_t chars; } string_len5_t;

static union { string_len5_t a; string_t b; } s1 = {
    617862378,
    { 5, { 'H', 'e', 'l', 'l', 'o' } }
};

... и мы будем использовать "s1.b" как prebuilt string_t (и никогда не будем ссылаться на "s1.a", который здесь только для статического объявления s1). Однако он разбивается на новейший GCC 4.8, который оптимизирует части нашего кода, потому что, очевидно, любой цикл над items для wchararray_t может выполнять итерацию только один раз, учитывая, что это массив длиной 1.

Эта конкретная проблема исправлена, предоставив gcc опцию -fno-aggressive-loop-optimizations. Вероятно, он также может быть исправлен, если не объявить длину в массиве wchararray_t items[], что делает его динамическим массивом "только потому, что". Тем не менее, этот способ писать код - это такой хак, который я бы предпочел совершенно другим способом подойти к проблеме...

(Обратите внимание, что это все сгенерированный код C, созданный PyPy, в отличие от написанного вручную кода, любое изменение в порядке, в том числе, если оно требует изменения способа доступа к данным во всем мире, если только "действительный" C оптимизация не предотвращается.)

ИЗМЕНИТЬ: заменить "char []" на "int []", который не принимает синтаксис с двойной кавычкой "hello". Это потому, что я ищу решение для любого типа массива.

НЕ ПОСТАНОВИЛИ: благодарит всех за ваши предложения. Кажется, что нет чистого пути, поэтому я реализовал хакерское решение: объявляя типы k + 1 раз, один раз с помощью гибкого массива "int items []"; и k в других случаях с "int items [N];" для различных значений N, которые необходимы. Это требует некоторых дополнительных хаков: например. не используя гибкие массивы для MSVC (они работают по-разному там, я не исследовал, знаю ли точно такой же синтаксис); и GCC следует тому, что C99 говорит и недовольна структурами, которые содержат int items[]; как только поле. Тем не менее, мы счастливы, если добавить фиктивное поле char _dummy[0];... которое не является строго C99, насколько я знаю...

4b9b3361

Ответ 1

Он взламывает, но может ли это работать?

#include <stdio.h>

typedef struct {
    int length;
    int items[];     /* 1 */
} wchararray_t;

typedef struct {
    long hash;
    wchararray_t chars;   /* 2 */
    int dummy[]; /* hack here */
} string_t;

static string_t s1 = {
    617862378, { 5 },
    { 'H', 'e', 'l', 'l', 'o' }  /* 3: changed assignment */
};

int main(void)
{
    int i;
    for (i=0; i < 5; ++i) {
        putchar(s1.chars.items[i]);
    }
    putchar('\n');
    return 0;
}

GCC дает мне предупреждения:

xx.c:10:22: warning: invalid use of structure with flexible array member [-pedantic]
xx.c:16:9: warning: initialization of a flexible array member [-pedantic]
xx.c:16:9: warning: (near initialization for ‘s1.dummy’) [-pedantic]

Но он работает.

Ссылка

Изменить: как добавить добавочный элемент, который гарантирует, что items[] всегда правильно выровнен?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

/* change to the strictest alignment type */
typedef long aligner;

typedef struct {
    long stuff;   /* to show misalignment on 64-bit */
    int length;
    aligner padding;
    int items[];
} chararray_t;

typedef struct {
    long hash;
    chararray_t chars;
    int dummy[];
} string_t;

static string_t b1 = {
    617862378,
    { 42, 5 },
    {-1, -2, -3, -4, -5}
};

int main(void)
{
    int i;

    printf("sizeof chararray_t: %zu\n", sizeof(chararray_t));
    printf("offsetof items: %zu\n", offsetof(chararray_t, items));

    printf("sizeof string_t: %zu\n", sizeof(string_t));
    printf("offsetof dummy: %zu\n", offsetof(string_t, dummy));

    for (i=0; i < 5; ++i) {
        printf("%d ", b1.chars.items[i]);
    }
    putchar('\n');
    for (i=0; i < 5; ++i) {
        printf("%d ", b1.dummy[i]);
    }
    putchar('\n');
    return 0;
}

Когда я запускаю выше, я, кажется, получаю правильный ответ:

sizeof chararray_t: 24
offsetof items: 24
sizeof string_t: 32
offsetof dummy: 32
-1 -2 -3 -4 -5 
-1 -2 -3 -4 -5 

Ответ 2

Отвечая на мой вопрос, чтобы записать его. Еще один взлом должен был бы основываться на предложении Alok, который может дать время от времени фальшивое выравнивание --- и затем исправить выравнивание по времени-времени. Это предполагает, что большая часть таких типов, используемых в программе, должна быть правильно выровнена. Код:

typedef struct {
    long stuff;   /* to show misalignment on 64-bit */
    int length;
    int items[];
} chararray_t;

typedef struct {
    long hash;
    chararray_t chars;
    int dummy[];
} string_t;


static string_t b1 = {
    617862378,
    { 42, 5 },
    {-1, -2, -3, -4, -5}
};
/* same with b2 .. b6 */

void fixme(void) {
    /* often compares as equal, and the whole function is removed */
    if (offsetof(string_t, dummy) !=
            offsetof(string_t, chars) + offsetof(chararray_t, items)) {
        static string_t *p_array[] = { &b1, &b2, &b3, &b4, &b5, &b6 };
        string_t *p;
        int i;
        for (i=0; i<6; i++) {
            p = p_array[i];
            memmove(p->chars.items, p->dummy, p->chars.length * sizeof(int));
        }
    }
}

Ответ 3

#include <stdio.h>
typedef struct {
    int length;
    char items[];     /* 1 */
} chararray_t;

typedef struct {
    long hash;
    chararray_t chars;   /* 2 */
} string_t;

/*static string_t s1 = {
    617862378,
    { 5, { 'H', 'e', 'l', 'l', 'o' } }  // 3
};*/

static string_t s1 =
{
    617862378,
    {6,"Hello"} /* 3 */
};

int main()
{
    printf("%d %d %s\n",s1.hash,s1.chars.length,s1.chars.items);
    return 0;
}

Добавьте 1 для нулевого символа, et voila!:)

Изменить, также работает для 2 уровней вложенности (GCC 4.8.0)

#include <stdio.h>
typedef struct {
    int length;
    char items[];     /* 1 */
} chararray_t;

typedef struct {
    long hash;
    chararray_t chars;   /* 2 */
} string_t;

typedef struct {
    long number;
    string_t arr;
}experiment_t;

static experiment_t s1 =
{
    617862378,
    {786,{6,"Hello"}} /* 3 */
};

int main()
{
    printf("%d %d %d %s\n",s1.number,s1.arr.hash,s1.arr.chars.length,s1.arr.chars.items);
    return 0;
}

---------- ИЗМЕНИТЬ 2 ------------------ Найден способ ограничения ограничения C инициализировать массив внутри структуры

Конечный код::

#include <stdio.h>
typedef struct {
    int length;
    int *items;     /* 1 */
} intarray_t;

typedef struct {
    long hash;
    intarray_t chars;   /* 2 */
    int dummy[2];
} string_t;

/*string_t s1 =
{
    617862378,
    {
        6,
        {1,2,3,4,5,6}
    },
    {
        0,0
    }
};*/

string_t s1 = {617862378,{},{0,0}};

int main()
{
    int i=0;
    intarray_t  t1 = {.length = 6, .items = (int[6]){1,2,3,4,5,6}};
    s1.chars = t1;
    printf("%d %d\n",s1.hash,s1.chars.length);
    while(i<s1.chars.length)
    {
        printf("%d",s1.chars.items[i]);
        i++;
    }
    putchar('\n');
    return 0;
}

Ответ 4

Я предполагаю, что есть какая-то причина для сохранения строки "внутри" структуры и вы хотите сохранить char, не инициализируя C-строкой.

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

typedef struct {
    int length;
    char *items;     /* 1 */
} chararray_t;

typedef struct {
    long hash;
    chararray_t chars;   /* 2 */
} string_t;

static string_t s1 = {
    617862378,
    { 5, "Hell" }  /* 3 */
}; 
s1.chars.items[4] = 'o' ;

Похоже, вы можете сделать трюк профсоюза, но вместо этого вместо него вместо него?

#include <stdio.h>

typedef struct { int length; int items[]; } wchararray_t;
typedef struct { long hash; wchararray_t chars; } string_t;

typedef struct { int length; int items[5]; } wchararray_len5_t;
typedef struct { long hash; wchararray_len5_t chars; } string_len5_t;

static union { string_len5_t a; string_t b; } s5 = {
    617862378,
    { 5, { 'H', 'e', 'l', 'l', 'o' } }
};

string_t *s1 = (string_t*) &s5 ;

int main( int argc, char *argv[])
{

  for( int i = 0 ; i < s1->chars.length ; i++ )
    {
      printf ( "%c", s1->chars.items[i] );
    }
  printf( "\n" );
}