Цитата
Конечно, разговор о стиле программирования это немного

(и мои замечания тоже), тем более он разный в зависимости от использования/неиспользования операционной системы, иногда и конкретной ОС, условий задачи (например, условие реентерабельности get_date()) и т.п.
Этот вопрос - это обсуждение сугубо
не стиля программирования, а вопросов написания кода, свободного от скрытых ошибок, которые потом можно выявлять годами... Стиль программирования - это когда споры идут до хрипоты какой из
эквивалентных фрагментов кода "правильный", а какой "ущербный":
Код
if( ... ) {
cout << "terminal error";
return( EXIT_FAILURE );
};
или
Код
if( ... ) cout << "terminal error", exit( EXIT_FAILURE );
Цитата
Ответ мой и zaratustra - это как надо бы, когда операционной системы точно нет (хотя и присутствуют функции динамического выделения памяти).
Функция malloc() - это API stdlib, в не зависимости от среды выполнения, и должна обеспечиваться любой совместимой С-системой, если таковая хочет нею считаться

, ... а если нет - то кто же ей доктор

.
Цитата
Пример уважаемого Olej со структурной копией в стеке не очень понятен. Копия живет до перезаписи ее в стеке? Не случится ли это раньше, чем она потребуется?
- нет, копия, выделенная в стеке будет жить гораздо дольше

... перезаписи её в стеке ... потому что перезаписи просто не будет! Если вы помните, стандарт С ISO/IEC 9899 1999 (называемый ANSI C 99) окончательно стандартизовал возврат любого
сколь угодно сложной структуриры как результата выполнения функци ... раньше это было: "... это можно - это нельзя, ... здесь селёдку заворачивали(с) ...". Как это может происходить? Смотрим такой фрагмент (я для чистоты эксперимента - использую pure C а не C++):
Код
typedef struct { char d[ 20 ]; } X; // sizeof( X ) = 20
. . .
struct X func1( struct X Y ) {
return Y;
};
. . .
void func2( void ) {
...
struct X Y = func( X );
};
- сколько стека будет выделено при вызове func1? ответ: 40 байт - для области возврата + области копии параметров;
- в каком порядке они будут заталкиваться в стек? ответ: именно в таком, как сказано выше: область возврата + копии параметров + локальные переменные (если они есть - у нас нет);
- сколько копирований (байт?) будет при вызове-возврате? ответ: 3 (60 байт) - копирование параметра - копирование в область возврата - копирование присваивания;
- как и кем будет освобождаться стек? это уже интереснее, для это придётся вспомнить соглашения о вызовах некоторых языковых средств, например:
- PASCAL (BASIC, FORTRAN, ADA, OBERON, MODULA2): параметры заталкиваются в стек слева-направо, сама процедура должна восстановить стек перед return;
- C/C++ (PROLOG): параметры заталкиваются в стек справа-налево,
вызывающий код должен восстановить стек после return;
- STDCALL (WIN32): параметны заталкиваются как С, а стек освобождается как PASCAL (Господи, это ж надо такое придумать!);
- WATCOM: первые (слева-направо) параметры грузятся в регистры EAX, EDX, EBX, ECX (сколько влезет ), а уж остальные заталкиваются в стек справа-налево как С;
Так вот -
код вызывающей единицы С/С++ свернёт область стека после выполнения, но не область возврата структурного результата. Эта область остаётся распределённой как и область локальных переменных
вызывающей единицы. Вопрос - когда же она освободится? - тогда же, когда и локальные переменные вызвавшей единицы - при выходе за область видимости вызывающей единицы (именно её завершении).
А чтобы не быть голословным, я вам продемонстрирую ассемблерный код, сгенерированный в подобном случае компилятором GCC 2.95 (ведь исходный вопрос был о Linux?):
Код
#define SIZE 1024 // только для степени 2 и простоты разбора
struct A { char B[ SIZE ]; } C;
struct A POW( struct A E ) { return E; };
int main( void ) {
struct A D = POW( C );
return 0;
};
И вот что из него получается:
Код
POW:
pushl %ebp
movl %esp,%ebp
pushl %edi
pushl %esi
movl 8(%ebp),%eax
movl %eax,%edi
leal 12(%ebp),%esi
cld
movl $256,%ecx
rep
movsl
movl %eax,%eax
leal -8(%ebp),%esp
popl %esi
popl %edi
leave
ret $4
main:
pushl %ebp
movl %esp,%ebp
subl $1040,%esp
pushl %edi
pushl %esi
leal -1024(%ebp),%eax
addl $-12,%esp
addl $-1024,%esp
movl %esp,%edi
movl $C,%esi
cld
movl $256,%ecx
rep
movsl
pushl %eax
call POW
addl $1036,%esp
xorl %eax,%eax
leal -1048(%ebp),%esp
popl %esi
popl %edi
leave
ret
.comm C,1024,32
Можете убедиться:
- структура из 1024 байт перед вызовом побайтно (у компилятора хватило ума сделать это 4-х байтными словами) копируется в стек, который "прогибается" вниз на 1024;
- возвращаемая структура также копируется в место в стеке, которое "бронируется" вызывающим кодом при завершении вызова.
P.S. Проу прощения за многословность, но такие вещи - проще не объясняются

.