Указатели как параметры
До сих пор указатель и его переменная были в одной функции main, и вы могли изменить переменную напрямую. Теперь о главной причине, по которой существуют указатели. Вспомните урок «Функции»: параметр — это копия аргумента. Когда функция меняет свой параметр, переменная вызывающей стороны остаётся прежней:
#include <stdio.h>
void add_bonus(int points, int bonus) {
points += bonus;
}
int main(void) {
int score = 10;
add_bonus(score, 5);
printf("Счёт: %d\n", score);
return 0;
}Программа выведет:
Счёт: 10Функция получила число 10, а не переменную score. Это как дать кому-то ксерокопию вашей контрольной: что бы он ни писал на копии, ваш оригинал не изменится.
Решение: передайте функции адрес переменной. Зная адрес, функция сможет добраться до ячейки вызывающей стороны и изменить то, что в ней лежит:
#include <stdio.h>
void add_bonus(int *points, int bonus) {
*points += bonus;
}
int main(void) {
int score = 10;
add_bonus(&score, 5);
add_bonus(&score, 3);
printf("Счёт: %d\n", score);
return 0;
}Программа выведет:
Счёт: 18Изменились три вещи: параметр стал указателем, int *points; внутри функции *points — это переменная вызывающей стороны; а в вызове передаётся адрес, &score. Адрес тоже копируется в параметр, но копия адреса всё равно ведёт к той же ячейке. Это как дать кому-то свой домашний адрес на листке бумаги: с копией листка он всё равно найдёт тот же дом.
В C это обычный способ, которым функция меняет переменные вызывающей стороны. Есть и ещё одно преимущество: по самому вызову видно, что функция может изменить score, ведь там стоит &. Параметр bonus остаётся обычным int, потому что функция его только читает.
Частые ошибки:
- Вызов без
&,add_bonus(score, 5);, заканчивается ошибкойincompatible integer to pointer conversion passing 'int' to parameter of type 'int *'; take the address with &. Компилятор подсказывает, что делать: возьмите адрес через&. - Если забыть звёздочку внутри функции и написать
points += bonus;, программа скомпилируется без ошибок, но изменит копию адреса, то есть сам указатель, а не счёт.scoreостанется равным 10. Когда функция «ничего не делает», первым делом проверьте звёздочки.
Задание
- 1
Функция
add_coinsдолжна добавлять монеты, но программа выводитМонеты: 80, жизни: 3, потому что функция меняет только свою копию. Измените её так, чтобы она менялаcoinsвmain: сделайте её первый параметр указателемint *coins, добавьте звёздочку внутри функции и передайте в вызове адрес&coins. Тогда программа выведетМонеты: 130, жизни: 3. - 2
В игре одна жизнь стоит 100 монет. Ниже
add_coinsнапишите функциюbuy_lifeс двумя параметрами типаint *:coinsиlives. Через указатели она вычитает 100 монет и прибавляет одну жизнь. В концеmain(передreturn 0;) вызовите её с адресами обеих переменных и снова выведите строку с монетами и жизнями тем же способом. Программа выведетМонеты: 130, жизни: 3, а затемМонеты: 30, жизни: 4. - 3
Жизнь можно купить, только если есть хотя бы 100 монет. Добавьте в
buy_lifeусловие: если монет меньше 100, функция ничего не меняет и выводитНе хватает монет. Затем вызовитеbuy_lifeещё раз и снова выведите строку с монетами и жизнями. Последние три строки вывода:Монеты: 30, жизни: 4,Не хватает монетиМонеты: 30, жизни: 4.