Similar presentations:
тема 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?
programming