Функции, области видимости и классы памяти

⏱ 25 мин · 🧩 заданий: 6

Функции

#include <stdio.h>

double dot(const double *a, const double *b, int n);   /* объявление (прототип) */

int main(void)
{
    double x[3] = {1, 2, 3}, y[3] = {4, 5, 6};
    printf("%f\n", dot(x, y, 3));
    return 0;
}

double dot(const double *a, const double *b, int n)    /* определение */
{
    double sum = 0.0;
    for (int i = 0; i < n; i++)
        sum += a[i] * b[i];
    return sum;
}
  • Объявление (прототип) сообщает компилятору имя, типы параметров и результата. Обычно прототипы лежат в заголовочных файлах .h.
  • Определение содержит тело.
  • void вместо типа результата — функция ничего не возвращает.

Передача по значению

В C аргументы копируются. Функция не может изменить переменную вызывающего, если не получит её адрес:

void reset(int *p) { *p = 0; }

int x = 5;
reset(&x);     /* x == 0 */

Так работают scanf("%d", &n), wait(&status), pipe(fds) — системные функции возвращают результаты через указатели.

Стек вызовов

При каждом вызове функции на стеке создаётся кадр (frame): адрес возврата, параметры, локальные переменные. При выходе кадр уничтожается.

старшие адреса ┌──────────────────────┐ │ кадр main │ x[3], y[3] ├──────────────────────┤ │ кадр dot │ адрес возврата, a, b, n, sum, i ├──────────────────────┤ ← указатель стека (rsp) │ свободно │ стек растёт вниз └──────────────────────┘ младшие адреса

Нельзя возвращать адрес локальной переменной

После выхода из функции её кадр освобождается, и указатель на локальную переменную указывает на мусор, который перезапишет следующий вызов.

Области видимости и время жизни

Где объявлена Видимость Время жизни Память
внутри блока {} до конца блока до выхода из блока стек (или регистр)
вне функций весь файл (и другие файлы через extern) вся программа сегмент данных
static внутри функции эта функция вся программа сегмент данных
static вне функций только этот файл вся программа сегмент данных

Классы памяти: static, extern, register

/* file1.c */
double *a;                 /* определение глобальной переменной */
static int hidden;         /* видна только в file1.c */

/* file2.c */
extern double *a;          /* объявление: «a определена в другом файле» */

void f(void)
{
    static int calls = 0;  /* сохраняется между вызовами */
    register int i;        /* просьба держать i в регистре */
    calls++;
}

Код из задачи 7

В условии: extern double *a; extern double *b; register int i; register double sum = 0.0;. Читаем так: i и sum — в регистрах, обращений к памяти не дают. А a и b — глобальные переменные-указатели где-то в памяти. Чтобы вычислить a[i], процессор сначала читает значение указателя a из памяти, а затем элемент массива. Раз a и b объявлены extern, они могут быть определены в разных файлах и лежать на разных страницах.

const и volatile

const int MAX = 100;            /* нельзя изменить */
void print(const char *s);      /* функция обещает не менять строку */

volatile int flag;              /* компилятор не должен кешировать значение в регистре:
                                   его может изменить обработчик сигнала или другой процесс */

volatile важен для переменных, которые меняет обработчик сигнала (тип volatile sig_atomic_t), и для памяти, общей с другими процессами. Но volatile не делает операции атомарными — для синхронизации нужны другие средства.

Итог

  • Аргументы передаются по значению; чтобы функция изменила переменную, передайте адрес.
  • Локальные переменные живут в стеке до выхода из функции; глобальные и static — всю программу.
  • extern — объявление переменной из другого файла, register — переменная в регистре (не память).
  • volatile запрещает кеширование в регистре, но не защищает от гонок.

Практика решено 0 из 6

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Ввод ответа

Что напечатает программа?

void inc(int x) { x++; }

int main(void) {
    int a = 1;
    inc(a);
    printf("%d\n", a);
    return 0;
}
Задание 2 Ввод ответа

Что напечатает программа после трёх вызовов f()?

int f(void) {
    static int n = 0;
    n++;
    return n;
}
int main(void) {
    f(); f();
    printf("%d\n", f());
}
Задание 3 Один вариант ответа

Что означает extern double *a; в задаче про TLB?

Задание 4 Несколько вариантов

Что верно про register int i;?

Задание 5 Написать код

Напишите функцию swap, которая меняет местами значения двух целых переменных по указателям: void swap(int *x, int *y).

Задание 6 Заполнить таблицу

Где хранится каждая переменная? Отметьте область памяти процесса.

СтекСегмент данных (.data/.bss)Куча
локальная int x внутри функции
глобальная int count
static int n внутри функции
память, полученная malloc(100)
параметр функции (если не в регистре)
Отметьте ячейку — «+», пустая — «−».

Войдите, чтобы отмечать пройденные уроки.