Ошибка №7. Операции над указателями. Арифметика.
Запись от steelcraft размещена 27.06.2024 в 01:46
Показов 5313
Комментарии 75
|
Указатели, пожалуй, самая интересная тема в языке C. Собственно, больше ничего интересного в нем и нет, все остальное очевидно и просто. Именно указатели придают этому языку его силу и одновременно опасность. Неудивительно, что большинство ошибок на собеседованиях вызывает именно эта тема. Итак, очередной вопрос: перечислите операции над указателями. Как правило, операции поименования (&) и разыменования (*) проблем не вызывают. Далее черед доходит до адресной арифметики. Тут появляются первые симптомы: есть некоторое количество кандидатов, которые вообще не знают, что это такое. Как правило, это люди, которые специализируются в области электроники, а на C пишут лишь небольшие фрагменты, чтобы проверить работоспособность макета. В продукцию этот код не идет. В этом случае на этом дискуссию сворачиваем и переходим ко второй части собеседования - по электронике (за редким исключением эмбеддеры собеседуются по двум дисциплинам сразу). Бывает и такой ответ: поскольку указатель - это адрес, а адрес - это целое число, то над указателями можно делать любые арифметические операции. Определенная логика тут, конечно присутствует (о том, является ли указатель в действительности адресом, поговорим отдельно), но возникает вопрос: какова семантика результата? Что дает перемножение, деление или сложение адресов (про вычитание пока молчок, это отдельная тема)? Типичная реакция - пожимание плечами. Те, кто изучал язык более тщательно, называют еще две допустимые операции: прибавление/вычитание целочисленной величины к указателю. Тут кроются еще две тонкости (правда, все же относительно толстые). 1. Каков результат прибавления целой величины k к указателю p? Плохой ответ: указатель смещается на k байтов относительно исходного положения. Хороший ответ: величина умножается на размер типа, на который указывает p, и полученное число используется как смещение указателя. Для вычитания верно то же самое. Вопрос довольно легкий, и с ним успешно справляется довольно большое число кандидатов. 2. При каком условии полученное новое значение указателя валидно? Тут обычно дело гораздо хуже, лишь небольшое количество кандидатов дает правильный ответ. Это уже показатель продвинутого уровня и дает возможность претендовать на миддла. Правильный ответ: если указатель указывал на элемент некоторого массива, и после смещения продолжает указывать на элемент этого же массива или на ячейку памяти непосредственно за концом массива, такая операция валидна. В противном случае возникает неопределенное поведение. В стандарте ISO/IEC 9899:2018 эта ситуация описана в разделе 6.5.6 "Additive operators", п. 8 (стр. 67). Итак, приходим к соглашению: к указателю можно прибавить (либо вычесть) целочисленное значение, и при определенных условиях мы получим новое значение указателя, смещенное на несколько элементов относительно исходного. Умножение, деление и сложение указателей не имеет никакого смысла. Вычитание указателей я пока обхожу, это тема отдельного вопроса. |
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 75
Комментарии
-
Раз это самая интересная тема- значит он ничем не отличается от других языков. И то-есть вот эта все на несколько постов - ни о чем. Хотя - не уверен, сейчас спрошу, вечером отпишусь.Запись от Hrethgir размещена 27.06.2024 в 05:32
-
C 1 2 3 4 5 6 7 8 9 10
#include <stdio.h> int main(){ int main; void * pmain = &main; printf( "%p\n", pmain); main: return pmain!=&main; }
Запись от politoto размещена 27.06.2024 в 08:29
-
Запись от sporta1982 размещена 27.06.2024 в 12:03
-
А в чем проблема? Прибавляйте на здоровье:
Сообщение от sporta1982
Результат:C 1 2 3 4 5 6 7 8 9 10
#include <stdio.h> int main(){ int main; void * pmain = &main; printf( "%p %p\n", pmain, pmain + 5); main: return pmain!=&main; }
Или я неправильно понял вопрос?Запись от steelcraft размещена 27.06.2024 в 13:09
-
Какой размерvoid?
Windows Batch file 1
cl /c /nologo void.c
Code 1 2
void.c void.c(7): error C2036: void *: неизвестный размер
Запись от politoto размещена 27.06.2024 в 13:27
-
Запись от sporta1982 размещена 27.06.2024 в 13:32
-
В вашем примере значение увеличилось на 5, а должно вроде как умножаться на размер типа. Указатель же указывает на тип int (по википедии 2 байта)?
Сообщение от steelcraft
Запись от testuser2 размещена 27.06.2024 в 13:33
-
Запись от sporta1982 размещена 27.06.2024 в 13:34
-
Размер void не определен:
Сообщение от politoto
error C2036: 'void *' : unknown size
Разработчики разных компиляторов трактуют по-разному, и все в чем-то правы. Стандарт пока молчит.Запись от steelcraft размещена 27.06.2024 в 13:37
-
Я использовал онлайн-компилятор (явно не оговаривается, но похож на GCC), он считает, что размер void - единица.
Сообщение от testuser2
Результат:C 1 2 3 4 5 6
#include <stdio.h> int main(){ printf( "%ld %ld\n", sizeof(void), sizeof(void *)); }
На самом деле на void. (Ну и попутно int формально может быть любого размера с некоторыми ограничениями, фактически обычно 2 или 4 байта).
Сообщение от testuser2
Запись от steelcraft размещена 27.06.2024 в 13:41
-
Вообще, конечно, странно, что такой тип можно задавать переменной, void, это же вроде "пустота", интересно какая в этом заложена философия )Запись от testuser2 размещена 27.06.2024 в 13:50
-
Запись от sporta1982 размещена 27.06.2024 в 13:57
-
Да кто его слушает, этот стандарт?!
Сообщение от steelcraft

Текущий черновик c++ не молчит:
https://eel.is/c++draft/expr.add#1
https://eel.is/c++draft/basic.fundamental#15
Сообщение от 7.6.6 Additive operators
Сравните с аналогичными местами т.н. стандартов Си.
Сообщение от 6.8.2 Fundamental types
Запись от politoto размещена 27.06.2024 в 14:11
-
Нет, переменной нельзя:
Сообщение от testuser2
C 1 2 3 4 5
int main() { void v; return 0; }
Можно только указывать на тип void:
C 1 2 3 4 5
int main() { void *pv; return 0; }
Запись от steelcraft размещена 27.06.2024 в 14:13
-
Не совсем. Хотя после приведения к числовому представлению выглядит очень похоже.
Сообщение от sporta1982
Запись от steelcraft размещена 27.06.2024 в 14:18
-
Вообще, конечно, нельзя.
Сообщение от testuser2
C 1
void v;
Bash 1
cc -c vv.c
Code 1 2 3
vv.c:1:6: error: storage size of ‘v’ isn’t known void v; ^
Windows Batch file 1
cl.exe -c vv.c -nologo
Code 1 2
vv.c vv.c(1): error C2182: v: недопустимое использование типа "void"
Запись от politoto размещена 27.06.2024 в 14:20
-
Во-первых, черновик; во-вторых, C++. Для компилятора C, увы, не указ. Поэтому компиляторы GCC и MS сейчас дают такой разнобой.
Сообщение от politoto
В действующем не увидел. Может, в будущем определят.
Сообщение от politoto
Запись от steelcraft размещена 27.06.2024 в 14:24
-
А может быть, потому, что это одно из многочисленных полувеликих extension gcc?
Сообщение от steelcraft
https://gcc.gnu.org/onlinedocs... Arith.html
Сообщение от GNU C Manual
Запись от politoto размещена 27.06.2024 в 14:51
-
Так если бы соответствовало стандарту, не было бы расширением по определению. Расширение - это всегда художественная самодеятельность.
Сообщение от politoto
Запись от steelcraft размещена 27.06.2024 в 15:04
-
Запись от CoderHuligan размещена 27.06.2024 в 15:06

Тем более видно что у вас получается x64 приложение.

