Форум программистов, компьютерный форум, киберфорум
steelcraft
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

Ошибка №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
Комментарии
  1. Старый комментарий
    Раз это самая интересная тема- значит он ничем не отличается от других языков. И то-есть вот эта все на несколько постов - ни о чем. Хотя - не уверен, сейчас спрошу, вечером отпишусь.
    Запись от Hrethgir размещена 27.06.2024 в 05:32 Hrethgir вне форума
  2. Старый комментарий
    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 politoto вне форума
  3. Старый комментарий
    Аватар для sporta1982
    Цитата Сообщение от politoto
    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;
    }
    и как же к этому указателю что-нибудь прибавить?
    Запись от sporta1982 размещена 27.06.2024 в 12:03 sporta1982 вне форума
  4. Старый комментарий
    Цитата Сообщение от 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;
    }
    Результат:
    0x7ffe2f0615fc 0x7ffe2f061601


    ** Process exited - Return Code: 0 **
    Или я неправильно понял вопрос?
    Запись от steelcraft размещена 27.06.2024 в 13:09 steelcraft вне форума
  5. Старый комментарий
    Какой размер 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 politoto вне форума
  6. Старый комментарий
    Аватар для sporta1982
    Ошибка C2036 void *: неизвестный размер
    Запись от sporta1982 размещена 27.06.2024 в 13:32 sporta1982 вне форума
  7. Старый комментарий
    Цитата Сообщение от steelcraft
    0x7ffe2f0615fc 0x7ffe2f061601
    В вашем примере значение увеличилось на 5, а должно вроде как умножаться на размер типа. Указатель же указывает на тип int (по википедии 2 байта)?
    Запись от testuser2 размещена 27.06.2024 в 13:33 testuser2 на форуме
  8. Старый комментарий
    Аватар для sporta1982
    если int поставить то работает, ну и увеличивается размер int * 5
    Запись от sporta1982 размещена 27.06.2024 в 13:34 sporta1982 вне форума
  9. Старый комментарий
    Цитата Сообщение от politoto
    Какой размер void?
    Windows Batch file
    1
    
    cl /c /nologo void.c
    Code
    1
    2
    
    void.c
    void.c(7): error C2036: void *: неизвестный размер
    Размер void не определен:

    error C2036: 'void *' : unknown size

    Разработчики разных компиляторов трактуют по-разному, и все в чем-то правы. Стандарт пока молчит.
    Запись от steelcraft размещена 27.06.2024 в 13:37 steelcraft вне форума
  10. Старый комментарий
    Цитата Сообщение от testuser2
    В вашем примере значение увеличилось на 5, а должно вроде как умножаться на размер типа.
    Я использовал онлайн-компилятор (явно не оговаривается, но похож на GCC), он считает, что размер void - единица.
    C
    1
    2
    3
    4
    5
    6
    
    #include <stdio.h>
     
    int
    main(){
        printf( "%ld %ld\n", sizeof(void), sizeof(void *));
    }
    Результат:
    1 8


    ** Process exited - Return Code: 0 **
    Цитата Сообщение от testuser2
    Указатель же указывает на тип int (по википедии 2 байта)?
    На самом деле на void. (Ну и попутно int формально может быть любого размера с некоторыми ограничениями, фактически обычно 2 или 4 байта).
    Запись от steelcraft размещена 27.06.2024 в 13:41 steelcraft вне форума
  11. Старый комментарий
    Вообще, конечно, странно, что такой тип можно задавать переменной, void, это же вроде "пустота", интересно какая в этом заложена философия )
    Запись от testuser2 размещена 27.06.2024 в 13:50 testuser2 на форуме
  12. Старый комментарий
    Аватар для sporta1982
    void* может быть все таки адрес? Тем более видно что у вас получается x64 приложение.
    Запись от sporta1982 размещена 27.06.2024 в 13:57 sporta1982 вне форума
  13. Старый комментарий
    Цитата Сообщение от steelcraft
    Размер void не определен:
    Стандарт пока молчит.
    Да кто его слушает, этот стандарт?!
    Текущий черновик c++ не молчит:
    https://eel.is/c++draft/expr.add#1
    Цитата Сообщение от 7.6.6 Additive operators
    For addition, either both operands shall have arithmetic type, or one operand shall be a pointer to a completely-defined object type and the other shall have integral type.
    https://eel.is/c++draft/basic.fundamental#15
    Цитата Сообщение от 6.8.2 Fundamental types
    A type cv void is an incomplete type that cannot be completed; such a type has an empty set of values.
    Сравните с аналогичными местами т.н. стандартов Си.
    Запись от politoto размещена 27.06.2024 в 14:11 politoto вне форума
  14. Старый комментарий
    Цитата Сообщение от testuser2
    Вообще, конечно, странно, что такой тип можно задавать переменной, void, это же вроде "пустота", интересно какая в этом заложена философия )
    Нет, переменной нельзя:
    C
    1
    2
    3
    4
    5
    
    int main()
    {
        void v;
        return 0;
    }
    main.c: In function 'main':
    main.c:8:10: error: variable or field 'v' declared void
    8 | void v;
    | ^


    ** Process exited - Return Code: 1 **
    Можно только указывать на тип void:
    C
    1
    2
    3
    4
    5
    
    int main()
    {
        void *pv;
        return 0;
    }


    ** Process exited - Return Code: 0 **
    Запись от steelcraft размещена 27.06.2024 в 14:13 steelcraft вне форума
  15. Старый комментарий
    Цитата Сообщение от sporta1982
    void* может быть все таки адрес? Тем более видно что у вас получается x64 приложение.
    Не совсем. Хотя после приведения к числовому представлению выглядит очень похоже.
    Запись от steelcraft размещена 27.06.2024 в 14:18 steelcraft вне форума
  16. Старый комментарий
    Цитата Сообщение от testuser2
    странно, что такой тип можно задавать переменной, void
    Вообще, конечно, нельзя.
    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 politoto вне форума
  17. Старый комментарий
    Цитата Сообщение от politoto
    Текущий черновик c++ не молчит
    Во-первых, черновик; во-вторых, C++. Для компилятора C, увы, не указ. Поэтому компиляторы GCC и MS сейчас дают такой разнобой.

    Цитата Сообщение от politoto
    Сравните с аналогичными местами т.н. стандартов Си.
    В действующем не увидел. Может, в будущем определят.
    Запись от steelcraft размещена 27.06.2024 в 14:24 steelcraft вне форума
  18. Старый комментарий
    Цитата Сообщение от steelcraft
    Во-первых, черновик; Поэтому компиляторы GCC и MS сейчас дают такой разнобой.
    А может быть, потому, что это одно из многочисленных полувеликих extension gcc?

    https://gcc.gnu.org/onlinedocs... Arith.html
    Цитата Сообщение от GNU C Manual
    6.24 Arithmetic on void- and Function-Pointers

    In GNU C, addition and subtraction operations are supported on pointers to void and on pointers to functions. This is done by treating the size of a void or of a function as 1.

    A consequence of this is that sizeof is also allowed on void and on function types, and returns 1.

    The option -Wpointer-arith requests a warning if these extensions are used.
    Запись от politoto размещена 27.06.2024 в 14:51 politoto вне форума
  19. Старый комментарий
    Цитата Сообщение от politoto
    А может быть, потому, что это одно из многочисленных полувеликих extension gcc?
    Так если бы соответствовало стандарту, не было бы расширением по определению. Расширение - это всегда художественная самодеятельность.
    Запись от steelcraft размещена 27.06.2024 в 15:04 steelcraft вне форума
  20. Старый комментарий
    Аватар для CoderHuligan
    И это вполне логично, так как в большинстве широко-распространенных архитектур размер байта является единицей адресации. То есть по умолчанию безтиповый поинтер имеет тип кратный байту. GCC молодец в этом смысле. пОЧЕМУ КОМИТЕТ В УПОР НЕ ЗАМЕЧАЕТ, ЧТО СИ ДАВНО ПЕРЕРОС СВОИ ДЕТСКИЕ ШТАНИШКИ..
    Запись от CoderHuligan размещена 27.06.2024 в 15:06 CoderHuligan вне форума
 
Новые блоги и статьи
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
Перемещение строк из ТЧ в другой документ с учетом текущего пробега
Maks 17.08.2026
Реализация из решения ниже выполнена на примере нетипового документа "Автозапчасти", с ТЧ "Шины". За основу взят алгоритм отсюда: https:/ / www. cyberforum. ru/ blogs/ 359708/ 10838. html Задача: . . .
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru