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

Содержание
Адрес в памяти
Введение в указатели
Пример
Как сделать 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 на Си
Оператор «стрелка» указатель на член структуры

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

Контакты и сотрудничество:
Рекомендую наш хостинг beget.ru
Пишите на info@urn.su если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящую по тематике.
3. Реклама на моём сайте имеет максимальный уровень цензуры. Если Вы увидели рекламный блок недопустимый для просмотра детьми школьного возраста, вызывающий шок или вводящий в заблуждение - пожалуйста свяжитесь с нами по электронной почте
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......
5. Статьи можно расшарить в соцсетях, нажав на иконку сети: