Как передать структуру в функцию 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 и возвращает его в качестве результата.
Указатели на структуры. Передача структур в функции
Начнем это занятие с указателей на структуры. Мы уже с вами знаем, что такое указатели и как с ними можно работать с обычными переменными базовых типов: char, int, double, …. Пришло время узнать, как используются указатели совместно со структурами.
Для примера опишем в программе простую структуру для представления двумерных радиус-векторов:
struct tag_vector { double x; double y; };
А ниже в функции main() объявим переменную на эту структуру и указатель этого типа:
int main(void) { struct tag_vector v = {1.0, 2.0}; struct tag_vector *ptr_v; return 0; }
Чтобы через указатель ptr_v работать с переменной v, необходимо присвоить адрес этой переменной указателю. Делается это уже известным нам образом с помощью операции взятия адреса:
ptr_v = &v;
После этого мы можем считывать и записывать информацию в структуру с помощью указателя ptr. Используя текущие знания об указателях, нам пришлось бы делать это, например, так:
(*ptr_v).x = 10.0; // запись нового значения double y = (*ptr_v).y; // считывание значения
Причем круглые скобки вокруг указателя обязательны, так как приоритет унарных операций возрастает при движении справа-налево. Поэтому приоритет операции «точка» выше приоритета операции разыменования. Если круглые скобки не прописывать:
*ptr_v.x = 10.0;
то операция «точка» была бы применена к указателю ptr_v, а не к структуре, на которую он указывает.
Как видите, имеем не очень удобную запись для доступа к отдельным полям структуры через указатель. Поэтому в языке Си именно для целей доступа к полям составных типов данных через указатель, был введен специальный оператор, состоящий из двух символов: «-» минус и «>» больше. И используется следующим образом:
ptr_v->x = 10.0; // запись нового значения double y = ptr_v->y; // считывание значения
Эта запись и предыдущая – абсолютно одно и то же. Но так визуально программа выглядит куда нагляднее и понятнее. И, обратите внимание, когда используется операция «->», то звездочку перед указателем записывать не нужно.
Далее, имея указатели на структуры, мы получаем возможность их динамического формирования в основной памяти устройства — «куче». Для этого можно воспользоваться уже знакомой нам функцией malloc() для выделения памяти под структуру:
ptr_v = malloc(sizeof(struct tag_vector));
Записать туда какие-либо значения:
ptr_v->x = -1.0; ptr_v->y = 2.0;
и вывести их на экран:
printf("x = %.2f, y = %.2f\n", ptr_v->x, ptr_v->y);
с последующим освобождением памяти:
free(ptr_v);
Фактически, с помощью функций malloc() и free() мы самостоятельно создали новую переменную на структуру struct tag_vector в основной памяти устройства (а не в стеке вызова функций), записали туда данные и прочитали их с выводом на экран. И, так как область памяти выделялась в «куче», то такая переменная продолжает существовать после завершения функции, где была создана, пока не будет вызвана функция free() для этой области памяти. Все эти детали нужно очень хорошо понимать для грамотного составления программного кода.
Возврат структур из функций
Давайте теперь посмотрим, как можно возвращать структуры из функций. Для примера объявим функцию, которая будет формировать структуру struct tag_vector по переданным ей вещественным значениям:
struct tag_vector create_vector(double x, double y) { struct tag_vector v = {x, y}; return v; }
Затем, в функции main() вызовем эту функцию:
int main(void) { struct tag_vector bias = create_vector(2.56, -7.88); printf("bias.x = %.2f, bias.y = %.2f\n", bias.x, bias.y); return 0; }
После запуска программы увидим строку:
bias.x = 2.56, bias.y = -7.88
Как работает функция create_vector()? При ее вызове формируется переменная на структуру struct tag_vector, инициализируется переданными значениями и возвращается. Вот здесь важный момент. Возврат структуры есть не что иное, как копирование всего ее содержимого в переменную bias. Сама же переменная v перестает существовать после завершения функции create_vector(). Вот такие процессы создания переменной v и копирования ее содержимого присутствуют в нашей программе. Если структура небольшого размера, то это не критично. Но если она занимает большие объемы, то возникает сразу две проблемы: первая – это значительный расход ограниченной памяти стекового фрейма; вторая – копирование большого объема памяти при возврате такой структуры из функции.
Для разрешения этих проблем можно воспользоваться механизмом динамического выделения памяти с помощью функций malloc() и free(). В этом случае функцию create_vector() можно переписать в таком виде:
struct tag_vector* create_vector(double x, double y) { struct tag_vector* v = malloc(sizeof(struct tag_vector)); v->x = x; v->y = y; return v; }
И, затем использовать ее следующим образом:
int main(void) { struct tag_vector* bias = create_vector(2.56, -7.88); printf("bias.x = %.2f, bias.y = %.2f\n", bias->x, bias->y); free(bias); return 0; }
Но это более тонкий процесс. Как только мы прописали функцию malloc() нужно не забыть вызвать функцию free(). Казалось бы, в нашем простом примере сделать это не сложно. Однако не все так очевидно. Например, если дважды вызвать функцию create_vector() с присвоением адреса одному и тому же указателю:
int main(void) { struct tag_vector* bias = create_vector(2.56, -7.88); bias = create_vector(3.0, -7.0); printf("bias.x = %.2f, bias.y = %.2f\n", bias->x, bias->y); free(bias); return 0; }
То адрес первой выделенной области памяти окажется потерянным. И мы уже в рамках программы не сможем ее освободить. Она будет освобождена только самой ОС при завершении программы. Это пример того самого эффекта под названием утечка памяти. И, как видите, допустить такой промах проще простого! Поэтому переходить на уровень указателей стоит только в случаях, когда размещение и копирование больших структур действительно критически сказывается на скорости работы программы. Без этого лучше прописывать структуры на уровне обычных автоматических переменных, как мы это сделали в первом случае.
Передача структур в функции
Следующим шагом посмотрим на механизм передачи структур в функции. Предположим, нам нужно объявить функцию, которая бы складывала два вектора между собой. В самом простом варианте это можно сделать так:
struct tag_vector sum_vector(const struct tag_vector v1, const struct tag_vector v2) { struct tag_vector res = {v1.x + v2.x, v1.y + v2.y}; return res; }
А, затем, вызвать ее из функции main() следующим образом:
int main(void) { struct tag_vector bias = create_vector(2.56, -7.88); struct tag_vector one = create_vector(1.0, 1.0); struct tag_vector result = sum_vector(bias, one); printf("result.x = %.2f, result.y = %.2f\n", result.x, result.y); return 0; }
Здесь create_vector() – первый вариант этой функции без указателей. В итоге, сначала создаются две структуры bias и one, а затем, вызывается функция sum_vector(), которой передаются структуры в качестве аргументов. Что происходит дальше? Да, содержимое структур bias и one копируется в соответствующие параметры v1 и v2. Внутри функции sum_vector() работа ведется уже с копиями и на основе их данных создается третья переменная res с суммой координат векторов v1 и v2. Затем, сформированная структура возвращается функцией опять же с копированием данных в переменную result. После завершения функции sum_vector() все ее параметры и переменная res автоматически исчезают.
Такой не самый быстрый, но безопасный процесс мы получаем при работе функции sum_vector(). Можно ли как то ускорить ее работу, сохранив безопасность работы? На самом деле да, можно, если в качестве параметров прописать константные указатели:
struct tag_vector sum_vector(const struct tag_vector* v1, const struct tag_vector* v2) { struct tag_vector res = {v1->x + v2->x, v1->y + v2->y}; return res; }
Обратите внимание, только у параметров, но не у возвращаемого значения. Возвращать мы по-прежнему будем структуру целиком. Безопасность обеспечивается благодаря использованию указателей, которые могут только читать данные из переданных структур, но не менять их. А более высокая скорость за счет того, что теперь нам не нужно копировать структуры целиком в каждый параметр, а лишь передать адреса:
struct tag_vector result = sum_vector(&bias, &one);
Это и безопасно и быстро. В таких случаях применение указателей оправданно.
Давайте пропишем еще одну функцию, которая бы суммировала координаты в первом переданном векторе:
void isum_vector(struct tag_vector* v1, const struct tag_vector* v2) { v1->x += v2->x; v1->y += v2->y; }
Смотрите, функция ничего не возвращает, но первый указатель не константный, через него можно менять данные в структуре, на которую он ссылается. В результате, функция isum_vector() изменяет координаты первой переданной структуры:
isum_vector(&bias, &one);
Или, вместо второго аргумента one можно сразу объявить структуру с нужным набором данных:
isum_vector(&bias, &(struct tag_vector) {0.5, -0.5});
Обратите внимание на эту конструкцию. В круглых скобках мы прописываем тип структуры, а затем, в фигурных – ее данные. Это бывает очень удобно, чтобы не создавать отдельно временные переменные только для передачи данных в нужном формате. И то же самое относится к возвращаемым значениям. Функции create_vector() и sum_vector() можно переписать таким образом:
struct tag_vector create_vector(double x, double y) { return (struct tag_vector) {x, y}; } struct tag_vector sum_vector(const struct tag_vector* v1, const struct tag_vector* v2) { return (struct tag_vector) {v1->x + v2->x, v1->y + v2->y}; }
То есть, буквально в одну строчку.
Директива typedef со структурами
В заключение этого занятия сделаем еще одно небольшое улучшение нашего текста программы. В ней очень часто фигурирует тип struct tag_vector и визуально это воспринимается не лучшим образом, да и, кроме того, постоянно приходится с клавиатуры набирать эту длинную последовательность символов. Поправим этот момент с помощью уже знакомой нам директивы typedef. Напомню, что она позволяет любой существующий тип заменить другим именем. Воспользуемся этим и запишем определение структуры следующим образом:
typedef struct tag_vector { double x; double y; } VECTOR;
Теперь вместо слов struct tag_vector можно использовать имя VECTOR:
VECTOR create_vector(double x, double y) { return (VECTOR) {x, y}; } VECTOR sum_vector(const VECTOR* v1, const VECTOR* v2) { return (VECTOR) {v1->x + v2->x, v1->y + v2->y}; } void isum_vector(VECTOR* v1, const VECTOR* v2) { v1->x += v2->x; v1->y += v2->y; } int main(void) { VECTOR bias = create_vector(2.56, -7.88); VECTOR one = create_vector(1.0, 1.0); VECTOR result = sum_vector(&bias, &one); isum_vector(&bias, &(VECTOR) {0.5, -0.5}); return 0; }
Теперь тип стал более читабельным, а текст программы более наглядным.
Видео по теме
Язык Си. Рождение легенды
#1. Этапы трансляции программы в машинный код. Стандарты
#2. Установка компилятора gcc и Visual Studio Code на ОС Windows
#3. Структура и понимание работы программы «Hello, World!»
#4. Двоичная, шестнадцатеричная и восьмеричная системы счисления
#5. Переменные и их базовые типы. Модификаторы unsigned и signed
#6. Операция присваивания. Числовые и символьные литералы. Операция sizeof
#7. Стандартные потоки ввода/вывода. Функции putchar() и getchar()
#8. Функция printf() для форматированного вывода
#9. Функция scanf() для форматированного ввода
#10. Арифметические операции: сложение, вычитание, умножение и деление
#11. Арифметические операции деления по модулю, инкремента и декремента
#12. Арифметические операции +=, -=, *=, /=, %=
#13. Булевый тип. Операции сравнения. Логические И, ИЛИ, НЕ
#14. Условный оператор if. Конструкция if-else
#15. Условное тернарное выражение
#16. Оператор switch множественного выбора. Ключевое слово break
#17. Битовые операции И, ИЛИ, НЕ, XOR. Сдвиговые операции
#18. Генерация псевдослучайных чисел. Функции математической библиотеки
#19. Директивы макропроцессора #define и #undef
#20. Директива #define для определения макросов-функций. Операции # и ##
#21. Директивы #include и условной компиляции
#22. Оператор цикла while
#23. Оператор цикла for
#24. Цикл do-while с постусловием. Вложенные циклы
#25. Операторы break, continue и goto
#26. Указатели. Проще простого
#27. Указатели. Приведение типов. Константа NULL
#28. Долгожданная адресная арифметика
#29. Введение в массивы
#30. Вычисление размера массива. Инициализация массивов
#31. Указатели на массивы
#32. Ключевое слово const с указателями и переменными
#33. Операции с массивами копирование, вставка, удаление и сортировка
#34. Двумерные и многомерные массивы. Указатели на двумерные массивы
#35. Строки. Способы объявления, escape-последовательности
#36. Ввод/вывод строк в стандартные потоки
#37. Строковые функции strlen(), strcpy(), strncpy(), strcat(), strncat()
#38. Строковые функции сравнения, поиска символов и фрагментов
#39. Строковые функции sprintf(), atoi(), atol(), atoll() и atof()
#40. Объявление и вызов функций
#41. Оператор return. Вызов функций в аргументах
#42. Прототипы функций
#43. Указатели как параметры. Передача массивов в функции
#44. Указатели на функцию. Функция как параметр (callback)
#45. Стековый фрейм. Автоматические переменные
#46. Рекурсивные функции
#47. Функции с произвольным числом параметров
#48. Локальные и глобальные переменные
#49. Локальные во вложенных блоках
#50. Ключевые слова static и extern
#51. Функции malloc(), free(), calloc(), realloc(), memcpy() и memmove()
#52. Перечисления (enum). Директива typedef
#53. Структуры. Вложенные структуры
#54. Указатели на структуры. Передача структур в функции
#55. Реализация стека (пример использования структур)
#56. Объединения (union). Битовые поля
#57. Файловые функции: fopen(), fclose(), fgetc(), fputc()
#58. Функции perror(), fseek() и ftell()
#59. Функции fputs(), fgets() и fprintf(), fscanf()
#60. Функции feof(), fflush(), setvbuf()
#61. Бинарный режим доступа. Функции fwrite() и fread()
#1. Первая программа на С++
#2. Ввод-вывод с помощью объектов cin и cout
#3. Пространства имен (namespace)
#4. Оператор using
#5. Новые типы данных. Приведение типов указателей
#6. Инициализация переменных. Ключевые слова auto и decltype
#7. Ссылки. Константные ссылки
#8. Объект-строка string. Операции с объектами класса string
#9. Файловые потоки. Открытие и закрытие файлов. Режимы доступа
#10. Чтение и запись данных в файл в текстовом режиме
#11. Чтение и запись данных в файл в бинарном режиме
#12. Перегрузка функций. Директива extern C
#13. Значения параметров функции по умолчанию
#15. Лямбда-выражения. Объявление и вызов
#16. Захват внешних значений в лямбда выражениях
#17. Структуры в С++, как обновленный тип данных
#18. Структуры. Режимы доступа. Сеттеры и геттеры
#19. Структуры. Конструкторы и деструкторы
#20. Операторы new / delete и new [] / delete []
© 2023 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
Как передать структуру в функцию c
Пример передачи структуры в функцию по указателю, и пример передачи массива структур.
Имеется некая структура console_menu :
Она используется в двух следующих функциях:
// Функция принимает одну структуру по указателю
// Функция принимает массив структур
sprintf(menu[0].text,»One item in array»);
sprintf(menu[1].text,»Two item in array»);
Вызов этих функция выглядит так:
// Передача по указателю одной структуры (передается адрес структуры)
// Передача массива структур
printf(«%s %d\n»,omnu.text, omnu.value);
- Стандарт языка программирования Си
- Передача параметра в функцию по указателю в C стиле
- Передача параметров в функцию по указателю (C стиль) и по ссылке (C++ стиль)
- Передача структуры в функцию и изменение значений элементов структуры
- В чем разница объявления строки как массива и как указателя
- Использование очень больших чисел
- getch() в Linux
- Аргументы функции main()
- Как побайтно считать файл
- Реализация циклического сдвига ROR и ROL
- Динамическая загрузка библиотек в Linux
- Функция fmemopen() — открытие набора байт как файла в памяти с получением файлового дескриптора
- Подмена дефайнов (#define)
- Успешной отладки, шутка
- Как описывать функции с аргументами по-умолчанию в C/C++
- Форматированный вывод через функцию printf
- Создание и удаления двухмерного и трехмерного динамического массива
- Как передать в функцию двумерный массив, размер которого известен
- Как передать в функцию динамический двумерный массив
- Как правильно читать объявления в Си
- Вычисление pbkdf2 на языке C
- Определение разрядности платформы 32 или 64 бит
- Функции getuid() и geteuid()
- Указатели и символьные строки в языке C
- Практическое применение LD_PRELOAD или замещение функций в Linux
- Проблемы C-подобных языков. Где находиться типу: справа или слева? (Теория)
- Откуда в языке Си появился синтаксис указателей, и для чего он предназначался изначально
- Как в языке Си вызвать функцию, для которой известен адрес вызова в виде числа
- Вызов функции по известному адресу в языке Си — абстрактный тип данных указателя на функцию
- Структуры в языке Си. Определения структур в сравнении с языком C++
- Структуры в языке Си. Указатели на структуры
- Структуры в языке Си. Массивы структур
- Операция запятая «,» в языке Си и Си++
- Самый быстрый и оптимальный способ копирования строк в Cи и C++
- Стоит запомнить: цикл for в языке Си/Си++ — это цикл с предусловием
- Как происходит компиляция C/C++ кода. Единица трансляции
Передача всей структуры в функцию
Когда структура используется как аргумент функции, передается вся структура с помощью стандартной передачи по значению. Это означает, что любые изменения, внесенные в содержимое структуры внутри функции, не повлияют на структуру, используемую в качестве аргумента.
Когда структура используется как параметр, самое важное — это запомнить, что тип аргумента должен соответствовать типу параметра. Лучший способ сделать это — определить структуру глобально, а затем использовать ее ярлык для объявления необходимых структурных переменных и параметров. Например:
/* объявление типа структуры */
struct struct_type int a, b;
char ch;
>;
void f1(struct struct_type parm);
int main(void)
struct struct_type arg; /* объявление arg */
arg.a = 1000;
f1(arg);
return 0;
>
void f1(struct struct_type parm) printf(«%d», parm.a);
>
Данная программа выводит число 1000 на экран. Можно видеть, что как arg, так и parm объявлены как структуры типа struct_type.