7 ответов:
сначала Бинки видео может помочь. Это хорошее видео о указателях. Для арифметики, вот пример:
int * pa = NULL; int * pb = NULL; pa += 1; // pa++. behind the scenes, add sizeof(int) bytes assert((pa - pb) == 1); print_out(pa); // possibly outputs 0x4 print_out(pb); // possibly outputs 0x0 (if NULL is actually bit-wise 0x0)(обратите внимание, что увеличение указателя, который содержит значение null указатель строго является неопределенным поведением. Мы использовали NULL, потому что нас интересовало только значение указателя. Обычно используется только инкремент / декремент при указании на элементы массива).
ниже приведены два важных понятия
- сложение / вычитание целого числа в указатель означает перемещение указателя вперед / назад на N элементов. Поэтому, если int имеет размер 4 байта, pa может содержать 0x4 на нашей платформе после увеличения на 1.
- вычитание указателя другим указателем означает получение их расстояния, измеренного элементами. Таким образом, вычитание pb из pa даст 1, так как они имеют одно расстояние элемента.
на практическом примере. Предположим вам напишите функцию, и люди предоставят вам указатель начала и конца (очень распространенная вещь в C++):
void mutate_them(int *begin, int *end) { // get the amount of elements ptrdiff_t n = end - begin; // allocate space for n elements to do something... // then iterate. increment begin until it hits end while(begin != end) { // do something begin++; } }
ptrdiff_tэто то, что является типом (конец - начало). Это может быть синонимом "int" для некоторого компилятора, но может быть другим типом для другого. Никто не может знать, поэтому он выбирает общий typedefptrdiff_t.
вот где я узнал указатели:http://www.cplusplus.com/doc/tutorial/pointers.html
как только вы поймете, указатели, указатель арифметика проста. Единственное различие между ним и обычной арифметикой заключается в том, что число, которое вы добавляете к указателю, будет умножено на размер типа, на который указывает указатель. Например, если у вас есть указатель на
intиint' s размер составляет 4 байта,(pointer_to_int + 4)будет вычислять адрес памяти 16 байты (4 дюйма) вперед.поэтому, когда вы пишите
(a_pointer + a_number)в арифметике с указателями, что на самом деле
(a_pointer + (a_number * sizeof(*a_pointer)))в обычная арифметика.
применяя НЛП, назовем это адресной арифметикой. "указатели" боятся и неправильно понимают в основном потому, что их учат не те люди и/или на неправильной стадии с неправильными примерами неправильным образом. Неудивительно, что никто не "получает" его.
при обучении указателям, факультет продолжает о "p-указатель на a, значение p-адрес a" и так далее. это просто не сработает. вот сырье для вас, чтобы построить С. практика с ним и ваши студенты получат оно.
'int a', a-целое число, оно хранит целочисленные значения типа. типа 'int* P' и P является 'инт' звезды, он хранит значения типа int звезды типа.
' a '- это то, как вы получаете целое число 'what', хранящееся в a (старайтесь не использовать 'значение a') "&a "- это то, как вы получаете " где "сам a хранится (попробуйте сказать "адрес")
'b = a' для того, чтобы это работало, обе стороны должны быть одного типа. если a-int, то b должен быть способен хранить int. (Итак _ _ _ _ _ _ B, пробел заполняется 'int')
'p = &a' для того, чтобы это работало, обе стороны должны быть одного типа. если a-целое число, &a-адрес, p должен быть способен хранить адреса целых чисел. (так _ _ _ _ _ _ p, пустое место заполняется 'int *')
теперь напишите int * p по-другому, чтобы вывести информацию о типе:
int* / p
Что такое 'p'? ответ: это 'int *'. так что 'p' - это адрес целого числа.
int / * p
Что такое '* p'? ответ: это это типа 'int'. так что '* p ' - это целое число.
теперь перейдем к адресной арифметике:
int a; a=1; a=a+1;
что мы делаем в 'A=a+1'? думайте об этом как о "следующем". Поскольку А-это число, это все равно что сказать "следующее число". Так как A держит 1, говоря "следующий" сделает его 2.
// ошибочный пример. вы были предупреждены!!! int *p int a; p = &a; p=p+1;
что мы делаем в 'p=p+1'? он все еще говорит "следующий". На этот раз, не количество но это адрес. Так что мы говорим "следующий адрес". Следующий адрес зависит от типа данных, более конкретно от размера типа данных.
функции printf("%д %д %д", оператор sizeof(тип char), как sizeof(тип int), оператор sizeof(поплавок));
поэтому "далее" для адреса будет двигаться вперед sizeof(тип данных).
это работает для меня и всех людей, которых я учил.
Я считаю хорошим примером арифметики указателя следующую функцию длины строки:
int length(char *s) { char *str = s; while(*str++); return str - s; }
Итак, главное помнить, что указатель - это просто переменная размером со Слово, которая вводится для разыменования. Это означает, что будь то void*, int*, long long**, это все еще просто переменная размера слова. Разница между этими типами заключается в том, что компилятор считает разыменованный тип. Просто чтобы уточнить, размер слова означает ширину виртуального адреса. Если вы не знаете, что это значит, просто помните, что на 64-разрядной машине указатели составляют 8 байт, а на 32-разрядной машине указатели это 4 байта. Понятие адреса очень важно для понимания указателей. Адрес-это число, способное однозначно идентифицировать определенное место в памяти. Все в памяти имеет адрес. Для наших целей мы можем сказать, что каждая переменная имеет адрес. Это не обязательно всегда верно, но компилятор позволяет нам предположить это. Сам адрес является байтовым гранулированным, то есть 0x0000000 указывает начало памяти, а 0x00000001-один байт в памяти. Это означает, что добавляя один к указателю, мы перемещаем один байт вперед в память. Теперь давайте возьмем массивы. Если вы создадите массив типа quux с 32 элементами big, он будет охватывать от начала его выделения до начала его выделения плюс 32*sizeof(quux), поскольку каждая ячейка массива является sizeof(quux) big. Итак, действительно, когда мы указываем элемент массива с массивом[n], это просто синтаксический сахар (стенография) для *(array+sizeof(quux)*n). Арифметика указателя действительно просто меняет адрес, на который вы ссылаетесь, поэтому мы можем реализовать strlen с
while(*n++ != ''){ len++; }поскольку мы просто сканируем вдоль, байт за байтом, пока мы не достигнем нуля. Надеюсь, это поможет!
есть несколько способов решения.
интуитивный подход, о котором думает большинство программистов на C/C++, заключается в том, что указатели являются адресами памяти. пример litb использует этот подход. Если у вас есть нулевой указатель (который на большинстве машин соответствует адресу 0), и вы добавляете размер int, вы получаете адрес 4. Это означает, что указатели в основном являются просто причудливыми целыми числами.
к сожалению, есть несколько проблем с этим. Начнем с того, что он может не работать. Нулевой указатель не гарантирует фактического использования адреса 0. (Хотя присвоение константы 0 дает нулевой указатель на указатель).
кроме того, вы не можете увеличить нулевой указатель, или в более общем случае указатель всегда должен указывать на выделенную память (или один элемент прошлого) или специальную константу нулевого указателя 0.
таким образом, более правильный способ мышления заключается в том, что указатели-это просто итераторы, позволяющие вам перебирать выделенные память. Это действительно одна из ключевых идей, лежащих в основе итераторов STL. Они моделируются так, чтобы вести себя очень похоже на указатели и предоставлять специализации, которые исправляют необработанные указатели для работы в качестве правильных итераторов.
более подробное объяснение этому дано здесь, например.
но это последнее представление означает, что вы действительно должны объяснить итераторы STL, а затем просто сказать, что указатели являются частным случаем этих. Вы можете увеличить указатель до точки к следующему элементу в буфере, так же, как вы можете a
std::vector<int>::iterator. Он может указывать на один элемент за концом массива, как и конечный итератор в любом другом контейнере. Вы можете вычесть два указателя эта точка в том же буфере чтобы получить количество элементов между ними, так же, как вы можете с итераторами, и так же, как с итераторами, если указатели указывают на отдельные буферы, вы можете не реально сравнить их. (Для практического примера почему бы и нет, рассмотрим, что происходит в сегментированной памяти. Каково расстояние между двумя указателями, указывающими на отдельные сегменты?)конечно, на практике существует очень тесная корреляция между адресами ЦП и указателями C/C++. Но они не ровно то же самое. Указатели имеют несколько ограничений, которые не могут быть строго необходимы на вашем процессоре.
Конечно, большинство программистов на C++ путаются в первом понимании, хотя это технически неправильный. Это, как правило, достаточно близко к тому, как ваш код в конечном итоге ведет себя, что люди думают, что они получают его, и двигаться дальше.
но для кого-то, кто приходит с Java, и просто изучает указатели с нуля, последнее объяснение может быть так же легко понято, и позже на них будет меньше сюрпризов.
Это один довольно хорошо по ссылке здесь об арифметике указателя
например:
указатель и массив
формула для вычисления адреса ptr + i, где ptr имеет тип T *. тогда формула для адреса:
адрес( ПТР + я ) = АДР( ПТР ) + [ оператор sizeof( Т ) * Я ]
для типа int на 32-битной платформе, addr (ptr+i) = addr (ptr)+4 * i;
вычитание
мы также можем рассчитать ПТР - я. Например, предположим, что у нас есть массив называется обр. int arr[ 10 ] ; int * p1, * p2 ;
p1 = arr + 3 ; // p1 == & arr[ 3 ] p2 = p1 - 2 ; // p1 == & arr[ 1 ]
Comments