Дополнительно
Добавить в закладкиHexElf
Главный модератор
В первой части мы познакомились с основами указателей в Си — переменными, которые хранят адреса других переменных. Теперь рассмотрим более сложные концепции, связанные с указателями.
1. Нулевой указатель. Используется для инициализации указателей, которые будут назначены позже, или для проверки, был ли указатель инициализирован.
Пример:
#include <stdio.h>
int main(void) {
int *p = NULL;
if (p == NULL) {
printf("0\n");
}
return 0;
}
Ответ:
0
P.s printf("%d\n", *p) так делать нельза, будет ошибка сегментации
GDB:
Program received signal SIGSEGV, Segmentation fault.
0x000055555555514d in main ()
2. Массивы и указатели. В Си массивы тесно связаны с указателями. По сути, имя массива — это указатель на его первый элемент ( printf("Четвертый элемент: %d\n", p[3]); // 40.)
Пример:
#include <stdio.h>
int main(void) {
0 1 2 3 4
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;
printf("Первый элемент: %d\n", *p); //10
printf("Второй элемент: %d\n", *(p + 1)); // 20
printf("Третий элемент: %d\n", *(p + 2)); // 30
printf("Четвертый элемент: %d\n", p[3]); // 40
return 0;
}
Ответ:
Первый элемент: 10
Второй элемент: 20
Третий элемент: 30
Четвертый элемент: 40
Использование указателя позволяет перемещаться по массиву, добавляя или вычитая целые числа.
3. Константные указатели.
Существует два типа константных указателей:
Указатель на константу — нельзя изменить значение, на которое указывает указатель.
Константный указатель — нельзя изменить адрес, на который указывает указатель.
Пример:
#include <stdio.h>
int main(void) {
int x = 10, y = 20;
// Указатель на константу
const int *p1 = &x;
p1 = &y; //Можно изменить адрес
// Константный указатель
int * const p2 = &x;
*p2 = 30; // Можно изменить значение
// Константный указатель на константу
const int * const p3 = &x;
printf("%d\n", x);
return 0;
}
Как избежать утечек памяти и "висячих" указателей
1. Утечки памяти. Происходит, когда программа выделяет память, но не освобождает ее.
Пример:
Неправильная программа.
#include <stdio.h>
#include <stdlib.h>
void memory_leak_example() {
int *p = (int *)malloc(sizeof(int));
*p = 10;
printf("Значение: %d\n", *p);
}
int main(void) {
memory_leak_example();
return 0;
}
Valgrind:
==134480== HEAP SUMMARY:
==134480== in use at exit: 4 bytes in 1 blocks
==134480== total heap usage: 2 allocs, 1 frees, 1,028 bytes allocated
==134480==
==134480== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==134480== at 0x48457A8: malloc (vg_replace_malloc.c:446)
==134480== by 0x10915A: memory_leak_example
==134480== by 0x109195: main
==134480== LEAK SUMMARY:
==134480== definitely lost: 4 bytes in 1 blocks
==134480== indirectly lost: 0 bytes in 0 blocks
==134480== possibly lost: 0 bytes in 0 blocks
==134480== still reachable: 0 bytes in 0 blocks
==134480== suppressed: 0 bytes in 0 blocks
В общем сообщаеться что
Правильная программа.
#include <stdio.h>
#include <stdlib.h>
void proper_memory_usage() {
int *p = (int *)malloc(sizeof(int));
if (p == NULL) {
printf("Ошибка выделения памяти\n");
return;
}
*p = 10;
printf("Значение: %d\n", *p);
free(p);
p = NULL; \\Обнулять указатель после освобождения
}
int main(void) {
proper_memory_usage();
return 0;
}
Valgrind:
HEAP SUMMARY:
==148324== in use at exit: 0 bytes in 0 blocks
==148324== total heap usage: 2 allocs, 2 frees, 1,028 bytes allocated
==148324== All heap blocks were freed -- no leaks are possible
Что нового? В первой программе мы забыли вызвать
2. "Висячий" указатель. Это указатель, который ссылается на объект, который был удален или перемещен.
Пример:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p1 = (int *)malloc(sizeof(int));
*p1 = 10;
int *p2 = p1;
free(p1);
p1 = NULL;
return 0;
}
Что делать не нужно, к примеру,
Valgrind:
==171731== Invalid read of size 4
==171731== at 0x109199: main
==171731== Address 0x4a86040 is 0 bytes inside a block of size 4 free'd
==171731== at 0x48488EF: free
==171731== by 0x10918C: main
==171731== Block was alloc'd at
==171731== at 0x48457A8: malloc
==171731== by 0x10916A: main
Возможно, выход третьей части, ибо недавно для себя открыл новые возможности указателей, их тьма тьмущая
1. Нулевой указатель. Используется для инициализации указателей, которые будут назначены позже, или для проверки, был ли указатель инициализирован.
Пример:
#include <stdio.h>
int main(void) {
int *p = NULL;
if (p == NULL) {
printf("0\n");
}
return 0;
}
Ответ:
0
P.s printf("%d\n", *p) так делать нельза, будет ошибка сегментации
GDB:
Program received signal SIGSEGV, Segmentation fault.
0x000055555555514d in main ()
2. Массивы и указатели. В Си массивы тесно связаны с указателями. По сути, имя массива — это указатель на его первый элемент ( printf("Четвертый элемент: %d\n", p[3]); // 40.)
Пример:
#include <stdio.h>
int main(void) {
0 1 2 3 4
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;
printf("Первый элемент: %d\n", *p); //10
printf("Второй элемент: %d\n", *(p + 1)); // 20
printf("Третий элемент: %d\n", *(p + 2)); // 30
printf("Четвертый элемент: %d\n", p[3]); // 40
return 0;
}
Ответ:
Первый элемент: 10
Второй элемент: 20
Третий элемент: 30
Четвертый элемент: 40
Использование указателя позволяет перемещаться по массиву, добавляя или вычитая целые числа.
3. Константные указатели.
Существует два типа константных указателей:
Указатель на константу — нельзя изменить значение, на которое указывает указатель.
Константный указатель — нельзя изменить адрес, на который указывает указатель.
Пример:
#include <stdio.h>
int main(void) {
int x = 10, y = 20;
// Указатель на константу
const int *p1 = &x;
p1 = &y; //Можно изменить адрес
// Константный указатель
int * const p2 = &x;
*p2 = 30; // Можно изменить значение
// Константный указатель на константу
const int * const p3 = &x;
printf("%d\n", x);
return 0;
}
Как избежать утечек памяти и "висячих" указателей
1. Утечки памяти. Происходит, когда программа выделяет память, но не освобождает ее.
Пример:
Неправильная программа.
#include <stdio.h>
#include <stdlib.h>
void memory_leak_example() {
int *p = (int *)malloc(sizeof(int));
*p = 10;
printf("Значение: %d\n", *p);
}
int main(void) {
memory_leak_example();
return 0;
}
Valgrind:
==134480== HEAP SUMMARY:
==134480== in use at exit: 4 bytes in 1 blocks
==134480== total heap usage: 2 allocs, 1 frees, 1,028 bytes allocated
==134480==
==134480== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==134480== at 0x48457A8: malloc (vg_replace_malloc.c:446)
==134480== by 0x10915A: memory_leak_example
==134480== by 0x109195: main
==134480== LEAK SUMMARY:
==134480== definitely lost: 4 bytes in 1 blocks
==134480== indirectly lost: 0 bytes in 0 blocks
==134480== possibly lost: 0 bytes in 0 blocks
==134480== still reachable: 0 bytes in 0 blocks
==134480== suppressed: 0 bytes in 0 blocks
В общем сообщаеться что
4 байта не были освобождены.
Правильная программа.
#include <stdio.h>
#include <stdlib.h>
void proper_memory_usage() {
int *p = (int *)malloc(sizeof(int));
if (p == NULL) {
printf("Ошибка выделения памяти\n");
return;
}
*p = 10;
printf("Значение: %d\n", *p);
free(p);
p = NULL; \\Обнулять указатель после освобождения
}
int main(void) {
proper_memory_usage();
return 0;
}
Valgrind:
HEAP SUMMARY:
==148324== in use at exit: 0 bytes in 0 blocks
==148324== total heap usage: 2 allocs, 2 frees, 1,028 bytes allocated
==148324== All heap blocks were freed -- no leaks are possible
Что нового? В первой программе мы забыли вызвать
free(p)
из-за этого утечка. Во второй мы выделяем память, сначала с помощью sizeof(int)
определяем размер памяти, который нужно выделить, malloc(sizeof(int))
- выделяем блок памяти указанного размера. Обязательна проверка, правильно ли выделили память.2. "Висячий" указатель. Это указатель, который ссылается на объект, который был удален или перемещен.
Пример:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p1 = (int *)malloc(sizeof(int));
*p1 = 10;
int *p2 = p1;
free(p1);
p1 = NULL;
return 0;
}
Что делать не нужно, к примеру,
printf("%d\n", *p2);
— мы уже освободили память, но вызвав printf, мы продолжаем с ним работать, что и вызывает "висячий" указатель.Valgrind:
==171731== Invalid read of size 4
==171731== at 0x109199: main
==171731== Address 0x4a86040 is 0 bytes inside a block of size 4 free'd
==171731== at 0x48488EF: free
==171731== by 0x10918C: main
==171731== Block was alloc'd at
==171731== at 0x48457A8: malloc
==171731== by 0x10916A: main
Возможно, выход третьей части, ибо недавно для себя открыл новые возможности указателей, их тьма тьмущая

Отредактировано автором: