Указатели в Си

Содержание
Адрес в памяти
Введение в указатели
Пример
Как сделать segmentation fault
Другие статьи о С

Адрес в памяти

Прежде чем углубляться в указатели. Разберем оператор &

Создадим файл address.c и напишем небольшую программу.

#include <stdio.h> int main() { int a; printf("Address of variable a in memory is: %u\n", &a); int b; printf("Address of variable b in memory is: %u\n", &b); double c; printf("Address of variable c in memory is: %u\n", &c); int d; printf("Address of variable d in memory is: %u\n", &d); return 0; }

andrey@olegovich:/mnt/c/Users/Andrei/c$ gcc -o address address.c
andrey@olegovich:/mnt/c/Users/Andrei/c$ ./address

Address of variable a in memory is: 3802827836 Address of variable b in memory is: 3802827832 Address of variable c in memory is: 3802827824 Address of variable d in memory is: 3802827820

Как видите, мы получили адрес a и адрес b, который меньше на четыре, адрес c меньше уже на 8 потому что тип c double и под него нужно не 4 а 8 байт. С помощью этого метода, Вы можете проверить сколько точно байт занимает тот или иной тип у Вашего компилятора.

Введение в указатели

Указатели хранят адреса переменных в памяти. Хотя это и не реальный адрес в вашей CPU а виртуальный, который потом будет сопоставлен реальныму Вашей ОС, сути это не меняет.

Указатель - это тоже переменная, поэтому можно сделать указатель на указатель.

У указателей есть тип. Это очень важно, потому что под каждый тип выделяется определённый размер памяти.

char * ptr_to_char;

Тем не менее, можно задать указатель void то есть с неизвестным типом.

void * ptr_to_unknown;

С такими указателями надо быть особенно осторожными.

указатель = 0 это null pointer - особый указатель, который никуда не указывает.

int * ptr_to_nothing = 0;

Если Ваш указатель указывает куда-то не туда - Вы получите segmentation fault.

Разыменование это получение значения, которое записано там куда указывает указатель

Рассмотрим небольшой пример

#include <stdio.h> #include <stdlib.h> int main() { int money = 100; int *p = &money; // p указывает на место в памяти, //в котором хранится переменная money //в данный момент значение, которое там хранится это 100 printf("money = %d\n", money); // ожидаем увидеть 100 printf("p = %d\n", p); // ожидаем увидеть адрес переменной money // делаем разыменование printf("*p = %d\n", *p); // ожидаем увидеть 100 int balance; // делаем разыменование balance = *p + 20; printf("balance = %d\n", balance); return 0; }

Выполним этот код два раза подряд

andrey@olegovich:/mnt/c/Users/Andrei/c$ ./money

money = 100 p = -814504480 *p = 100 balance = 120

andrey@olegovich:/mnt/c/Users/Andrei/c$ ./money

money = 100 p = -691897248 *p = 100 balance = 120

money и balance, как и ожидалось не изменяются, а вот адрес памяти выделенный под money, за которым мы наблюдаем через p - изменяется

#include <stdio.h> int main() { int a; printf("Address of variable a in memory is: %u\n", &a); // Ожидаем какое-то заранее неизвестное число int b = 10; printf("Address of variable b in memory is: %u\n", &b); // Ожидаем число на 4 меньше предыдущего double d; printf("Address of variable d in memory is: %u\n", &d); // Ожидаем число на 8 меньше предыдущего float f = 5.8; printf("Address of variable f in memory is: %u\n", &f); // Ожидаем число на 4 меньше предыдущего int *ptr1; ptr1 = &a; int *ptr2 = &b; float *ptr3 = &f; printf("---------------------------------------------------\n"); printf("Value of ptr1 is %u\n", ptr1); // Ожидаем адрес a printf("Value of ptr2 is %u\n", ptr2); // Ожидаем адрес b printf("Value of ptr3 is %u\n", ptr3); // Ожидаем адрес f printf("---------------------------------------------------\n"); printf("Value stored in a = %u\n", a); // Ожидаем какой-то мусор printf("Value stored in a access with ptr1 = %u\n", *ptr1); // Ожидаем то же самое мусорное значение printf("Value stored in b = %u\n", b); // Ожидаем 10 printf("Value stored in b access with ptr2 = %u\n", *ptr2); // Ожидаем 10 printf("---------------------------------------------------\n"); printf("Comment: now a = 30 \n"); printf("---------------------------------------------------\n"); a = 30; printf("Address of variable a in memory is: %u\n", &a); // Ожидаем тот же самый адрес printf("Value stored in a = %u\n", a); // Ожидаем 30 printf("The sum of a and b is %d\n", *ptr1 + *ptr2); // Ожидаем 40 printf("---------------------------------------------------\n"); printf("Address of ptr1 in memory is: %u\n", &ptr1); // Ожидаем какое-то заранее неизвестное число printf("Address of ptr2 in memory is: %u\n", &ptr2); // Ожидаем число на 8 меньше предыдущего если у Вас x64 архитектура и на 4 меньше если x86 printf("Address of ptr3 in memory is: %u\n", &ptr3); // Ожидаем число на 8 меньше предыдущего если у Вас x64 архитектура и на 4 меньше если x86 printf("---------------------------------------------------\n"); printf("Comment: now ptr1 = ptr2\n"); printf("---------------------------------------------------\n"); ptr1 = ptr2; // теперь ptr1 указывает туда же куда и ptr2 printf("Address of variable b in memory is: %u\n", &b); // Адрес b не должен измениться с предыдущего вызова printf("Value of ptr1 is: %u\n", ptr1); // Ожидаем адрес b printf("Value stored in b access with ptr1 = %u\n", *ptr1); // Ожидаем 10 printf("---------------------------------------------------\n"); printf("Address of ptr1 in memory is: %u\n", &ptr1); // Адрес ptr1 не должен измениться printf("Address of ptr2 in memory is: %u\n", &ptr2); // Адрес ptr2 не должен измениться printf("Value stored in a = %u\n", a); // Ожидаем 30 printf("---------------------------------------------------\n"); return 0; }

Пример из wikipedia.org

int n = 6; // Объявление переменной n типа int и присваивание ей значения 6 int *pn = malloc( sizeof ( int ) ); // Объявление указателя pn и выделение под него памяти *pn = 5; // Разыменование указателя и присваивание значения 5 n = *pn; // Присваивание n того значения (5), на которое указывает pn free(pn); // Освобождение занятой памяти pn = &n; // Присваивание указателю pn адреса переменной n (указатель будет ссылаться на n) n = 7; // *pn тоже стало равно 7

Как сделать segmentation fault

Это не сложно - попробуйте разыменовать указатель, который никуда не указывает.

#include <stdio.h> #include <stdlib.h> int main() { int * p = 0; int var = *p + 3; return 0; }

Похожие статьи
Программирование на Си
Основы Си
Учебник по Си
Boolean в Си
Сокеты в Си
К и Р
Что такое argc, char * argv[]
Функция scanf()
Структуры в Си
Запросы к REST API на Си
Оператор «стрелка» указатель на член структуры