Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Инкрементация указателя


Автор: AlanG 15.5.2006, 09:14
Всем доброго времени суток.
Возникли непонятки с указателями. К примеру есть указатель ссылающийся на определенный адрес, если его инкрементиролвать или декрементировать, то адрес изменяется на 2 значения, но только если тип данных занимает 2 байта. Вопрос: в чем суть таких действий, какая связь между длиной в байтах и адресом ячейки? 
Код

int *p, h=100;
p=&h;   //к примеру адрес =2000
p++;     //адрес станет = 2002


 

Автор: Daevaorn 15.5.2006, 09:29
AlanG,
В твоём случае значит sizeof(int)==2, а поскольку p у тебя указатель именно на int, то и ++p и --p будет добавлять и убавлять от адреса 2. Вот если указатель был бы void*, то его нельзя ++/--, т.к. не определен тип. Арифметика указателей очень удобна при реализации сложных алгоритмов работы с памятью. 

Автор: AlanG 15.5.2006, 10:22
Цитата

В твоём случае значит sizeof(int)==2, а поскольку p у тебя указатель именно на int, то и ++p и --p будет добавлять и убавлять от адреса 2. Вот если указатель был бы void*, то его нельзя ++/--, т.к. не определен тип. Арифметика указателей очень удобна при реализации сложных алгоритмов работы с памятью.  

Да это я знаю smile , мне бы все о связь между длиной в байтах и адресом ячейки? 
 

Автор: likehood 15.5.2006, 10:46
удобно работать с массивами: ++p переходит к следующему элементу массива. 

Автор: LuckLess 15.5.2006, 10:51
++ и -- заставляют указатель указывать на следующий(++) или предыдущий(--) элемент ТАКОГО же типа, что и элемент на который он указывает.
логично предположить, что раз элемент занимает 2 байта, то следующий элемент будет через 2 байта после этого 

Автор: MAKCim 15.5.2006, 16:11
если
Код

int* p;

то
++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
Цитата(MAKCim @ 15.5.2006,  16:11)
если
Код

int* p;

то
++p эквивалентно p+=sizeof(int);
--p ... p-=sizeof(int)
p+i ... p+i*sizeof(int)
p-i ... p-i*sizeof(int)
...

++p эквивалентно p += 1!!!
Не надо путать!
Код
char * pc;
int * &pi = (int*)pc;
...
++pi; // эквивалентно: pc += sizeof(int)
 

Автор: MAKCim 15.5.2006, 21:12
Цитата

++p эквивалентно p += 1!!!

ну да - sizeof неявно добавляется
спасибо что заметил 

Автор: AlanG 16.5.2006, 08:56
Народ smile,  давайте разберем строение байта и разряды. Как вообще устроены разряды и байты? 
Я, если чесно совсем не разбираюсь в строении ячеек памяти. Я знаю что такое байт, но я не знаю как он храница в ячейках. На данный момент в голове  складывается следующая картина:
Одна ячейка хранит в себе 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 ячейка состоит из четырех байт, идущих в памяти друг за другом. При этом адресом переменной (ячейки) называют адрес самого младшего ее байта (т.е. имеющего наименьший адрес). Выглядит это так:
Код

| байт1 | байт2 | байт3 | байт4 | байт5 | байт 6 |...
..^<---указатель на 1-ю ячейку   ^<---а здесь начинается следующая ячейка

если long *p указывает на 1-ю ячейку, то ++p увеличится на 4 байта и будет указывать на следующую ячейку. Такое поведение указателей сильно упрощает обработку массивов данных, но если нужно сдвинуть указатель только на один байт, надо сделать приведение к типу (byte*). 

Автор: AlanG 16.5.2006, 11:40
Цитата

Чтобы не путать байты с ячейками будем считать, что ячейка - это область памяти, где храниться конкретная переменная. 

Тогда лоника мне совсем не понятна. Ведь если переменная храница в одной ячейке, и если к примеру ячейка хранит в себе тип int  размером в 2 байта, то зачем перескакивать через одну ячейку? Ведь мы инкрементируем адрес (ячейку).

Код

int *p, h=100; //Адрес ячейки переменной h = 2000, переменная типа int имеет длину в 2 байта
p=&h;   //адрес ячейки =2000
p++;     //адрес станет = 2002

 
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*. Кстати, разница типизированных указателей равна расстоянию в элементах, а не байтах:
Код
int a[100];
int *p = & a[10];     // эту строку можно записать по-другому: int *p = a + 10;
cout << ( p - a ) << endl;

Результат будет 10. Это очень хорошая особенность C/C++. Без нее, было бы намного сложнее писать программы, оперирующие массивами структур. Да и они были бы менее читабельными.
Но, повторяю, это особенность C/C++. Машинный код, который генерируется компилятором, прибавляет уже нужное количество байт:
Код
int *a;
...
a += 10;

Будет транслированно во что-то типа:
Код
add  [ebp+08h],028h

Здесь [bp+08h] - это адрес переменной a в стеке приложения, а 028h - это 40 в десятичной системе счисления.
Если ассемблер для тебя не очень понятен, то объясню проще - работу по умножению 10 на размер элемента (т.е. 4) возьмет на себя компилятор. И процессор уже выполнить увеличение на 40, а не на 10. Но с точки зрения человека, запись 10 легче воспринимается, чем 40 - не надо делить, чтобы узнать, на какой элемент массива будет указывать указатель после выполнения данной операции.
 

Автор: AlanG 16.5.2006, 17:31
Ребят, здорого расписали, спасибо smile 
(Жаль немогу репутацию повысить, но попрошу модераторов smile )
И еще мне кажется что пора книгу менять по C++.
Что посоветуйте? 

Автор: MAKCim 16.5.2006, 18:20
Цитата

Что посоветуйте?  

Классику: Б. Страуструп (Язык программирования C++) 

Автор: bsa 16.5.2006, 19:14
AlanG, посоветую заглянуть в заголовок раздела - там есть соответствующая тема. 

Автор: likehood 16.5.2006, 19:57
Цитата(bsa @  16.5.2006,  17:03 Найти цитируемый пост)
у меня и машина 64-х битная, и ОС

а что у тебя за ОС (сорри за офтоп) 

Автор: 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
Мне ООП от Лафоре (издательство Питер) нравится smile 

Автор: Чемодан 10.7.2011, 02:39
Можно я тут вопрос по теме задам...
Небольшой пример:

Код

int main() {
    int counter = 0, *p_counter = &counter;

    for (int i = 0; i++ != 10;)
        *p_counter += 1;

    cout << *p_counter << endl;

    return 0;
}

работает, а если *p_counter += 1; поменять на *p_counter++; то не работает.
Я ведь получается указываю ему увеличить counter на 1 по адресу, а он так на нуле и остается.
Это нормально? smile

Автор: bsa 10.7.2011, 18:11
Чемодан, лучше создавай отдельную тему.
Проблема у тебя в том, что ты не знаешь приоритеты операций. Так вот, у тебя сначала выполняется постфиксный инкремент p_counter, а уже затем разыменовывается старое его значение. Фактически делается это:
Код
int *p_tmp = p_counter;
++p_counter;
*p_tmp += 1;

Автор: Чемодан 11.7.2011, 01:41
Да почему же не знаю, пост и пре, до и после, ***++/++***. Думаю, тебе известно, что порой забываешь про элементарные
вещи или просто на них не обращаешь внимания, а потом ломаешь голову - "почему же не так".
Я вообще "под шумок" вопрос задал, думал, может что-то не так. Оказалось всё так. В моём случае просто не *p_counter++ а ++*p_counter;
Спасибо smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)