Указатели и работа с памятью в языке Си в контексте встроенных систем
ЦЕЛИ ЗАНЯТИЯ:
Актуализация
Память и адреса
Что такое указатель?
Две ключевые операции
Связь:
Инициализация указателей
Связь с массивами
Передача по ссылке
Возврат нескольких значений
Указатели в прошивке
Опасности:
Итоги:
ЗАДАНИЯ ДЛЯ ЗАКРЕПЛЕНИЯ:
ДОМАШНЕЕ ЗАДАНИЕ
83.60K
Category: programmingprogramming

тема 9. Указатели и работа с памятью в языке Си в контексте встроенных систем

1. Указатели и работа с памятью в языке Си в контексте встроенных систем

2. ЦЕЛИ ЗАНЯТИЯ:


Образовательная: Сформировать понимание концепции
указателей: адрес памяти, разыменование, арифметика
указателей. Научиться передавать данные в функции по
ссылке.
Развивающая: Развить «низкоуровневое» мышление —
умение представлять переменные не как абстрактные
значения, а как ячейки памяти с конкретными адресами.
Воспитательная: Формировать критическое отношение к
работе с памятью. Понимание того, что ошибка с
указателем (нулевой указатель, висячий указатель, выход за
границы) — это не просто «баг», а авария встроенной
системы, которая может стоить очень дорого.

3. Актуализация

Вспомним:
• Функции для структурирования кода
• Массивы передаются в функцию по адресу
• Изменения массива внутри функции видны снаружи
Вопрос: почему изменения массива видны, а изменения обычной
переменной — нет?
Ответ: потому что массив передаётся по ссылке (через указатель)!

4.

Как прочитать значение из регистра
микроконтроллера?
• Регистр — это ячейка памяти по фиксированному
адресу.
Как к ней обратиться?
• Нужно знать адрес!
Как в языке Си работать с адресами?
• Указатели!

5. Память и адреса

Память — массив ячеек с номерами (адресами).
Адрес:
0x1000
Значение:
[42]
Переменные: x
0x1004
0x1008
[100]
[7]
[3]
y
arr[0]
arr[1]
Каждая переменная имеет:
Значение (что хранится)
Адрес (где хранится)
Операция & возвращает адрес.
0x100C

6. Что такое указатель?

• Указатель — переменная, которая хранит адрес другой
переменной.
int x = 42;
int *ptr = &x;
// ptr хранит адрес x
Разбор:
int * — тип «указатель на int»
ptr — имя переменной
&x — адрес x
Операция * (разыменование) даёт значение по адресу:
*ptr = 100;
// изменить значение x через ptr

7. Две ключевые операции

Операция
Смысл
Пример
&
Взять адрес
&x
*
Разыменовать
*ptr

8. Связь:

int x = 42;
int *ptr = &x;
printf("%d", *ptr);
// 42 — *ptr == x
Правило:
& и * — «зеркальные» операции
*&x == x

9. Инициализация указателей

Опасность: неинициализированный указатель содержит «мусор».
int *ptr;
//
опасно!
*ptr = 10;
// непредсказуемо
Правильно: инициализировать NULL:
int *ptr = NULL;
if (ptr != NULL) {
*ptr = 10;
// безопасно
}
В микроконтроллерах обращение по «мусорному» адресу = HardFault (сбой).

10. Связь с массивами

Имя массива — указатель на первый элемент:
int arr[5] = {10, 20, 30, 40, 50};
Эквивалентности:
arr[0]
==
*arr
arr[1]
==
*(arr + 1)
arr[i]
==
*(arr + i)
Адресная арифметика:
int *p = arr;
p++;
// p теперь указывает на arr[1]
printf("%d", *p);
// 20

11. Передача по ссылке

• Передача по значению (без указателя):
void change(int x) {
x = 100;
// меняется копия
}
int a = 5;
change(a);
// a = 5, не изменилось

12.

• Передача по ссылке (через указатель):
void change(int *x) {
*x = 100;
// меняется оригинал
}
int a = 5;
change(&a);
// a = 100, изменилось

13. Возврат нескольких значений

void min_max(int arr[], int n, int *min,
int *max) {
*min = arr[0];
*max = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] < *min) *min = arr[i];
if (arr[i] > *max) *max = arr[i];
}
}
.
// Вызов:
int minimum, maximum;
min_max(arr, n, &minimum, &maximum);
Применение: анализ данных за один
проход

14. Указатели в прошивке

1. Регистры МК:
#define PORTB (*(volatile uint8_t *)0x25)
PORTB = 0xFF;
(uint8_t *)0x25 — указатель на адрес
* — разыменование
volatile — «не оптимизируй»

15.

2. Буферы данных:
void process(uint8_t *buf, int len) {
for (int i = 0; i < len; i++) {
buf[i] ^= 0xFF;
}
}
// инверсия

16. Опасности:

Опасность
Последствия
NULL-указатель
HardFault / Segfault
Неинициализированный
Непредсказуемо
Выход за границы
Порча данных
«Висячий» указатель
Мусор
Правила:
Инициализируйте NULL
Проверяйте на NULL
Не выходите за границы
Используйте const

17. Итоги:

• Что мы узнали:
Указатель — переменная, хранящая адрес
& — взять адрес, * — разыменовать
Имя массива — указатель на 1-й элемент
Указатели позволяют менять данные в функциях
• Указатели — мост между Си и железом!

18. ЗАДАНИЯ ДЛЯ ЗАКРЕПЛЕНИЯ:

Задание 1. Что выведет программа?
int x = 5;
int *p = &x;
*p = 10;
printf("%d\n", x);

19.

Задание 2. Найдите ошибку
int *p;
*p = 42;
printf("%d\n", *p);

20.

Задание 3. Что выведет?
int arr[] = {10, 20, 30, 40};
int *p = arr;
printf("%d\n", *(p + 2));

21.

Задание 4. Чему равен x?
int x = 10;
int *p = &x;
int **pp = &p;
// указатель на указатель
**pp = 20;
printf("%d\n", x);

22. ДОМАШНЕЕ ЗАДАНИЕ

1. Написать программу: объявить переменные a = 5, b = 10. Через указатели поменять их
значения местами (функция swap).
2. Написать функцию void find_min_max(int arr[], int n, int *min, int *max), которая находит минимум и
максимум за один проход и возвращает их через указатели.
3. Написать программу: найти сумму элементов массива через обход указателем (не через
индексы).
4. Ответить на вопросы:
o
Чем отличается & от *?
o
Зачем нужны указатели, если есть массивы?
o
Что будет, если разыменовать NULL?
English     Русский Rules