Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Общие вопросы > Инкрементация указателя |
Автор: AlanG 15.5.2006, 09:14 | ||
Всем доброго времени суток. Возникли непонятки с указателями. К примеру есть указатель ссылающийся на определенный адрес, если его инкрементиролвать или декрементировать, то адрес изменяется на 2 значения, но только если тип данных занимает 2 байта. Вопрос: в чем суть таких действий, какая связь между длиной в байтах и адресом ячейки?
|
Автор: Daevaorn 15.5.2006, 09:29 |
AlanG, В твоём случае значит sizeof(int)==2, а поскольку p у тебя указатель именно на int, то и ++p и --p будет добавлять и убавлять от адреса 2. Вот если указатель был бы void*, то его нельзя ++/--, т.к. не определен тип. Арифметика указателей очень удобна при реализации сложных алгоритмов работы с памятью. |
Автор: AlanG 15.5.2006, 10:22 | ||
Да это я знаю ![]() |
Автор: likehood 15.5.2006, 10:46 |
удобно работать с массивами: ++p переходит к следующему элементу массива. |
Автор: LuckLess 15.5.2006, 10:51 |
++ и -- заставляют указатель указывать на следующий(++) или предыдущий(--) элемент ТАКОГО же типа, что и элемент на который он указывает. логично предположить, что раз элемент занимает 2 байта, то следующий элемент будет через 2 байта после этого |
Автор: MAKCim 15.5.2006, 16:11 | ||
если
то ++p эквивалентно p+=sizeof(int); --p ... p-=sizeof(int) p+i ... p+i*sizeof(int) p-i ... p-i*sizeof(int) ... |
Автор: bsa 15.5.2006, 19:49 | ||||||
++p эквивалентно p += 1!!! Не надо путать!
|
Автор: MAKCim 15.5.2006, 21:12 | ||
ну да - sizeof неявно добавляется спасибо что заметил |
Автор: AlanG 16.5.2006, 08:56 |
Народ ![]() Я, если чесно совсем не разбираюсь в строении ячеек памяти. Я знаю что такое байт, но я не знаю как он храница в ячейках. На данный момент в голове складывается следующая картина: Одна ячейка хранит в себе 1 байт. Если да, то инкремент адреса увеличивая на еденицу, прибавляет 2 байта, если тип данных имеет размер 2 байта. Получается что, инкрементируя адрес который несет в себе 1 байт, невольно перемещается на 2 байта, т.е. на две яцейка памяти? |
Автор: UnrealMan 16.5.2006, 09:21 |
Адресация памяти побайтовая. Запись p = p+1 для указателя будет делать примерно то же, что и следующая: p = (T *)((char *)p+sizeof(T)); если p имеет тип T * Если тип int занимает 4 байта, то это значит, что под каждую переменную типа int будет отводиться чётверка байт. Записав p++; мы увеличиваем p таким образом, что он указывает теперь на начало следующей четвёрки байт, составляющей единое целое значение типа int. |
Автор: likehood 16.5.2006, 09:38 | ||
надо определится, что называть ячейкой памяти. Чтобы не путать байты с ячейками будем считать, что ячейка - это область памяти, где храниться конкретная переменная. Для типа long ячейка состоит из четырех байт, идущих в памяти друг за другом. При этом адресом переменной (ячейки) называют адрес самого младшего ее байта (т.е. имеющего наименьший адрес). Выглядит это так:
если long *p указывает на 1-ю ячейку, то ++p увеличится на 4 байта и будет указывать на следующую ячейку. Такое поведение указателей сильно упрощает обработку массивов данных, но если нужно сдвинуть указатель только на один байт, надо сделать приведение к типу (byte*). |
Автор: AlanG 16.5.2006, 11:40 | ||||
Тогда лоника мне совсем не понятна. Ведь если переменная храница в одной ячейке, и если к примеру ячейка хранит в себе тип int размером в 2 байта, то зачем перескакивать через одну ячейку? Ведь мы инкрементируем адрес (ячейку).
P.S. Про байты и размеры байтов мне все понятно, пока не могу вкурить логику и само строение ячеек. |
Автор: threef 16.5.2006, 14:13 |
Тогда так адрес байт(биты) переменные массив 0x00000000 76543210 long z; int x[4], x[0] 0x00000001 76543210 0x00000002 76543210 x[1] 0x00000003 76543210 0x00000004 76543210 long y; x[2] 0x00000005 76543210 0x00000006 76543210 x[3] 0x00000007 76543210 если у тебя переменная типа long находится по адресу 0x00000000 и занимает 4 байта, то адрес следующей - 0x00000004. &z+1 == &y Если размер int 2 байта и у тебя массив int , то каждый следующий элемент находится через 2 байта &x[0]+1==&x[1] &x[0]+2==&x[2] и т.д. В С /C++ адресная арифметика выполняется для типизированных указателей, бывает только целочисленной и прибавление целого числа к указателю дает новый адрес, смещенный на число * размер типа |
Автор: bsa 16.5.2006, 16:03 | ||||||
baronp, про тип long ты не совсем прав. У меня он занимает в памяти 8 байт (у меня и машина 64-х битная, и ОС). А вот int у всех машин с разрядностью не менее 32-х бит - 4 байта. AlanG Ты сам подумай, что легче написать: a += sizeof(*a) или a += 1? Естественно второй вариант. Случаи, когда нужно методом изменения указателя получить доступ к каждой ячейке очень редки и для этого используются всякие ухищрения, например p = (char*)a + 1, здесь p - это char*. Кстати, разница типизированных указателей равна расстоянию в элементах, а не байтах:
Результат будет 10. Это очень хорошая особенность C/C++. Без нее, было бы намного сложнее писать программы, оперирующие массивами структур. Да и они были бы менее читабельными. Но, повторяю, это особенность C/C++. Машинный код, который генерируется компилятором, прибавляет уже нужное количество байт:
Будет транслированно во что-то типа:
Здесь [bp+08h] - это адрес переменной a в стеке приложения, а 028h - это 40 в десятичной системе счисления. Если ассемблер для тебя не очень понятен, то объясню проще - работу по умножению 10 на размер элемента (т.е. 4) возьмет на себя компилятор. И процессор уже выполнить увеличение на 40, а не на 10. Но с точки зрения человека, запись 10 легче воспринимается, чем 40 - не надо делить, чтобы узнать, на какой элемент массива будет указывать указатель после выполнения данной операции. |
Автор: AlanG 16.5.2006, 17:31 |
Ребят, здорого расписали, спасибо ![]() (Жаль немогу репутацию повысить, но попрошу модераторов ![]() И еще мне кажется что пора книгу менять по C++. Что посоветуйте? |
Автор: MAKCim 16.5.2006, 18:20 | ||
Классику: Б. Страуструп (Язык программирования C++) |
Автор: bsa 16.5.2006, 19:14 |
AlanG, посоветую заглянуть в заголовок раздела - там есть соответствующая тема. |
Автор: likehood 16.5.2006, 19:57 |
а что у тебя за ОС (сорри за офтоп) |
Автор: bsa 17.5.2006, 15:28 |
baronp Gentoo Linux b-s-a 2.6.16-gentoo-r7 #1 PREEMPT Thu May 11 15:00:17 MSD 2006 x86_64 AMD Athlon 64 Processor 3000+ GNU/Linux |
Автор: Xenon 17.5.2006, 21:41 |
Мне ООП от Лафоре (издательство Питер) нравится ![]() |
Автор: Чемодан 10.7.2011, 02:39 | ||
Можно я тут вопрос по теме задам... Небольшой пример:
работает, а если *p_counter += 1; поменять на *p_counter++; то не работает. Я ведь получается указываю ему увеличить counter на 1 по адресу, а он так на нуле и остается. Это нормально? ![]() |
Автор: bsa 10.7.2011, 18:11 | ||
Чемодан, лучше создавай отдельную тему. Проблема у тебя в том, что ты не знаешь приоритеты операций. Так вот, у тебя сначала выполняется постфиксный инкремент p_counter, а уже затем разыменовывается старое его значение. Фактически делается это:
|
Автор: Чемодан 11.7.2011, 01:41 |
Да почему же не знаю, пост и пре, до и после, ***++/++***. Думаю, тебе известно, что порой забываешь про элементарные вещи или просто на них не обращаешь внимания, а потом ломаешь голову - "почему же не так". Я вообще "под шумок" вопрос задал, думал, может что-то не так. Оказалось всё так. В моём случае просто не *p_counter++ а ++*p_counter; Спасибо ![]() |