Там какие-либо оборотные стороны к передающим структурам значением в C, вместо того, чтобы передать указатель?

Для забавного применения инициализации двойной скобки см. здесь Массив Dwemthy's в Java .

Отрывок

private static class IndustrialRaverMonkey
  extends Creature.Base {{
    life = 46;
    strength = 35;
    charisma = 91;
    weapon = 2;
  }}

private static class DwarvenAngel
  extends Creature.Base {{
    life = 540;
    strength = 6;
    charisma = 144;
    weapon = 50;
  }}

И теперь, будьте готовы для BattleOfGrottoOfSausageSmells и & hellip; короткое бекон!

153
задан 10 revs, 5 users 96% 14 June 2018 в 22:56
поделиться

7 ответов

Для маленьких структур (например, точка, реагируйте), передача значением совершенно приемлема. Но, кроме скорости, существует еще одна причина, почему необходимо быть осторожными передающими/возвращающими большими структурами значением: Стековое пространство.

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

, Если я вижу приложение, которое, кажется, имеет чрезмерное использование стека, структуры, переданные значением, являются одной из вещей, которые я ищу сначала.

196
ответ дан Roddy 23 November 2019 в 22:05
поделиться

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

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

Видят: http://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html

-fpcc-struct-return

-freg-struct-return

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

64
ответ дан tonylo 23 November 2019 в 22:05
поделиться

Простое решение будет возвратом код ошибки как возвращаемое значение и все остальное в качестве параметра в функции,
, Этот параметр может быть структурой, конечно, но не видит, что какое-то конкретное преимущество передает это значением, просто отправил указатель.
Передающая структура значением опасна, необходимо быть очень осторожными, что является Вами, передача, помнит, что в C нет никакого конструктора копии, если один из параметров структуры будет указателем, то значение указателя будет скопировано, это могло бы очень сбивать с толку и могло бы быть твердо поддержать.

Только для завершения ответа (полный кредит к Roddy) использование стека является другой причиной не, передают структуру значением, верят мне отлаживающий переполнение стека, реальный ЛАВАШ.

Воспроизведение для комментария:

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

15
ответ дан Community 23 November 2019 в 22:05
поделиться

Я думаю, что Ваш вопрос подвел итог вещей вполне прилично.

Еще одно преимущество передающих структур значением состоит в том, что владение памяти является явным. Нет никакого удивления о том, если структура от "кучи", и кто несет ответственность за освобождение его.

9
ответ дан Darron 23 November 2019 в 22:05
поделиться

Я сказал бы, что передача (not-too-large) структуры значением, и как параметры и как возвращаемые значения, является совершенно законной техникой. Нужно заботиться, конечно, что структура является или типом POD, или семантика копии хорошо определяется.

Обновление: Извините, у меня был свой C++, думая ограничение на. Я вспоминаю время, когда не было законно в C возвратить структуру из функции, но это, вероятно, изменилось с тех пор. Я все еще сказал бы, что это допустимо пока все компиляторы, которые Вы ожидаете использовать, поддерживают практику.

9
ответ дан Greg Hewgill 23 November 2019 в 22:05
поделиться

Одна вещь, которую люди здесь забыли упоминать до сих пор (или я пропустил его) состоит в том, что структуры обычно имеют дополнение!

struct {
  short a;
  char b;
  short c;
  char d;
}

Каждый символ составляет 1 байт, каждыми короткими составляют 2 байта. Насколько большой структура? Нет, это не 6 байтов. По крайней мере, не в больше наиболее часто используемых системах. В большинстве систем это будет 8. Проблема, выравнивание не является постоянным, это системно-зависимо, таким образом, та же структура будет иметь различное выравнивание и различные размеры в различных системах.

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

9
ответ дан D.W. 23 November 2019 в 22:05
поделиться

Чтобы действительно ответить на этот вопрос, нужно глубоко копнуть в области сборки:

(В следующем примере используется gcc на x86_64. Любой может добавить другой архитектуры, такие как MSVC, ARM и т. д.)

Давайте возьмем наш пример программы:

// foo.c

typedef struct
{
    double x, y;
} point;

void give_two_doubles(double * x, double * y)
{
    *x = 1.0;
    *y = 2.0;
}

point give_point()
{
    point a = {1.0, 2.0};
    return a;
}

int main()
{
    return 0;
}

Скомпилируйте ее с полной оптимизацией

gcc -Wall -O3 foo.c -o foo

Посмотрите на сборку:

objdump -d foo | vim -

Вот что мы получим:

0000000000400480 <give_two_doubles>:
    400480: 48 ba 00 00 00 00 00    mov    $0x3ff0000000000000,%rdx
    400487: 00 f0 3f 
    40048a: 48 b8 00 00 00 00 00    mov    $0x4000000000000000,%rax
    400491: 00 00 40 
    400494: 48 89 17                mov    %rdx,(%rdi)
    400497: 48 89 06                mov    %rax,(%rsi)
    40049a: c3                      retq   
    40049b: 0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

00000000004004a0 <give_point>:
    4004a0: 66 0f 28 05 28 01 00    movapd 0x128(%rip),%xmm0
    4004a7: 00 
    4004a8: 66 0f 29 44 24 e8       movapd %xmm0,-0x18(%rsp)
    4004ae: f2 0f 10 05 12 01 00    movsd  0x112(%rip),%xmm0
    4004b5: 00 
    4004b6: f2 0f 10 4c 24 f0       movsd  -0x10(%rsp),%xmm1
    4004bc: c3                      retq   
    4004bd: 0f 1f 00                nopl   (%rax)

Исключая nopl pads, give_two_doubles () имеет 27 байтов, а give_point () - 29 байтов. С другой стороны, give_point () дает на одну инструкцию меньше, чем give_two_doubles ()

Что интересно, так это то, что мы заметили, что компилятор смог оптимизировать mov в более быстрые варианты SSE2 movapd и movsd . Кроме того, give_two_doubles () фактически перемещает данные в память и из нее, что замедляет работу.

По-видимому, многое из этого может быть неприменимо во встроенных средах (где в настоящее время большую часть времени играет поле для C). Я не мастер сборки, поэтому любые комментарии приветствуются!

19
ответ дан 23 November 2019 в 22:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: