Указатели в Си
Адрес в памяти | |
Введение в указатели | |
Пример | |
Как сделать 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;
}