Реализация указателя детализирует в C

//' 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
16
задан Community 23 May 2017 в 12:17
поделиться

10 ответов

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.

10
ответ дан 30 November 2019 в 16:14
поделиться

Для 6 .: указатель не обязательно является адресом памяти. См., Например, « The Great Pointer Conspiracy » пользователя Stack Overflow jalf :

Да, я использовал слово «адрес» в комментарии выше. Важно понимать, что я имею в виду под этим. Я не имею в виду «адрес памяти, по которому физически хранятся данные», а просто абстрактное «все, что нам нужно, чтобы найти значение. Адрес i может быть любым, но как только он у нас есть, мы всегда можем найти и изменить i. "

И:

Указатель не является адресом памяти! Я упоминал об этом выше, но давайте повторим еще раз. Указатели обычно реализуются компилятором просто как адреса памяти, да, но это не обязательно. "

7
ответ дан 30 November 2019 в 16:14
поделиться

Дополнительная информация об указателях из стандарта C99:

  • 6.2.5 §27 гарантирует, что void * и char * имеют идентичные представления, то есть их можно использовать взаимозаменяемо. без преобразования, т. е. тот же адрес обозначается одним и тем же битовым шаблоном (что не обязательно должно быть истинным для других типов указателей)
  • 6.3.2.3 §1 утверждает, что любой указатель на неполный или объектный тип может быть приведен к (и из) void * и обратно, но по-прежнему действительны; сюда не входят указатели на функции!
  • 6.3.2.3 §6 гласит, что void * может быть преобразовано в (и из) целые числа, а 7.18.1.4 §1 предоставляет подходящие типы intptr_t и uintptr_t ; эта проблема: эти типы являются необязательными - в стандарте явно указывается, что целочисленный тип не обязательно должен быть достаточно большим, чтобы фактически содержать значение указателя!
6
ответ дан 30 November 2019 в 16:14
поделиться

Я не Насчет остальных не знаю, но для DOS предположение № 3 неверно. DOS является 16-битной и использует различные приемы для отображения памяти, объем которой превышает 16 бит.

2
ответ дан 30 November 2019 в 16:14
поделиться

Представление указателя в памяти такое же, как целое число той же длины в битах, что и архитектура.

Я думаю, что это предположение неверно, потому что на 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)) , насколько мне известно. Выравнивание может быть проблемой, но заполнение может быть выполнено в размере . Я не совсем уверен.

2
ответ дан 30 November 2019 в 16:14
поделиться

В общем, ответ на все вопросы: " да ", и это потому, что только те машины, которые реализуют популярные языки напрямую, увидели свет и сохранились до нынешнего века. Хотя языковые стандарты оставляют за собой право изменять эти «инварианты» или утверждения, этого никогда не происходило в реальных продуктах, за возможным исключением пунктов 3 и 4, которые требуют некоторого повторения, чтобы быть универсально верным.

Можно создавать сегментированные конструкции MMU, которые примерно соответствуют архитектурам, основанным на возможностях, которые были популярны в академических кругах в прошлые годы, но ни одна такая система обычно не использовалась с такими включенными функциями. Такая система могла бы противоречить утверждениям, поскольку она, вероятно, имела бы большие указатели.

Помимо сегментированных / функциональных блоков MMU, которые часто имеют большие указатели, более экстремальные конструкции пытались кодировать типы данных в указателях. Некоторые из них когда-либо были построены. (Этот вопрос поднимает все альтернативы базовым словесно-ориентированным архитектурам типа "указатель-есть-слово".)

В частности:

  1. Представление в памяти всех указателей для данной архитектуры одинаково независимо от типа данных, на который указывает. Верно, за исключением чрезвычайно дурацких прошлых проектов, которые пытались реализовать защиту не на строго типизированных языках, а на аппаратном уровне.
  2. Представление указателя в памяти совпадает с целым числом той же длины в битах, что и архитектура . Может, конечно, какой-то интегральный тип такой же, см. LP64 против LLP64 .
  3. Умножение и деление типов данных указателя запрещено только компилятором. Справа .
  4. Все значения указателя могут быть преобразованы в одно целое число. Другими словами, в каких архитектурах все еще используются сегменты и смещения? Ничто сегодня не использует сегменты и смещения, но C int часто бывает недостаточно большим, вам может понадобиться long или long long для хранения указателя. .
  5. Увеличение указателя эквивалентно добавлению sizeof (указанного типа данных) к адресу памяти, хранящемуся указателем. Если p - это int32 *, то p + 1 равно адресу памяти через 4 байта после p. Да.

Интересно отметить, что каждый процессор с архитектурой Intel, т. Е. Каждый отдельный PeeCee, содержит тщательно продуманный блок сегментации эпической, легендарной сложности. Однако, он фактически отключен. Всякий раз, когда загружается ОС ПК, она устанавливает базы сегментов на 0 и длину сегментов на ~ 0, обнуляя сегменты и давая плоскую модель памяти.

2
ответ дан 30 November 2019 в 16:14
поделиться

Я не имею в виду конкретные примеры из реального мира, но «авторитет» - это стандарт C. Если что-то не требуется стандартом, вы можете создать соответствующую реализацию, которая намеренно не соответствует любым другим предположениям. Некоторые из этих предположений верны в большинстве случаев просто потому, что удобно реализовать указатель как целое число, представляющее адрес памяти, который может быть напрямую выбран процессором, но это просто следствие «удобства» и не может рассматриваться как универсальная истина.

  1. Не требуется стандартом ( см. этот вопрос ). Например, sizeof (int *) может быть не равным size (double *) . void * гарантированно может хранить любое значение указателя.
  2. Не требуется стандартом. По определению, размер - это часть представления. Если размер может быть другим, то и представление может быть другим.
  3. Не обязательно. Фактически, «разрядность архитектуры» - это расплывчатое утверждение. Что такое 64-битный процессор на самом деле? Это адресная шина? Размер регистров? Шина данных? Что?
  4. Нет смысла «умножать» или «делить» указатель. Это запрещено компилятором, но вы, конечно, можете умножать или делить базовое представление (что для меня не имеет смысла), что приводит к неопределенному поведению.
  5. Возможно, я не понимаю вашу точку зрения, но все в цифровом компьютере - это просто какое-то двоичное число.
  6. Да; вид. Гарантированно указывает на место, которое ' sa sizeof (pointer_type) дальше. Это не обязательно эквивалентно арифметическому сложению числа (то есть дальше здесь является логической концепцией. Фактическое представление зависит от архитектуры)
8
ответ дан 30 November 2019 в 16:14
поделиться

sizeof (char *)! = Sizeof (void (*) (void) ? - Нет на x86 в режиме 36-битной адресации (поддерживается практически на всех процессорах Intel, начиная с Pentium 1) )

«Представление указателя в памяти совпадает с целым числом такой же длины в битах» - представления в памяти нет ни в одной современной архитектуре; память с тегами никогда не приживалась и уже была устаревшей до того, как был На самом деле память даже не хранит целые числа, только биты и, возможно, слова (не байты; большая часть физической памяти не позволяет читать только 8 бит.)

«Умножение указателей невозможно» - семейство 68000; адресные регистры (те, которые содержат указатели) не поддерживают этот IIRC.

«Все указатели могут быть преобразованы в целые числа» - Не на PIC.

»Увеличение T * эквивалентно добавлению sizeof (T) к адресу памяти "- истина по определению. Также эквивалентно & pointer [1] .

3
ответ дан 30 November 2019 в 16:14
поделиться

В 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 , если я помню.

2
ответ дан 30 November 2019 в 16:14
поделиться

РЕДАКТИРОВАТЬ: Не отвечайте на вопросы, когда у вас низкий уровень сахара в крови.Ваш мозг (конечно, мой) работает не так, как вы ожидаете. :-(

Незначительная придирка:

p - это int32 *, тогда p + 1

неверно, он должен быть беззнаковым int32, иначе он будет перенесен на 2 ГБ.

Интересная странность - я понял от автора компилятора C для микросхемы Transputer - он сказал мне, что для этого компилятора NULL был определен как -2 ГБ. Почему? Потому что у Transputer был подписанный диапазон адресов: от -2 ГБ до + 2 ГБ. Можете ли вы поверить в это? не так ли?

С тех пор я встречал разных людей, которые говорили мне, что такое определение NULL нарушено. Я согласен, но если вы этого не сделаете, вы получите указатели NULL в середине диапазона адресов.

Думаю, большинство из нас может быть рад, что мы не работаем над Транспьютером!

0
ответ дан 30 November 2019 в 16:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: