У меня есть этот код здесь:
Void() aba ()
{
}
#define std_Dynamic_Array(T) struct{Int32() count; Int32() capacity; T* memory; }
Void() no_body_func (Int32() b);
Int32() test_func (Int32()*** a)
{
}
Int32() test_func2 (Int32() x)
{
}
Int32() b = 33;
#define v_macro(T) struct{Int32() count; Int32() capacity; T* memory; } v (std_Dynamic_Array(T)* m) \
{ \
Int32() l = 44; \
}
Void() _main ()
{
Int64() a = 5;
b = 22;
Int32() c = test_func2(6);
Int32() inner_func (Int32()* a)
{
Int32() g = 3;
}
Int32()* x = &b;
inner_func(x);
Int32() j = *x;
Float32() f = 33.000000;
std_Dynamic_Array(Int64()) da = {.count = 4};
v_macro(Int64());
std_Dynamic_Array(Int64()) k = v(&da);
}
int main(int argc, char ** argv)
{
return 0;
}
Я пытаюсь написать свой собственный компилятор, который может генерировать код C в качестве одной из целей. Моя проблема в том, что я получаю эту странную ошибку:
tests/simple.c:34:36: error: invalid initializer
34 | std_Dynamic_Array(Int64()) k = v(&da);
| ^
Вот код, сгенерированный gcc -E ...
void aba ()
{
}
void no_body_func (int b);
int test_func (int*** a)
{
}
int test_func2 (int x)
{
}
int b = 33;
void _main ()
{
long long a = 5;
b = 22;
int c = test_func2(6);
int inner_func (int* a)
{
int g = 3;
}
int* x = &b;
inner_func(x);
int j = *x;
float f = 33.000000;
struct{int count; int capacity; long long* memory; } da = {.count = 4};
struct{int count; int capacity; long long* memory; } v (struct{int count; int capacity; long long* memory; }* m) { int l = 44; };
struct{int count; int capacity; long long* memory; } k = v(&da);
}
int main(int argc, char ** argv)
{
return 0;
}
Почему возникает эта ошибка? И возвращаемый тип v
, и тип k
расширяются до struct{int count; int capacity; long long* memory; }
, так почему же GCC рассматривает их как разные типы?
Что я могу сделать, чтобы это исправить?
Также:
Я знаю, что, вероятно, мог бы решить эту проблему, создав несколько определений типов, но я хочу этого избежать. По крайней мере, на данный момент.
Спасибо!!
«так почему же GCC рассматривает их как разные типы»: это разные типы. Две структуры без тегов могут быть совместимы только в том случае, если они объявлены в разных единицах трансляции.
А что касается «это сгенерированный код»… Если это правда, то с программой, сгенерировавшей этот ужасный код, что-то серьезно не так. Генератор кода, создающий неверный код, бесполезен.
«Если решение требует самого хакерского трюка GCC, известного человеку, и языковых расширений, пусть будет так».: Я не проверял это, но с -fpermissive
GCC допускает многое...
это сгенерированный код, предназначенный для GCC, а не для людей. И вот вы здесь....
@Someprogrammerdude Плохие примеры всегда полезны. ;-)
@Someprogrammerdude Ужасный код — это код C, сгенерированный моим компилятором. В этом коде нет никакой реальной цели, это просто результат простого тестового файла, написанного на моем языке. Это НЕ часть моего компилятора, это то, что он сгенерировал. Извините, если это не было очевидно из моего исходного поста, английский не мой родной язык.
@AndrewHenle Я сказал это, потому что не хочу, чтобы решение было «красивым». Я просто хочу иметь возможность скомпилировать сгенерированный код. Я просто не знаю, какой код C должен сгенерировать мой компилятор.
Тогда мне жаль говорить, что ваш компилятор не генерирует действительный код. В C нет вложенных функций. А функции, объявленные как возвращающие значение, должны возвращать значение. Вам необходимо убедиться, что компилятор не генерирует недопустимый код, независимо от синтаксиса или семантики кода, который он должен компилировать. Вам нужно много знать не только об исходном языке, но и о языке, сгенерированном компилятором.
@Someprogrammerdude: GCC действительно имеет вложенные функции, и OP сказал, что они ориентированы исключительно на GCC. В C есть вложенные функции. Стандарт C — это всего лишь одна спецификация языка C или семейства языков, и никто не обязан его использовать. У вас нет причин критиковать работу, в которой намеренно используется другая версия языка C. ISO не имеет монополии на язык C, и люди могут свободно использовать другие варианты.
@Someprogrammerdude Расширения GCC допускают вложенные функции. Функции не обязаны возвращать значение, вы просто получите предупреждение, но я подавил все предупреждения. Кроме того, я задаю этот вопрос, потому что мой компилятор не генерирует «компилируемый» код из-за проблемы со структурой. Вам не нужно говорить мне, что код неправильный. Вот почему я задаю вопрос
@Someprogrammerdude: Re «И функции, которые объявлены как возвращающие значение, должны возвращать значение»: это неверно даже в стандарте C. int foo(void) {} int main(void) { foo(); }
— допустимая программа, поведение которой полностью соответствует стандарту C. Кроме того, учитывая, что эти функции совершенно пусты, а ОП явно возится с разрабатываемым транспилятором, вполне вероятно, что это всего лишь предварительный код-заполнитель, который будет развиваться дальше. Вы должны это признать и не засорять Stack Overflow неуместной критикой.
Подавление предупреждений — хороший способ получить код, который приводит к неопределенному поведению. Да, функция может пропустить оператор return
, но что происходит с кодом, который вызывает функцию и ожидает, что она что-то вернет? Например k = v(&da)
... Вы заявили, что функция должна возвращать одну из ваших структур, и вы хотите инициализировать с ее помощью переменную k
. Но поскольку v
не возвращает ничего, что могло бы привести к неопределенному поведению, вся программа становится неправильной.
И это ваш учитель (или другой учебный ресурс) научил вас полагаться на специфичные для компилятора и нестандартные расширения? Тогда это действительно плохой учитель или ресурс. Если ваш компилятор генерирует простой стандарт C, то его может использовать каждый, даже тот, у кого нет GCC. Если бы я был вашим учителем, я бы протестировал ваш компилятор не только с неверным исходным кодом, но и с другими компиляторами C. И учитывая код, который он сейчас генерирует (который приводит к UB), я бы вас подвел.
@Someprogrammerdude: Re: «И это ваш учитель (или другой учебный ресурс) научил вас полагаться на специфичные для компилятора и нестандартные расширения?»: Чепуха. Существует множество бизнес-ситуаций, в которых уместно и целесообразно полагаться на специфичные для компилятора и нестандартные расширения. Не существует универсального требования, чтобы все использовали стандарт C. Чтобы понять, какая это полная чушь, подумайте, сколько работы было вложено в расширения GCC и Clang. Эти расширения существуют для того, чтобы полагаться на свои функции, и на них явно есть спрос.
@Someprogrammerdude Это хобби-проект, где я просто развлекаюсь летом. Вся суть в том, чтобы сгенерировать код СПЕЦИАЛЬНО для GCC. Это очень ЖЕЛАЕМОЕ ограничение, благодаря которому я могу с комфортом полагаться на языковые расширения и специальные приемы GCC, чтобы все работало. Это все равно, что сгенерировать код NASM и злиться, потому что он не работает с GAS или каким-то другим инструментом. Немного пошевелите головой
@kamkow1: Можешь игнорировать Someprogrammerdude. Их комментарии не соответствуют действительности.
@EricPostpischil лучше для моего здравомыслия
@EricPostpischil int foo(void) {} int main(void) { foo(); }
— допустимая программа, поведение которой полностью определено стандартом C. Это действительно только потому, что вы указали, что к отсутствующему возвращаемому значению нет доступа. С таким же успехом можно припарковать машину рядом с обрывом, чтобы ее пришлось включить заднюю передачу, чтобы уехать, а затем отдать ключи от машины водителю с завязанными глазами и не упомянуть об обрыве перед машиной...
@AndrewHenle: Мой комментарий здесь не предназначен для обучения начинающих пользователей и не должен быть таким, поскольку это не является целью комментариев. Он призван исправить ложное заявление Someprogrammerdude. Их комментарий был изначально неуместен — абсурдно приходить и критиковать намеренно неполный код как неполный. Это всего лишь код-заполнитель для проекта, находящегося в разработке, и он не был частью вопроса и не должен был комментироваться.
Каждое определение структуры создает отдельный тип, и это целенаправленная и желательная особенность C. Если у нас есть:
typedef struct { double a, b; } GeometricPoint;
typedef struct { double a, b; } ComplexNumber;
тогда мы хотим, чтобы GeometricPoint
и ComplexNumber
были разными типами, чтобы один нельзя было случайно назначить другому, даже если их расположение идентично.
Везде в вашем коде, где у вас есть struct{int count; int capacity; long long* memory; }
, у вас есть разные типы, и каждый из этих типов несовместим с другими, и поэтому вы получаете предупреждения и ошибки при попытке присвоить один другому, передать один в качестве аргумента для другого, или попробовать присваивать значения или передавать аргументы с указателями на них.
Чтобы это исправить, объявите структуру один раз с тегом:
struct MyStructure { int count; int capacity; long long *memory; };
а затем используйте только struct
с тегом struct MyStructure
во всем остальном коде. Альтернативно вы можете использовать typedef
:
typedef struct { int count; int capacity; long long *memory; } MyStructure;
а затем используйте имя typedef
, MyStructure
в остальной части кода.
Откуда вы взяли этот ужасный код? Какова цель этого кода? Нет ли файлов заголовков, определяющих такие вещи, как
Void()
илиInt32()
? Не говоря уже о том, что вложенные функции не являются частью C. Как в исходном, так и в предварительно обработанном коде очень много ошибок.