Как объявить и передать структуру во время вызова функции?

Очень часто во время вызова функции объявляют и передают базовую переменную типа данных. Можем ли мы добиться чего-то подобного со структурами? Ниже код лучше объясняет мой вопрос.

struct s 
{
    int i;
    char c;
};

void f(int i)
{
    return;
}

void g(struct s s1)
{
    return;
}

int main()
{
    int i = 5;  // possible
    struct s s1 = {1, 'c'}; // possible

    f(i);   // possible
    g(s1);  // possible

    f(5);   // possible
    g({1, 'c'});    // not possible, is there any alternative way ?

    return 0;
}

person m0hithreddy    schedule 14.05.2020    source источник


Ответы (1)


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

void g (struct s* s1)
...
g(&s1);

Чтобы ответить на вопрос, вы можете использовать составной литерал:

g( (struct s){1, 'c'} );
person Lundin    schedule 14.05.2020
comment
согласно вашему предложению о передаче структур по значению, в этом случае это будет: g(&((struct s){1, 'c'}) ); - person Frankie_C; 14.05.2020
comment
В целом хорошо, но действительно ли это занимает много памяти в случае OP (небольшая структура размером, сравнимым с указателем)? - person Bob__; 14.05.2020
comment
Да, я знаю, что при передаче по значению потребляется больше стека функций, по заданному мною вопросу. Но другое дело, после объявления константы мы можем получить доступ к ее адресу с помощью & ? В случае целочисленных констант (&1) мы получили бы ошибку lvalue, нормально ли это со структурами? - person m0hithreddy; 14.05.2020
comment
@Frankie_C Действительно! Хотя на самом деле вам не нужны внешние скобки, поскольку составные литералы считаются постфиксными операторами и имеют более высокий приоритет оператора, чем унарный &. - person Lundin; 14.05.2020
comment
@Bob__ Зависит от ABI и соглашения о вызовах. Или 8-битный процессор, структура будет занимать 24 бита, а указатель будет занимать только 16 бит, но на 32-битном ПК не будет заметной разницы, поскольку структура меньше 32 бит. Отсюда эмпирическое правило. В некоторых системах вам может сойти с рук передача небольших структур по значению. - person Lundin; 14.05.2020
comment
@MohithReddy Составной литерал не является константой, это безымянный инициализированный объект. Это означает, что его адрес может быть взят, он может быть прочитан или записан, а указатели на него могут быть переданы другим функциям (конечно, пока он все еще находится в области видимости) - person Felix G; 14.05.2020
comment
@MohithReddy Целочисленная константа 1 не имеет местоположения в памяти (это не lvalue), но составная литеральная структура работает так же, как локальная переменная, за исключением того, что у нее нет имени. У него есть адрес и локальная область видимости, в данном случае он действителен в main(). В этом отношении он работает так же, как ваша явно объявленная переменная s1. - person Lundin; 14.05.2020
comment
Я совершенно не согласен с вашим правилом. struct параметры могут быть очень эффективным инструментом, потому что они избегают алиасинга. И в сочетании с встраиванием любые накладные расходы на копирование обычно исчезают. Для использования вариантов с указателями (если вы спорите об эффективности), которые должны быть как минимум const квалифицированы для целевого типа и restrict квалифицированы для указателя. - person Jens Gustedt; 14.05.2020
comment
@Frankie_C Просто для придирки: вы имели в виду передачу структур по ссылке, а не передачу структур по значению. - person RobertS supports Monica Cellio; 14.05.2020
comment
Вероятно, вам следует добавить примечание, что это работает только в C99 или выше. - person DarkAtom; 14.05.2020
comment
@Lundin да, я привык делать код более читаемым (а также потому, что некоторые IDE раздражают, предлагая использовать скобки). - person Frankie_C; 14.05.2020
comment
@RobertSsupportsMonicaCellio да, я имею в виду «передавать структуры по ссылке». Извините за опечатку. - person Frankie_C; 14.05.2020
comment
@JensGustedt Эти проблемы довольно сложны, и новичкам не следует их учитывать (новички прекращают читать здесь). Чтобы решить ваши проблемы: во-первых, большинство функций, принимающих параметры структуры, вероятно, находятся в другой единице перевода, чем вызывающий объект, поэтому встраивание может оказаться невозможным. И глядя на большинство ABI, структуры, передаваемые по значению, чаще всего усложняют ситуацию. Документация ABI для структур нетривиальна, если вообще документирована. -› - person Lundin; 14.05.2020
comment
Структуры также вводят требование выравнивания, которого могло не быть для отдельных элементов (если они uint8_t и т. д.), вызывая накладные расходы на заполнение. Кроме того, структуры обеспечивают порядок распределения, который, в свою очередь, может даже помешать некоторым оптимизациям, если нам не повезет. -› - person Lundin; 14.05.2020
comment
Что касается алиасинга, оптимизация простых типов, передаваемых через указатели, действительно может быть выполнена с помощью restrict, если это необходимо. Однако отсутствие псевдонимов скорее будет бременем, чем преимуществом. Рассмотрим функцию, принимающую struct s { int x; int y; }, а вызывающий объект получает массив int[2]. Теперь, даже если структура размещает два целых числа рядом без заполнения, вы все равно не можете вызвать функцию, передающую массив, потому что у вас не будет совместимых типов, и это приведет к нарушениям алиасинга. - person Lundin; 14.05.2020
comment
@Lundin, эти опасения не более серьезны, чем те, которые вы подняли в своем ответе. Концептуально передача struct намного проще, и пока вас не слишком заботит производительность, это должно быть предпочтительным. Но если вас беспокоит производительность, то это гораздо больше, чем просто передача указателя. Так что в целом, как бы коротко вы ни сказали в своем ответе, это просто плохой совет. - person Jens Gustedt; 14.05.2020
comment
@JensGustedt Если мы ограничим обсуждение существующими реальными компьютерами, то передача структуры по значению будет хорошо работать только в том случае, если это целесообразно для данного ABI. Опять же, чаще всего это не посмотрите это для x86 и это для высокопроизводительного процессора. Если вы думаете, что понимание того, что ABI для структур тривиально, вы, вероятно, совершенно одиноки. -› - person Lundin; 14.05.2020
comment
Не заводите меня на младшие микроконтроллеры. Я помню новичка, которому удалось уничтожить весь стек PIC16 одним вызовом функции передачи структуры по значению. На младших ISA часто гораздо лучше предоставить указатель структуры, чем даже список параметров. Что-то вроде void func (int a, int b, int c, int d) может генерировать довольно медленный, потребляющий память код на 8-битных микроконтроллерах, но void func (const struct s* obj) использует только один индексный регистр и значительно превосходит список параметров. - person Lundin; 14.05.2020
comment
@lundin, ты прочитал, что я только что сказал? Это вы подтолкнули все это к производительности в своем ответе своим эмпирическим правилом. Все, что я хочу сказать, это то, что это не хорошее общее правило и что его не следует использовать в ущерб безопасности. Во многих случаях это будет просто преждевременная оптимизация. - person Jens Gustedt; 14.05.2020
comment
@JensGustedt Бесполезное использование стека — это жертва безопасностью, поскольку передача структур по значению значительно увеличивает вероятность переполнения стека. - person Lundin; 14.05.2020