Заполнение массива глобальной структуры
Подскажите, пожалуйста, возможно ли заполнить массив включенный в глобальную структуру циклом при этом не в теле функции.
#include <stdio.h>
#include <stdint.h>
#define MY_SIZE 150
typedef struct {
uint32_t Num;
uint32_t Enable;
uint32_t a;
uint32_t b[MY_SIZE];
} userstruct;
userstruct myStruct = {
.Num = 6,
.Enable = 1,
.a = 10,
for (uint8_t i = 0; i < MY_SIZE; i++) {
.b[i] = i;
}
};
int main() {
printf("%d", myStruct.b[149]);
return 0;
}
Конечно, можно это сделать в main через цикл, но есть ли способ сделать это иначе не в теле функции? Текущий код выдает ошибку: "error: expected expression before ‘for’"
Ответы (4 шт):
#include <stdio.h>
struct user {
int a;
int b;
char *c;
} user = {1, 2, "koko"},
l = {33, 22, "pituh"};
typedef struct user user_t;
int main() {
printf("%d %d %s\n", user.a, user.b, user.c);
user_t b = {.a = 12, .c = "jojo", .b = -1};
printf("%d %d %s\n", b.a, b.b, b.c);
return 0;
}
Статические переменные компилятор не умеет динамически создавать.
Существую способы мета-программирования в Си используя двойную компиляцию. Вы создаёте программу, которая сама пишет код на Си, а потом компилируем уже всю программу.
makemeta.c :
# include <stdio.h>
# include <stdint.h>
# define MY_SIZE 150
int main(){
FILE * f = fopen("meta.h","w");
fprintf(f,"// код создан автоматически, для изменения используйте\n");
fprintf(f,"// gcc makemeta.c -o makemeta && ./makemeta\n");
for (uint8_t i = 0; i < MY_SIZE; i++) {
fprintf(f,"[ %u ] = %u ,\n" , i , i ) ;
}
fclose(f);
}
main.c :
userstruct myStruct = {
.Num = 6,
.Enable = 1,
.a = 10,
.b = {
# include "meta.h"
}
};
компилируем :
$ gcc makemeta.c -o makemeta
$ ./makemeta
$ gcc main.c -o main
Можно использовать препросессор. Например, с использованием boost:
#include <stdio.h>
#include <stdint.h>
#include <boost/preprocessor/repetition/enum.hpp>
#define MY_SIZE 150
typedef struct {
uint32_t Num;
uint32_t Enable;
uint32_t a;
uint32_t b[MY_SIZE];
} userstruct;
userstruct myStruct = {
.Num = 6,
.Enable = 1,
.a = 10,
.b = {
#define ELEMENT(z,i,unused_data) i
BOOST_PP_ENUM( MY_SIZE, ELEMENT , unused_data )
#undef ELEMENT
},
};
int main() {
printf("%d", myStruct.b[149]);
return 0;
}
Несколько упрощая, работает это так:
Макрос BOOST_PP_ENUM после подстановки в него конкретного числа MY_SIZE, с использованием оператора склейки лексем ## заменятся на один из макросов BOOST_PP_ENUM_1, BOOST_PP_ENUM_2, ... , BOOST_PP_ENUM_256. Каждый из которых, в свою очередь, определен, примерно, так:
#define BOOST_PP_ENUM_1(z, MACRO, DATA) MACRO(z,0,DATA)
#define BOOST_PP_ENUM_2(z, MACRO, DATA) BOOST_PP_ENUM_1(z, MACRO, DATA), MACRO(z,1,DATA)
#define BOOST_PP_ENUM_3(z, MACRO, DATA) BOOST_PP_ENUM_2(z, MACRO, DATA), MACRO(z,2,DATA)
...
#define BOOST_PP_ENUM_256(z, MACRO, DATA) BOOST_PP_ENUM_255(z, MACRO, DATA), MACRO(z,255,DATA)
Т.е. для целого N, определенного в качестве переменной препроцессора,
BOOST_PP_ENUM(N, MACRO, DATA) после подстановки, превратится в MACRO(z,0,DATA), MACRO(z,1,DATA), ... , MACRO(z,N-1,DATA). Если макрос MACRO определен то он будет подставлен с соответствующими аргументами.
Недостатки и ограничения такого подхода:
- размер N должен быть переменной препроцессора, содержащей число, а не константой и не макросом содержащим выражение.
- заголовочные файлы boost/preprocessor большие, и требуют большого числа итераций подстановки препроцессора, что может заметно замедлить компиляцию, особенно, если злоупотреблять использованием этой библиотеки в заголовочных файлах. Также, рекомендую включать только минимально необходимый набор заголовков и не в коем случае не boost/preprocessor.hpp.
- Максимальное число итераций, для repitition, ограничено макросом BOOST_PP_LIMIT_REPEAT, который (по умолчанию) равен 256. (его можно поднять до 512 или 1024, но с этими ограничениями справляются не все компиляторы, из-за ограничений компилятора по длительности цикла подстановки макросов (так они борются с экспоненциальным ростом текста, на этапе препроцессора).
Другие возможности библиотеки boost/preprocessor/repetition:
- Если потребуется повторить текст без разделителя
,то используйте BOOST_PP_REPEAT. - Если потребуется сделать константу N - зависимой от других констант, то в простых случаях можно использовать boost/preprocessor/arithmetic. Например,
BOOST_PP_ADD(3,4)будет раскрыто в 7, а не в выражение 3+4. В сложных случаях, я ввожу отдельную переменную задавая ей собственное значение и только проверяю, что значения переменных препроцессора согласованны между собой.
Почти наверняка, существуют аналоги библиотеки boost/preprocessor ориентированные на использование именно в C (не C++), но я их не знаю.
Если циклом, то для таких вещей есть конструктор по умолчанию:
#define MY_SIZE 150
struct userstruct
{
uint32_t Num;
uint32_t Enable;
uint32_t a;
uint32_t b[MY_SIZE];
userstruct();
};
userstruct::userstruct()
{
Num = 6;
Enable = 1;
a = 10;
for (uint32_t i = 0; i < MY_SIZE; i++)
b[i] = i;
}
int main()
{
userstruct st_obj;
printf("%d\n", st_obj.b[140]);
return 0;
}