Указатели на структуры
В предыдущем упражнении функция sell_one меняла только свою копию товара. Решение вы знаете из урока «Указатели»: передайте функции адрес переменной, и через него она сможет изменить оригинал. Со структурами это работает так же:
typedef struct {
char name[20];
double price;
int count;
} Product;
void add_count(Product *item, int extra) {
(*item).count += extra;
}Параметр item — указатель на Product. *item — товар, на который он указывает, а (*item).count — количество этого товара. Скобки необходимы: точка вычисляется раньше звёздочки, поэтому *item.count означало бы «поле count у item, а потом *», но item — указатель, а не структура. Поэтому в C есть более короткая и понятная запись — стрелка ->, которая пишется как знак минус и знак «больше»:
#include <stdio.h>
typedef struct {
char name[20];
double price;
int count;
} Product;
void add_count(Product *item, int extra) {
item->count += extra;
}
int main(void) {
Product milk = {"Milk", 24.9, 3};
add_count(&milk, 2);
printf("%s: %d\n", milk.name, milk.count);
return 0;
}Программа выведет:
Milk: 5item->count означает в точности то же, что и (*item).count: «поле count структуры, на которую указывает item». Программисты пишут стрелку, и мы тоже будем. Функцию вызывают с адресом товара, &milk, как и с любым параметром-указателем.
Так когда же писать точку, а когда стрелку? Смотрите, что стоит слева. Структура (milk, cart[i] или параметр Product item) требует точку. Указатель на структуру (параметр Product *item) требует стрелку. Если их перепутать, компилятор сообщит об этом и даже подскажет правильный вариант:
main.c:10:9: error: member reference type 'Product *' is a pointer; did you mean to use '->'?
10 | item.count += extra;
| ~~~~^
| ->В обратную сторону, milk->count заканчивается ошибкой member reference type 'Product' is not a pointer; did you mean to use '.'?. А если забыть & в вызове, add_count(milk, 2), ошибка будет такой: passing 'Product' to parameter of incompatible type 'Product *'; take the address with &. Функции нужен адрес, так передайте его через &.
Указатель полезен, даже если функция не меняет структуру. Параметр-структура получает копию всей структуры при каждом вызове, а некоторые структуры велики. Указатель — это всего один адрес. Если функция только читает структуру, добавьте const, как вы делали с массивами: в double line_total(const Product *item) функция может прочитать item->price, но случайно изменить его не сможет.
А что с массивом структур? Функция получает сам массив вызывающей стороны, а не копию, как с любым массивом. В функции с параметром Product cart[] элемент cart[i] — это структура, поэтому пишется точка, cart[i].count = 0;, и изменение остаётся в массиве вызывающей стороны. Чтобы передать в add_count один элемент массива, отдайте ей адрес этого элемента: add_count(&cart[1], 2);.
Задание
- 1
Функция
record_winдолжна добавлять команде 3 очка за победу, но программа выводитLions, очков: 5. Измените функцию так, чтобы она получала указатель на команду, используйте в ней->и вызовите её с адресомlions. Тогда программа выведетLions, очков: 8. - 2
Ниже
record_winнапишите функциюvoid record_match(Team *team, int scored, int conceded)для любого матча. Если команда забила больше голов, чем пропустила, она победила и получает 3 очка; за ничью (одинаковое число голов) она получает 1 очко; за поражение — ничего. В концеmain(передreturn 0;) вызовите её дважды,record_match(&lions, 2, 2)иrecord_match(&lions, 3, 1), а затем снова выведите команду тем жеprintf. Последняя строка:Lions, очков: 12.