//' OOOO oooo that smell!! Can't you smell that smell!??!??!!!!11!??/!!!!!1!!!!!!1
If Not Me.CurrentMenuItem.Parent Is Nothing Then
For Each childMenuItem As MenuItem In aMenuItem.Children
do something
Next
If Not Me.CurrentMenuItem.Parent.Parent Is Nothing Then
//'item is at least a grand child
For Each childMenuItem As MenuItem In aMenuItem.Children
For Each grandchildMenuItem As MenuItem In childMenuItem.Children
do something
Next
Next
If Not Me.CurrentMenuItem.Parent.Parent.Parent Is Nothing Then
//'item is at least a grand grand child
For Each childMenuItem As MenuItem In aMenuItem.Children
For Each grandchildMenuItem As MenuItem In childMenuItem.Children
For Each grandgrandchildMenuItem As MenuItem In grandchildMenuItem.Children
do something
Next
Next
Next
End If
End If
End If
I can't give you concrete examples of all of these, but I'll do my best.
sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *)
I don't know of any systems where I know this to be false, but consider:
Mobile devices often have some amount of read-only memory in which program code and such is stored. Read-only values (const variables) may conceivably be stored in read-only memory. And since the ROM address space may be smaller than the normal RAM address space, the pointer size may be different as well. Likewise, pointers to functions may have a different size, as they may point to this read-only memory into which the program is loaded, and which can otherwise not be modified (so your data can't be stored in it).
So I don't know of any platforms on which I've observed that the above doesn't hold, but I can imagine systems where it might be the case.
The in-memory representation of all pointers for a given architecture is the same regardless of the data type pointed to.
Think of member pointers vs regular pointers. They don't have the same representation (or size). A member pointer consists of a this
pointer and an offset.
And as above, it is conceivable that some CPU's would load constant data into a separate area of memory, which used a separate pointer format.
The in-memory representation of a pointer is the same as an integer of the same bit length as the architecture.
Depends on how that bit length is defined. :)
int
на многих 64-битных платформах по-прежнему 32-битный. Но указатель 64 бита.
As already said, CPU's with a segmented memory model will have pointers consisting of a pair of numbers. Likewise, member pointers consist of a pair of numbers.
Multiplication and division of pointer data types are only forbidden by the compiler.
Ultimately, pointers data types only exist in the compiler. What the CPU works with is not pointers, but integers and memory addresses. So there is nowhere else where these operations on pointer types could be forbidden. You might as well ask for the CPU to forbid concatenation of C++ string objects. It can't do that because the C++ string type only exists in the C++ language, not in the generated machine code.
However, to answer what you mean, look up the Motorola 68000 CPUs. I believe they have separate registers for integers and memory addresses. Which means that they can easily forbid such nonsensical operations.
All pointer values can be casted to a single integer.
You're safe there. The C and C++ standards guarantee that this is always possible, no matter the memory space layout, CPU architecture and anything else. Specifically, they guarantee an implementation-defined mapping. In other words, you can always convert a pointer to an integer, and then convert that integer back to get the original pointer. But the C/C++ languages say nothing about what the intermediate integer value should be. That is up to the individual compiler, and the hardware it targets.
Incrementing a pointer is equivalent to adding sizeof(the pointed data type) to the memory address stored by the pointer.
Again, this is guaranteed. If you consider that conceptually, a pointer does not point to an address, it points to an object, then this makes perfect sense. Adding one to the pointer will then obviously make it point to the next object. If an object is 20 bytes long, then incrementing the pointer will move it 20 bytes, so that it moves to the next object.
If a pointer was merely a memory address in a linear address space, if it was basically an integer, then incrementing it would add 1 to the address -- that is, it would move to the next byte.
Finally, as I mentioned in a comment to your question, keep in mind that C++ is just a language. It doesn't care which architecture it is compiled to. Many of these limitations may seem obscure on modern CPU's. But what if you're targeting yesteryear's CPU's? What if you're targeting the next decade's CPU's? You don't even know how they'll work, so you can't assume much about them. What if you're targeting a virtual machine? Compilers already exist which generate bytecode for Flash, ready to run from a website. What if you want to compile your C++ to Python source code?
Staying within the rules specified in the standard guarantees that your code will work in all these cases.
Для 6 .: указатель не обязательно является адресом памяти. См., Например, « The Great Pointer Conspiracy » пользователя Stack Overflow jalf :
Да, я использовал слово «адрес» в комментарии выше. Важно понимать, что я имею в виду под этим. Я не имею в виду «адрес памяти, по которому физически хранятся данные», а просто абстрактное «все, что нам нужно, чтобы найти значение. Адрес i может быть любым, но как только он у нас есть, мы всегда можем найти и изменить i. "
И:
Указатель не является адресом памяти! Я упоминал об этом выше, но давайте повторим еще раз. Указатели обычно реализуются компилятором просто как адреса памяти, да, но это не обязательно. "
Дополнительная информация об указателях из стандарта C99:
void *
и char *
имеют идентичные представления, то есть их можно использовать взаимозаменяемо. без преобразования, т. е. тот же адрес обозначается одним и тем же битовым шаблоном (что не обязательно должно быть истинным для других типов указателей) void *
и обратно, но по-прежнему действительны; сюда не входят указатели на функции! void *
может быть преобразовано в (и из) целые числа, а 7.18.1.4 §1 предоставляет подходящие типы intptr_t
и uintptr_t
; эта проблема: эти типы являются необязательными - в стандарте явно указывается, что целочисленный тип не обязательно должен быть достаточно большим, чтобы фактически содержать значение указателя! Я не Насчет остальных не знаю, но для DOS предположение № 3 неверно. DOS является 16-битной и использует различные приемы для отображения памяти, объем которой превышает 16 бит.
Представление указателя в памяти такое же, как целое число той же длины в битах, что и архитектура.
Я думаю, что это предположение неверно, потому что на 80186, например, 32-битный указатель хранится в двух регистрах (регистр смещения и регистр сегмента), и какая полуслова идет в какой регистр имеет значение во время доступа.
Умножение и деление типов данных указателя запрещены только компилятором.
Вы не можете умножать или делить типы. ; P
Я не уверен, зачем вам умножать или делить указатель.
Все значения указателя можно преобразовать в одно целое число. Другими словами, какие архитектуры все еще используют сегменты и смещения?
Стандарт C99 позволяет хранить указатели в intptr_t
, который является целочисленным типом. Так да.
Увеличение указателя эквивалентно добавлению sizeof (указанного типа данных) к адресу памяти, сохраненному указателем. Если p является int32 *, то p + 1 равно адресу памяти через 4 байта после p.
x + y
, где x
- это T *
и y
является целым числом, эквивалентным (T *) ((intptr_t) x + y * sizeof (T))
, насколько мне известно. Выравнивание может быть проблемой, но заполнение может быть выполнено в размере
. Я не совсем уверен.
x + y
, где x
- это T *
и y
- целое число, эквивалентно (T *) (( intptr_t) x + y * sizeof (T))
, насколько мне известно. Выравнивание может быть проблемой, но заполнение может быть выполнено в размере
. Я не совсем уверен.
x + y
, где x
- это T *
и y
- целое число, эквивалентно (T *) (( intptr_t) x + y * sizeof (T))
, насколько мне известно. Выравнивание может быть проблемой, но заполнение может быть выполнено в размере
. Я не совсем уверен.
В общем, ответ на все вопросы: " да ", и это потому, что только те машины, которые реализуют популярные языки напрямую, увидели свет и сохранились до нынешнего века. Хотя языковые стандарты оставляют за собой право изменять эти «инварианты» или утверждения, этого никогда не происходило в реальных продуктах, за возможным исключением пунктов 3 и 4, которые требуют некоторого повторения, чтобы быть универсально верным.
Можно создавать сегментированные конструкции MMU, которые примерно соответствуют архитектурам, основанным на возможностях, которые были популярны в академических кругах в прошлые годы, но ни одна такая система обычно не использовалась с такими включенными функциями. Такая система могла бы противоречить утверждениям, поскольку она, вероятно, имела бы большие указатели.
Помимо сегментированных / функциональных блоков MMU, которые часто имеют большие указатели, более экстремальные конструкции пытались кодировать типы данных в указателях. Некоторые из них когда-либо были построены. (Этот вопрос поднимает все альтернативы базовым словесно-ориентированным архитектурам типа "указатель-есть-слово".)
В частности:
int
часто бывает недостаточно большим, вам может понадобиться long
или long long
для хранения указателя. . Интересно отметить, что каждый процессор с архитектурой Intel, т. Е. Каждый отдельный PeeCee, содержит тщательно продуманный блок сегментации эпической, легендарной сложности. Однако, он фактически отключен. Всякий раз, когда загружается ОС ПК, она устанавливает базы сегментов на 0 и длину сегментов на ~ 0, обнуляя сегменты и давая плоскую модель памяти.
Я не имею в виду конкретные примеры из реального мира, но «авторитет» - это стандарт C. Если что-то не требуется стандартом, вы можете создать соответствующую реализацию, которая намеренно не соответствует любым другим предположениям. Некоторые из этих предположений верны в большинстве случаев просто потому, что удобно реализовать указатель как целое число, представляющее адрес памяти, который может быть напрямую выбран процессором, но это просто следствие «удобства» и не может рассматриваться как универсальная истина.
sizeof (int *)
может быть не равным size (double *)
. void *
гарантированно может хранить любое значение указателя. sizeof (pointer_type)
дальше. Это не обязательно эквивалентно арифметическому сложению числа (то есть дальше здесь является логической концепцией. Фактическое представление зависит от архитектуры) sizeof (char *)! = Sizeof (void (*) (void)
? - Нет на x86 в режиме 36-битной адресации (поддерживается практически на всех процессорах Intel, начиная с Pentium 1) )
«Представление указателя в памяти совпадает с целым числом такой же длины в битах» - представления в памяти нет ни в одной современной архитектуре; память с тегами никогда не приживалась и уже была устаревшей до того, как был На самом деле память даже не хранит целые числа, только биты и, возможно, слова (не байты; большая часть физической памяти не позволяет читать только 8 бит.)
«Умножение указателей невозможно» - семейство 68000; адресные регистры (те, которые содержат указатели) не поддерживают этот IIRC.
«Все указатели могут быть преобразованы в целые числа» - Не на PIC.
»Увеличение T * эквивалентно добавлению sizeof (T) к адресу памяти "- истина по определению. Также эквивалентно & pointer [1]
.
В 1950-х, 1960-х и 1970-х годах было много архитектур с адресной адресацией. Но я не могу вспомнить какие-либо основные примеры, в которых использовался компилятор C. Я вспоминаю машины ICL / Three Rivers PERQ в 1980-х годах, которые были адресованы по словам и имели записываемое хранилище управления (микрокод). Один из его экземпляров содержал компилятор C и разновидность Unix под названием PNX , но компилятор C требовал специального микрокода.
Основная проблема заключается в том, что типы char * на машинах с адресной адресацией по словам неудобны, однако вы их реализуете. Вы часто используете sizeof (int *)! = Sizeof (char *)
...
Интересно, что до C существовал язык под названием BCPL , в котором базовый тип указателя был адрес слова; то есть увеличение указателя давало вам адрес следующего слова, и ptr! 1
дал вам слово по адресу ptr + 1
. Для адресации байта был другой оператор: ptr% 42
, если я помню.
РЕДАКТИРОВАТЬ: Не отвечайте на вопросы, когда у вас низкий уровень сахара в крови.Ваш мозг (конечно, мой) работает не так, как вы ожидаете. :-(
Незначительная придирка:
p - это int32 *, тогда p + 1
неверно, он должен быть беззнаковым int32, иначе он будет перенесен на 2 ГБ.
Интересная странность - я понял от автора компилятора C для микросхемы Transputer - он сказал мне, что для этого компилятора NULL был определен как -2 ГБ. Почему? Потому что у Transputer был подписанный диапазон адресов: от -2 ГБ до + 2 ГБ. Можете ли вы поверить в это? не так ли?
С тех пор я встречал разных людей, которые говорили мне, что такое определение NULL нарушено. Я согласен, но если вы этого не сделаете, вы получите указатели NULL в середине диапазона адресов.
Думаю, большинство из нас может быть рад, что мы не работаем над Транспьютером!