@Xproz
Python Student

Указатели в языке Си?

Стал изучать указатели в Си и у меня появилось несколько вопросов по ним.

Приведу пример кода:
int a*;
printf("Address: %p\n", a);
printf("Value: %d\n", *a);
*a = 10;
printf("Updated value: %d\n", *a);

Output:
Address: 00400080
Value: 17744


После чего программа, не дойдя до последнего printf, завершается. Я надеюсь, что это не привело к порче стека, но тем не менее, почему программа завершилась? Вмешалась ОС? Но если так, то почему? Почему я не могу поменять значение самого указателя (ведь указатель - это всего лишь переменная, хранящая адрес значения, а не само значение).

Зато, если инициализировать указатель адресом переменной, то все работает:
int number = 123;
int a* = &number;
printf("Address: %p\n", a);
printf("Value: %d\n", *a);
*a = 10;
printf("Updated value: %d\n", *a);

Output:
Address: 0061FF18
Value: 123
Updated value: 10


Еще интересно: могу ли я указателю дать адрес на конкретную ячейку в памяти?
Из предыдущих примеров видно, что я получаю адрес значения. Изначально, вообще, я хотел сделать следующее: получить адрес в памяти и присвоить ему произвольное значение. После этого создать новую программу, где я хотел обратиться к известному мне адресу и затем посмотреть - сохранилось ли значение или нет.
Но если я напишу что-то по типу:
int *a = 0x0061FF18;
Output:
warning: initialization of 'int *' from 'int' makes pointer from integer without a cast


то очевидно получаю предупреждение, ведь вряд ли адрес памяти у указателя хранится в виде 16-ричного целочисленного литерала.
Так как можно обратиться (и можно ли вообще) к известному заранее участку памяти?
  • Вопрос задан
  • 199 просмотров
Решения вопроса 1
jcmvbkbc
@jcmvbkbc
http://dilbert.com/strip/1998-08-24
почему программа завершилась? Вмешалась ОС? Но если так, то почему?

Потому что у тебя был неинициализированный указатель, который по стечению обстоятельств указывал в область памяти которую можно читать (мы это знаем, поскольку ты смог оттуда прочитать значение) но нельзя изменять (поскольку ну ты понял).

Почему я не могу поменять значение самого указателя (ведь указатель - это всего лишь ссылка на адрес памяти, а не на значение).

Значение указателя ты поменять можешь, но это не то, что было написано в коде. В коде было написано поменять значение на которое указатель указывает. Поменять указатель было бы a = (int *)10;

могу ли я указателю дать ссылку на конкретную ячейку в памяти?

Можешь, но под ОС с виртуальной памятью это в большинстве случаев не имеет смысла. API в таких ОС обычно устроены так, что ты просишь выделить область памяти с заданными характеристиками, а ОС выполняет выделение и возвращает тебе адрес выделенного участка.

получаю ошибку, ведь вряд ли адрес памяти у указателя хранится в виде 16-ричного целочисленного литерала.

В памяти всё хранится как последовательность байтов. Любую последовательность байтов можно проинтерпретировать как указатель. Но чтобы компилятор С понял что ты имеешь в виду, ему надо явно сказать, что ты хочешь проинтерпретировать целое число как адрес: int *a = (int *)0x0061FF18;
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
CityCat4
@CityCat4
Жил-был у бабушки серенький троллик...
Вмешалась ОС? Но если так, то почему?

Потому что классическая ошибка - использование неинициализированного указателя. Ты обьявил a указателем на целое, но не присвоил ему никакого значения (через malloc(), calloc() или функции, возвращающие указатель). Обьявленная переменная была выделена в куче и получила какое-то мусорное значение, доступное твоей программе (вполне может быть указатель на часть кода, например, на ядро, на общие функции). Ты его прочитал.
При попытке записать по данному адресу поймал SIGSEGV, потому что данный адрес твоей программе по записи недоступен. Все, как должно быть.
могу ли я указателю дать ссылку на конкретную ячейку в памяти?

Теоретически можешь, но практически ось тебе сразу выдаст SIGSEGV, потому что писать ты можешь только в те области памяти, которые тебе доступны по записи.
warning: initialization of 'int *' from 'int' makes pointer from integer without a cast

Ну правильно говорит - инициализация делается без приведения типа. Такой финт ушами часто делают со строками - по умолчанию выставляя там указатель на NULL (что обычно означает, что строке еще не выделялась память), напрмер
char *str = (char *) NULL;
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы