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

Ошибка №7. Операции над указателями. Арифметика.

Запись от steelcraft размещена 27.06.2024 в 01:46
Показов 5332 Комментарии 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 вне форума
 
Новые блоги и статьи
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
[EasyBuilder Pro] Памятка по разработке для панелей Weintek
ФедосеевПавел 26.08.2026
Памятка по разработке для панелей Weintek ВВЕДЕНИЕ Ранее, при реализации проектов основное внимание уделял разработке управляющей программы для контроллера, а панели оператора доставалось время. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru