Как вернуть структуру из функции c
Как и любой другой объект, структура может использоваться в качестве параметра функции и также может быть возвращаемым объектом функции.
Структура как параметр функции
При передаче структуры в качестве параметра получает копию значений структуры:
#include struct person < char* name; int age; >; void print_person(struct person user) < printf("Name: %s \n", user.name); printf("Age: %d \n", user.age); >int main(void) < struct person tom = ; print_person(tom); return 0; >
В данном случае функция print_person() принимает объект структуры person и выводит значения его элементов на консоль.
Чтобы не писать тип параметра полностью — struct person , можно определить псевдоним структуры:
#include typedef struct < char* name; int age; >person; void print_person(person user) < printf("Name: %s \n", user.name); printf("Age: %d \n", user.age); >int main(void) < person tom = ; print_person(tom); return 0; >
Указатели на структуру как параметры
При использовании структур в качестве параметров в функции следует учитывать, что при вызове функции для структуры, также как и для параметров типа int или char, выделяется память, в которую помещаются значения элементов структуры. То есть структура в функцию передается по значению, а это значит, что переданную в функцию структуру мы изменить не можем.
Если необходимо уменьшить выделение памяти (особенно если структура большая) или иметь возможность изменять изначальную структуру в функции, то можно передавать в функцию указатель на структуру:
#include struct person < char* name; int age; >; void change_person(struct person * user) < user->age = user->age + 1; > int main(void) < struct person bob = ; printf("Before change. %s : %d \n", bob.name, bob.age); change_person(&bob); printf("After change. %s : %d \n", bob.name, bob.age); return 0; >
В этом примере функция change_person принимает указатель на структуру person и увеличивает на единицу значение элемента age.
Для проверки в функции main выводим данные объекта person на консоль до и после вызова функции change_person.
Before change. Bob : 22 After change. Bob : 23
Структура как результат функции
Также функция может возвращать объект структуры:
#include struct person < char* name; int age; >; struct person create_person(char* name, int age) < struct person user; user.name = name; user.age = age; return user; >int main(void)
Здесь функция create_person() создает на основании полученных параметров объект структуры person и возвращает его в качестве результата.
Возврат структуры из функции в C++
Существует легенда, что возврат структуры по значению из функции в C++ — затратная процедура. Сегодня я займусь проверкой этой легенды.
Для этого пишу небольшой тестовый примерчик:
#include #include struct Object < public: int a; int b; int c; char string[50]; >; Object func() < Object o; o.a = 1; o.b = 2; o.c = 3; strcpy(o.string, "Hello world!"); return o; >int main(int argc, char ** argv)
Компилирую его c помощью gcc в моем случае. Тестирую, работает как положено выводит Hello world. Вторым шагом, чтобы проверить к чему приводит такой код, компилирую приведенный тестовый пример в ассемблер. gcc -S -c main.cpp
Что я вижу в ассеблерном коде:
.file "main.cpp" .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "Hello world!" .text .p2align 1,0x90 .p2align 2,,3 .globl _Z4funcv .type _Z4funcv, @function _Z4funcv: .LFB3: pushl %ebp .LCFI0: movl %esp, %ebp .LCFI1: movl 8(%ebp), %eax pushl %edi .LCFI2: pushl %esi .LCFI3: cld movl $.LC0, %esi leal 12(%eax), %edi movl $3, %ecx movl $1, (%eax) movl $2, 4(%eax) movl $3, 8(%eax) rep movsl movsb popl %esi popl %edi leave ret $4 .LFE3: .size _Z4funcv, .-_Z4funcv .section .rodata.str1.1 .LC1: .string "%s" .text .p2align 1,0x90 .p2align 2,,3 .globl main .type main, @function main: .LFB4: pushl %ebp .LCFI4: movl %esp, %ebp .LCFI5: subl $72, %esp .LCFI6: andl $-16, %esp leal -72(%ebp), %eax subl $16, %esp pushl %eax .LCFI7: call _Z4funcv subl $8, %esp leal -60(%ebp), %eax pushl %eax pushl $.LC1 .LCFI8: call printf xorl %eax, %eax leave ret .LFE4: .size main, .-main .section .eh_frame,"a",@progbits .Lframe1: .long .LECIE1-.LSCIE1 .LSCIE1: .long 0x0 .byte 0x1 .string "zP" .uleb128 0x1 .sleb128 -4 .byte 0x8 .uleb128 0x5 .byte 0x0 .long __gxx_personality_v0 .byte 0xc .uleb128 0x4 .uleb128 0x4 .byte 0x88 .uleb128 0x1 .p2align 2 .LECIE1: .LSFDE3: .long .LEFDE3-.LASFDE3 .LASFDE3: .long .LASFDE3-.Lframe1 .long .LFB4 .long .LFE4-.LFB4 .uleb128 0x0 .byte 0x4 .long .LCFI4-.LFB4 .byte 0xe .uleb128 0x8 .byte 0x85 .uleb128 0x2 .byte 0x4 .long .LCFI5-.LCFI4 .byte 0xd .uleb128 0x5 .byte 0x4 .long .LCFI7-.LCFI5 .byte 0x2e .uleb128 0x4 .byte 0x4 .long .LCFI8-.LCFI7 .byte 0x2e .uleb128 0x10 .p2align 2 .LEFDE3: .ident "GCC: (GNU) 3.4.6 [FreeBSD] 20060305"
Что отсюда видно. Видно что в функции main() резервируется место в стеке под структуру Object o. А потом, как это не странно ссылка на эту выделенную память передается внутрь функции func(). И функция func не производя никакого выделения памяти самостоятельно, работает с полями структуры, предварительно загрузив адрес структуры в регистр eax.
Т.е. это означает, что никаких накладных расходов данный фрагмент не вызывает! Во первых нет ни одной операцией с «кучей». Всё выделяется в стеке вызывающей функции. Во-вторых адрес выделенной памяти передается скрытой ссылкой или указателем. Это как вам больше нравится. В ассеблере просто передается адрес.
Теперь проверим следующий, закономерный вопрос. А что если функция func сама является членом другого класса. Как компилятор поступит в этом случае. Провожу для этого второй эксперимент, аналогичный первому:
#include #include struct Object < int a; int b; int c; char s[50]; >; class A < int a; public: Object func(); >; Object A::func() < Object o; o.a=1; o.b=2; o.c=3; strcpy(o.s,"Hello world"); a=5; return o; >int main(int argc, char ** argv)
Аналогично, тестирую, затем компилирую в ассемблер:
.file "main1.cpp" .section .rodata .LC0: .string "Hello world" .text .p2align 1,0x90 .p2align 2,,3 .globl _ZN1A4funcEv .type _ZN1A4funcEv, @function _ZN1A4funcEv: .LFB3: pushl %ebp .LCFI0: movl %esp, %ebp .LCFI1: pushl %ebx .LCFI2: subl $4, %esp .LCFI3: movl 8(%ebp), %ebx movl $1, (%ebx) movl $2, 4(%ebx) movl $3, 8(%ebx) subl $8, %esp pushl $.LC0 leal 12(%ebx), %eax pushl %eax .LCFI4: call strcpy addl $16, %esp movl 12(%ebp), %eax movl $5, (%eax) movl %ebx, %eax movl -4(%ebp), %ebx leave ret $4 .LFE3: .size _ZN1A4funcEv, .-_ZN1A4funcEv .section .rodata .LC1: .string "%s" .text .p2align 1,0x90 .p2align 2,,3 .globl main .type main, @function main: .LFB4: pushl %ebp .LCFI5: movl %esp, %ebp .LCFI6: subl $88, %esp .LCFI7: andl $-16, %esp movl $0, %eax addl $15, %eax addl $15, %eax shrl $4, %eax sall $4, %eax subl %eax, %esp leal -88(%ebp), %edx subl $8, %esp leal -12(%ebp), %eax pushl %eax pushl %edx .LCFI8: call _ZN1A4funcEv addl $12, %esp subl $8, %esp leal -88(%ebp), %eax addl $12, %eax pushl %eax pushl $.LC1 call printf addl $16, %esp movl $0, %eax leave ret .LFE4: .size main, .-main .section .eh_frame,"a",@progbits .Lframe1: .long .LECIE1-.LSCIE1 .LSCIE1: .long 0x0 .byte 0x1 .string "zP" .uleb128 0x1 .sleb128 -4 .byte 0x8 .uleb128 0x5 .byte 0x0 .long __gxx_personality_v0 .byte 0xc .uleb128 0x4 .uleb128 0x4 .byte 0x88 .uleb128 0x1 .p2align 2 .LECIE1: .LSFDE1: .long .LEFDE1-.LASFDE1 .LASFDE1: .long .LASFDE1-.Lframe1 .long .LFB3 .long .LFE3-.LFB3 .uleb128 0x0 .byte 0x4 .long .LCFI0-.LFB3 .byte 0xe .uleb128 0x8 .byte 0x85 .uleb128 0x2 .byte 0x4 .long .LCFI1-.LCFI0 .byte 0xd .uleb128 0x5 .byte 0x4 .long .LCFI3-.LCFI1 .byte 0x83 .uleb128 0x3 .byte 0x4 .long .LCFI4-.LCFI3 .byte 0x2e .uleb128 0x10 .p2align 2 .LEFDE1: .LSFDE3: .long .LEFDE3-.LASFDE3 .LASFDE3: .long .LASFDE3-.Lframe1 .long .LFB4 .long .LFE4-.LFB4 .uleb128 0x0 .byte 0x4 .long .LCFI5-.LFB4 .byte 0xe .uleb128 0x8 .byte 0x85 .uleb128 0x2 .byte 0x4 .long .LCFI6-.LCFI5 .byte 0xd .uleb128 0x5 .byte 0x4 .long .LCFI8-.LCFI6 .byte 0x2e .uleb128 0x10 .p2align 2 .LEFDE3: .ident "GCC: (GNU) 3.4.6 [FreeBSD] 20060305"
Этот код показывает, что опять в функцию func() в качестве первого аргумента передается указатель на структуру Object o. Функция в данном случае загружает его адрес в регистр ebx и заполняет поля структуры. Затем загружает в регистр eax второй скрытый параметр, содержащий указатель на экземпляр класса, которому принадлежит функция, т.е. this.
Т.о. вывод при возврате структуры по значению никаких накладных расходов на его выделение не случается. Память выделяется в стеке вызывающей функции. В функцию передается скрытый указатель на выделенную память. В случае, если функция, возвращающая структуру является членом класса, тогда в нее передается не один, а два указателя. Один указатель на выделенную для возвращаемого значения память и второй указатель this (на экземпляр объекта класса, членом которого является функция). Функция по окончании работы в регистре eax возвращает указатель на объект, тот же, что ей передали в скрытом параметре. Это уже лишнее действие по сути. Но присвоение регистра — это не сильно затратная операция.
Как вернуть структуру из функции c
Подскажите пожалуйста новичку, как на СИ возвратить структуру из функции
struct myStruct < UINT16 Param1; UINT16 Param2; >;
VOID. myFunc() < struct myStruct *myStruct; myStruct->Param1 = 10; myStruct->Param2 = 20; // Как сдесь написать return что бы вернуть структуру myStruct . Подскажите новичку пожалуйста return . ; >;
По форуму искал но так и ничего толком не понял, можно ожалуйста наглядный пример, большое спасибо
Re: Возврат структуры из функции на СИ
| От: | gandjustas | http://blog.gandjustas.ru/ |
| Дата: | 11.08.07 12:45 | |
| Оценка: |
Мжет лучше еще раз перечитать книжку по С ?
Столько ошибок в одной тривиальной функции я еще не видел.
Re: Возврат структуры из функции на СИ
| От: | Аноним | |
| Дата: | 11.08.07 12:49 | |
| Оценка: | 3 (1) | |
Здравствуйте, AlexBosen, Вы писали:
AB>Подскажите пожалуйста новичку, как на СИ возвратить структуру из функции
AB>Имеем структуру:
AB>
AB>struct myStruct AB> < AB>UINT16 Param1; AB> UINT16 Param2; AB>>; AB>
AB>Имеем функцию:
AB>
AB>VOID. myFunc() AB> < AB>struct myStruct *myStruct; AB> myStruct->Param1 = 10; AB> myStruct->Param2 = 20; AB> // Как сдесь написать return что бы вернуть структуру myStruct . Подскажите новичку пожалуйста AB> return . ; AB>>; AB>
AB>По форуму искал но так и ничего толком не понял, можно ожалуйста наглядный пример, большое спасибо
void* myFunc() < struct myStruct *myStruct; myStruct->Param1 = 10; myStruct->Param2 = 20; return ( (void*)(myStruct) ); >;
но это не совсем красиво
ИМХО лучше так:
// . myStruct *pStruct = new myStruct; myFunc( *pStruct ); // . void myFunc(myStruct& aStruct) < aStruct.Param1 = 10; aStruct.Param2 = 20; >;
Re[2]: Возврат структуры из функции на СИ
| От: | AlexBosen |
| Дата: | 11.08.07 12:52 |
| Оценка: |
Здравствуйте, gandjustas, Вы писали:
G>Мжет лучше еще раз перечитать книжку по С ?
G>Столько ошибок в одной тривиальной функции я еще не видел.
Спасибо за совет, но я же новичек и только учусь
Re: Возврат структуры из функции на СИ
| От: | Аноним | |
| Дата: | 11.08.07 12:52 | |
| Оценка: | 6 (2) | |
Здравствуйте, AlexBosen, Вы писали:
AB>Подскажите пожалуйста новичку, как на СИ возвратить структуру из функции
AB>Имеем структуру:
AB>
AB>struct myStruct AB> < AB>UINT16 Param1; AB> UINT16 Param2; AB>>; AB>
AB>Имеем функцию:
AB>
AB>VOID. myFunc() AB> < AB>struct myStruct *myStruct; AB> myStruct->Param1 = 10; AB> myStruct->Param2 = 20;
Так нельзя. Для структуры надо выделить память
1. статически
struct myStruct s;
2. либо динамически
struct myStruct* ps = (myStruct*)malloc(sizeof(myStruct));
Во втором случае не забудь освободить память (free(. )).
AB> // Как сдесь написать return что бы вернуть структуру myStruct . Подскажите новичку пожалуйста AB> return . ; AB>>; AB>
struct myStruct myFunc()
struct myStruct* myFunc()
AB>По форуму искал но так и ничего толком не понял, можно ожалуйста наглядный пример, большое спасибо
Re[2]: Возврат структуры из функции на СИ
| От: | Awaken | |
| Дата: | 11.08.07 12:56 | |
| Оценка: | 2 (1) | |
А>myStruct *pStruct = new myStruct;
забыли вы Си совсем
в C нет new/delete. по значению тоже не передашь т.к. нет конструкторов копирования
единственный вариант тут это calloc/malloс . или объявить ее глобальной переменной
Re[3]: Возврат структуры из функции на СИ
| От: | Аноним | |
| Дата: | 11.08.07 13:06 | |
| Оценка: | 3 (2) | |
Здравствуйте, Awaken, Вы писали:
А>>myStruct *pStruct = new myStruct;
A>забыли вы Си совсем
A>в C нет new/delete. по значению тоже не передашь т.к. нет конструкторов копирования
Чего? Comeau в режиме С99 компилит нормально.
Re[3]: Возврат структуры из функции на СИ
| От: | Аноним | |
| Дата: | 11.08.07 13:10 | |
| Оценка: | 3 (1) | |
Здравствуйте, Awaken, Вы писали:
А>>myStruct *pStruct = new myStruct;
A>забыли вы Си совсем
A>в C нет new/delete. по значению тоже не передашь т.к. нет конструкторов копирования
A>единственный вариант тут это calloc/malloс . или объявить ее глобальной переменной
Все правильно, это ++ вариант.
плюсы съели остатки моего мозга
Re[4]: Возврат структуры из функции на СИ
| От: | Awaken |
| Дата: | 11.08.07 14:09 |
| Оценка: |
А>Чего? Comeau в режиме С99 компилит нормально.
за С99 не слежу. ты прав, там это стало стандартной фичей.
Re[5]: Возврат структуры из функции на СИ
| От: | Аноним |
| Дата: | 11.08.07 14:29 |
| Оценка: |
Здравствуйте, Awaken, Вы писали:
А>>Чего? Comeau в режиме С99 компилит нормально.
A>за С99 не слежу. ты прав, там это стало стандартной фичей.
А какой Си Вы имели в виду? С89/С90 тоже поддерживает передачу и возврат структур по значению. Разве в K&R C было по другому?
Re[2]: Возврат структуры из функции на СИ
| От: | AlexBosen |
| Дата: | 11.08.07 14:55 |
| Оценка: |
Здравствуйте, Аноним, Вы писали:
struct myStruct s; struct myStruct myFunc()
Огромное спасибо Все получилось!
Возврат структуры из функции

Помогите разобраться. В функции мы создаем структуру, область видимости которой ограничивается этой функцией. Из нее мы возвращаем указатель на эту структуру.
Мне думалось, что подобное работать не должно. Т.к. объявленная структура при выходе за области действия функции перестает существовать, и, в таком случае, возвращаемый указатель должен ссылаться на какой-то мусор.
Т.е. получается, область памяти остается без изменений, мы просто не можем обращаться к структуре вне функции по имени? Может ли быть такое, что эта область памяти изменится каким-либо другим образом? Структура хранится в стеке же?
Лучшие ответы ( 1 )
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Функции: возврат структуры
struct my_info < //поля >my_struct; //структура struct my_info funct (struct my_info);.

Возврат структуры функцией через указатель
Здравствуй. После С++ никак не могу понять, как сделать. Нужно создать функцию struct A*.
Возврат структуры из функции
Добрый день! Я только начинаю, и сразу стал путаться с массивами и структурами с горем попалам.

Возврат структуры из функции
Всем доброго времени суток. awesome.h struct point < float x = 1; float y = 1; >; struct.
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472

Сообщение было отмечено Garcian как решение
Решение
Всё правильно. Структура может некоторое время и просуществовать, и обращение к ней будет давать правильные результаты, пока та область стека где она находилась, не будет затёрта выполняющейся функцией (при выделении ей новых переменных) или переменными новой вызываемой функции. Тут просто повезло. Единственной вызываемой функцией оказалась printf(), а поля структуры Ms ей передали при вызове, т. е. фактически к структуре обратились, извлекли её поля и присвоили их значения переменным-параметрам функции printf() ещё до самого вызова функции printf(). Дальше в процессе своей работы функция printf() наверняка затёрла структуру Ms со всеми её полями (но сохранение значений полей структуры в стековом кадре данного вызова printf() произошло раньше, непосредственно перед вызовом printf()). Так что в общем-то пример содержит ошибку, хотя в данном случае он и работает правильно.
Тут два выхода. Или объявить структуру Ms глобально, или объявить её внутри функции, но с квалификатором static. В последнем случае структура будет находиться в области глобальных данных в памяти процесса, фактически она будет глобальной переменной, но видимо её имя будет только внутри функции, в которой она объявлена, извне к ней обратиться будет нельзя. В смысле области видимости она будет похожа на локальную переменную. А по поведению она будет в общем-то глобальной переменной.
![]()
![]()
![]()
![]()
12260 / 7426 / 1739
Регистрация: 25.07.2009
Сообщений: 13,628
Сообщение от Garcian 
Может ли быть такое, что эта область памяти изменится каким-либо другим образом?
Может, и скорее всего так и произойдёт, сделай программа ещё что-нибудь. Правильный путь — внутри функции выделять память под структуру из кучи, заполнять поля и возвращать указатель. Только не забывать потом освобождать память, а то она не резиновая.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
mystruct* mystruct_new(const int a, const int b) { mystruct * ms = malloc(sizeof(mystruct)); if ( ! ms ) return NULL; // если памяти не хватило. ms->a = a; ms->b = b; return ms; } // где-то в программе mystruct * ms = mystruct_new(13, 69); if ( ! ms ) { fprintf(stderr, "Memory error!\n"); exit(1); } //do something free(ms);
243 / 268 / 219
Регистрация: 14.11.2016
Сообщений: 1,040
Garcian, выделяй память ( malloc ) под указатель, записывай потом в структуру значения (не забывай разыменовывать только указатель), и после возвращай указатель.
1 2 3 4 5 6 7
myStruct* simplefunc(int a, int b) { myStruct *ptr_item = ( myStruct*) malloc(sizeof(myStruct)); ptr_item->a = a; ptr_item->b = b; return ptr_item; }
Только потом не забывай в main() освободить выделенную память, чтобы не произошло утечки ( free(ptr); ).
Где ptr , это указатель на структуру (в твоём случае это будет ms_p ).
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
Можно и выделить память для новой структуры динамически, из кучи, а можно и объявить структуру как статическую переменную, в этом случае она будет иметь всегда один и тот же статический адрес.
Вот твой пример со статически объявленной структурой.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#include typedef struct { int a; int b; } myStruct; myStruct* simplefunc(int a, int b) { static myStruct Ms; Ms.a = a; Ms.b = b; return &Ms; } int main(int argc, char** argv) { myStruct *ms_p = simplefunc(3, 5); printf("a: %d b: %d\n", ms_p->a, ms_p->b); return 0; }
Только тут надо помнить две вещи.
1. При каждом новом вызове simplefunc() структура будет перезаписываться новыми параметрами, но это будет всё та же, прежняя переменная, хранящаяся по тому же самому старому адресу. Новой переменной создано не будет. Адрес функция simplefunc() будет возвращать каждый раз один и тот же. То есть новых копий структуры при каждом последующем вызове функция не порождает, она работает при каждом своём вызове с одной и той же копией, и эта единственная копия при каждом новом вызове будет перезаписана. Это может не годиться в некоторых случаях. Если же память для структуры выделяется динамически функцией malloc(), как тебе предложили другие форумчане в своих постах, при каждом вызове функции simplefunc() будет создана новая копия структуры. В некоторых случаях этот вариант является более приемлемым.
2. Этот подход со статической переменной (т. е. с переменной, объявленной с квалификатором static), возможно, не подойдёт для многопоточной программы.
Добавлено через 10 часов 48 минут
Ещё небольшое добавление хотел сделать, Garcian.
Твоя задача очень известная и по-моему во многих пособиях по C она рассматривается (мне она попадалась, а уже где, не помню).
Допустим, нам нужно объявить некоторую переменную в теле функции, вычислить значение этой переменной в функции, а по выходу из функции вернуть указатель на эту переменную. Если переменная локальная и имеет автоматический класс памяти, такое решение будет работать через раз и оно считается грубой ошибкой. Почему, я писал выше (переменная определена в стеке и она будет затёрта со временем другими данными). Чтобы решить эту проблему, объявляем переменную с классом памяти static, она после этого существует всё время жизни программы (а не только пока лишь выполняется функция, в которой она объявлена) и в этом отношении она подобна переменным, объявленным глобально, и на эту переменную можно возвращать указатель при выходе из функции.
P.S.
Хотелось бы хороший пример привести такой функции, чтобы он не выглядел надуманно, но ничего пока в голову не приходит.
Добавлено через 10 минут
P.P.S.
Ну и конечно, такое решение имеет свои ограничения, связанные с тем, что у нас существует всего один экземпляр переменной и каждый раз функция при своём вызове будет перезаписывать значение, хранящееся в этом экземпляре, и возвращать один и тот же указатель на этот единственный экземпляр. Об этом я также писал выше. Просто об этом надо помнить.
Регистрация: 13.11.2012
Сообщений: 173
Всем спасибо за Ваши ответы на вопрос, который не давал мне заснуть!) И тем более не ожидал таких исчерпывающих ответов, очень приятно! JohnyWalker, а если саму функцию пометить как static, это же тоже будет верно?
ПС: я не полный новичок в программировании, просто так сложилось, что жизнь кинула на самые «низы» уровня программирования. Жаль, что начинал я не с этого, т.к. только сейчас складывается полная картина происходящего. Но уж лучше поздно, чем никогда)
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
Garcian, функцию можно пометить ключевым словом static. Но функции в C (по крайней мере в стандартном C, не в ГНУшном его расширении, допускающем вложение функций друг в друга) — это всегда глобальные, внешние объекты (в отличие от переменных, которые могут быть и глобальными, и локальными). И функции, и глобальные переменные тоже можно помечать словом static, но смысл этого будет совсем другой, в отличие от локальных переменных, объявленных статическими. Они от этого как были, так и останутся глобальными объектами, но область их видимости сократится до пределов того файла, в котором они объявлены. Дело в том (ну ты это знаешь, наверное, Garcian), что в Си принята раздельная пофайловая компиляция. Каждый файл транслиуется отдельно и независимо, и при этом порождается свой объектный модуль. Так вот, все глобальные объекты — функции и переменные (но не типы) — считаются внешними объектами, видимыми в других файлах, если они не помечены словом static. Переменную или функцию, описанную в одном файле, можно объявить в другом файле. Объявление внешней функции в другом файле — это её прототип. Объявление (но не описание!) внешней переменной делается с ключевым словом (фиг его знает, чем оно считается, — квалификатором, модификатором или ещё чем) extern. Компилятор, встречая в тексте файла имена объявленных, но не описанных объектов, порождает какие-то свои адреса и таблицу имён. Дальше эти имена и адреса разрешаются уже линкером при компоновке объектных модулей в окончательный исполняемый файл. Так вот, если в разных исходных файлах программы будет описано несколько глобальных объектов (переменных, либо функций) с одним именем, произойдёт конфликт имён. Причём случится он не на уровне компилятора (т. к. файлы транслируются в C независимо и на стадии компиляции они друг друга не видят), а на уровне линкера. Линкер встретит, допустим, 2 объектных файла, в каждом из которых описана переменная с именем a, и не будет знать, что с ними делать, и выдаст ошибку конфликта имён. А ключевое слово static, применённое к глобальной функции или переменной, позволяет сделать имя переменной или функции невидимым для других объектных модулей. Другие модули не смогут воспользоваться этими объектами. Ими пользоваться можно будет только внутри того же файла, в котором они объявлены. И даже если в программе объявлена в каком-то другом файле ещё одна переменная или функция с тем же именем, но уже без слова static, конфликта имён не произойдёт.
Коротко говоря. Помечание словом static глобальных переменных и функций ограничивает область их видимости тем файлом, в котором они объявлены. Это способ изоляции объектов, который позволяет избежать конфликта имён на уровне линкера. Если сравнивать C с другими языками, в которых есть нормальные полноценные модули, такими как Модула-2, Оберон, Java, C#, то все объекты, не помеченные словом static, являются экспортируемыми объектами модуля, а объекты, помеченные словом static, являются внутренними скрытыми объектами модуля, не подлежащими экспорту.
Как-то так. Наверняка в моих словах есть куча неточностей, этим вопросом владею недостаточно хорошо. Пусть меня другие форумчане поправят, если найдут ошибки в моих словах. Но суть именно такая. Надеюсь, что понятно написал
Регистрация: 13.11.2012
Сообщений: 173
JohnyWalker, да, я в курсе, что для функций ключевое слово static своеобразный (по моему разумению) классификатор доступа, ограничивающий видимость функции этим файлом. Я задал этот вопрос, потому что видел в рабочем проекте, как из функции, помеченной static , возвращают указатель на структуру. Собственно, поэтому я и задался этим вопросом. Конечно, есть вероятность, что я упустил что-то, но по-моему в этой статической функции была объявлена нестатическая переменная, указатель на которую возвращался.
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
Сообщение от Garcian 
JohnyWalker, да, я в курсе, что для функций ключевое слово static своеобразный (по моему разумению) классификатор доступа, ограничивающий видимость функции этим файлом. Я задал этот вопрос, потому что видел в рабочем проекте, как из функции, помеченной static , возвращают указатель на структуру. Собственно, поэтому я и задался этим вопросом. Конечно, есть вероятность, что я упустил что-то, но по-моему в этой статической функции была объявлена нестатическая переменная, указатель на которую возвращался.
Ну так это самая настоящая ошибка. При чём тут static, помечающий функцию, и переменные, объявленные внутри этой функции? static здесь делает функцию невидимой за пределами этого файла, но он не делает статическими её внутренние переменные. Была объявлена стековая переменная (ещё такие переменные называются автоматическими в терминологии C) и на неё функцией возвращён указатель. Этого нельзя делать. Ту переменную тогда тоже нужно объявить со словом static.