Лежат ли адреса элементов данных между (этим) и (этим + 1)?

Предположим, что у нас есть следующие два неравенства внутри функции-члена

this <=  (void *) &this->data_member

и

&this->data_member < (void *) (this+1) 

Гарантированно ли они верны? (Кажется, они верны в нескольких случаях, которые я проверил.)

Обновлено: я пропустил амперсанды, теперь это правильная форма неравенства.

Является ли data_member указателем?

Öö Tiib 10.12.2018 12:41

Каковы были бы ваши планы, если бы конкретный компилятор C++ использовал, например, один за концом фактического объекта в качестве фактического значения указателя памяти для объекта?

Sam Varshavchik 10.12.2018 12:42

@ Öö Tiib, Никаких амперсандов не пропало, сейчас поправили.

user10636819 10.12.2018 12:58

Я полагаю, вы имеете в виду this <= (void *)(&this->data_member) и (void *)(&this->data_member) < (void *)(this + 1). В любом случае стандарт определяет только результаты операторов сравнения, применяемых к членам. this не является членом, и ничего не говорится о связи между this и адресом его нестатических элементов, т.е. поведение не указано. На практике для доступа к нестатической структуре или членам класса, вероятно, потребовалась бы довольно забавная бухгалтерия, если бы члены не были размещены в памяти, на которую указывает this.

Peter 10.12.2018 12:58

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

T.C. 11.12.2018 01:24

@ T.C. Я пытался создать контрпример с виртуальной базой, но пока у меня ничего не вышло. Не могли бы вы показать это?

user10636819 11.12.2018 11:36

@ user10636819 Ну вот

Caleth 11.12.2018 13:04

@Caleth Спасибо, это хороший пример. Это также немного странно, поскольку эти неравенства, кажется, снова удовлетворяются, если тестировать непосредственно внутри структуры D, а не путем вызова функций-членов базовых классов. Я все еще изучаю C++ (и еще не понимаю виртуального наследования), но я предполагаю, что это подразумевает неопределенное поведение.

user10636819 11.12.2018 14:47

@ user10636819 sizeof(D) не является sizeof(B) + sizeof(C), потому что у него только одна база A. Есть некоторая забавность, позволяющая преобразовать D* в B*, что, вероятно, реализовано представлением B, имеющим только указатель на его базу A.

Caleth 11.12.2018 14:59

Было бы полезно, если бы вы сказали, заботитесь ли вы только о полных объектах и ​​членах массива или произвольных подобъектах.

curiousguy 11.12.2018 22:07

@ user10636819 "и еще не разбираюсь в виртуальном наследовании" Регулярное наследование - это исключительное отношение, подобное членству. Виртуальное наследование - это отношение общего наследования, поэтому оно сильно отличается. Виртуальное наследование похоже на виртуальную функцию, его можно переопределить (в некотором смысле): это означает, что отношение наследования между B и A переопределяется в D. Таким образом, B в D не создается как полный объект B. Без виртуального наследования базовый класс строится как законченный объект.

curiousguy 11.12.2018 23:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
11
256
4

Ответы 4

Из проекта стандарта CPP 4713:

6.6.2 Object model [intro.object]/7
An object of trivially copyable or standard-layout type (6.7) shall occupy contiguous bytes of storage.

12.2 Class members [class.mem]/18
Non-static data members of a (non-union) class with the same access control (Clause 14) are allocated so that later members have higher addresses within a class object.

12.2 Class members [class.mem]/25
If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member. Otherwise, its address is the same as the address of its first base class subobject (if any).

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

Также из онлайн ссылка cpp:

The result of comparing two pointers to objects (after conversions) is defined as follows:

1) If two pointers point to different elements of the same array, or to subobjects within different elements of the same array, the pointer to the element with the higher subscript compares greater. In other words, they results of comparing the pointers is the same as the result of comparing the indexes of the elements they point to.
2) If one pointer points to an element of an array, or to a subobject of the element of the array, and another pointer points one past the last element of the array, the latter pointer compares greater. Pointers to single objects are treated as pointers to arrays of one: &obj+1 compares greater than &obj (since C++17)

Итак, если ваш data_member является указателем нет и ему не была выделена память отдельно, уравнения, которые вы опубликовали, остаются в силе. по крайней мере для тривиально копируемых объектов.

Тонкость и проблема в том, что программа обычно не может протестировать ее сама, потому что тогда ее поведение будет неопределенным.

StoryTeller - Unslander Monica 10.12.2018 12:58

@StoryTeller Как ваше утверждение связано с вопросом или ответом?

Peter - Reinstate Monica 10.12.2018 13:01

@ PeterA.Schneider - Прямо из вопроса: «В некоторых случаях, которые я проверил, они кажутся правдивыми». Как вы себе представляете, что OP это проверяет?

StoryTeller - Unslander Monica 10.12.2018 13:02

Спасибо! Это гарантирует выполнение второго неравенства. А как насчет первого (он сравнивает элементы объекта с его собственным адресом)?

user10636819 10.12.2018 13:04

@ user10636819: «Если объект класса стандартной компоновки имеет какие-либо нестатические элементы данных, его адрес такой же, как адрес его первого нестатического члена данных». и «Нестатические элементы данных (не объединенного) класса с одинаковым контролем доступа выделяются таким образом, чтобы более поздние члены имели более высокие адреса в объекте класса».. Взятые вместе, это будет означать, что первое также верно.

P.W 10.12.2018 13:54

@StoryTeller Логический результат сравнения - true, который легко проверить в программе. Это не имеет ничего общего с тем, правильна программа или нет, и тем более с самой программой. (Если OP преуспеет в тот, он немедленно получит предложение от прачечной, от которого он не сможет отказаться ;-).)

Peter - Reinstate Monica 10.12.2018 14:10

@ PeterA.Schneider - Логический результат этого выражения не определен стандартом, я не знаю, каково ваше определение «правильного», мое приближение к этому начинается с того, дает ли стандарт нам спецификацию, чтобы рассуждать об этом. . Если мы не можем рассуждать об этом действительно портативным способом, обсуждение «правильности» бессмысленно.

StoryTeller - Unslander Monica 10.12.2018 14:15

@StoryTeller Вы сказали, что «программа обычно не может протестировать ее сама», и хотя это правда, это не имеет никакого отношения к вопросу или ответу: в обоих случаях мы люди пытается установить, является ли программа действительной на C++, и если да, имеет ли он определенное поведение, и если да, то указано ли это поведение. Ни в коем случае программа не пытается себя протестировать. в этой программе все три.

Peter - Reinstate Monica 10.12.2018 14:24

@ PeterA.Schneider - Никто не встал на сторону против P.W. И я отказываюсь, чтобы меня затащили в эту кроличью нору. Вы все еще не понимаете, как связан мой комментарий? Что ж, я думаю, это очень плохо. Добрый день.

StoryTeller - Unslander Monica 10.12.2018 14:26

@StoryTeller Вы сказали: «Логический результат этого выражения не определен стандартом», что, по-видимому, противоречит выводу П.В. «Уравнения, которые вы опубликовали, остаются в силе», если я не неправильно читаю одного из вас.

Peter - Reinstate Monica 10.12.2018 14:27

Наличие одного и того же адреса не гарантирует, что он будет иметь эквивалентное частичное упорядочение при преобразовании в void*. this не указывает на нестатический член данных, и поэтому результат сравнения не указан. Для типов стандартного макета нет специального предложения.

Passer By 18.12.2018 12:26

Полный стандартный текст составляет:

[expr.rel] - 4: The result of comparing unequal pointers to objects82 is defined in terms of a partial order consistent with the following rules:

Здесь мы имеем дело с частичным порядком, а не с полным порядком. Это означает, что a < b и b < c подразумевают a < c, но не более того.

(В примечании 82 указано, что для этой цели объекты, не являющиеся массивом, считаются элементами одноэлементного массива, с интуитивным значением / поведением «указатель на элемент, следующий за концом»).

(4.1) If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to the element with the higher subscript is required to compare greater.

Указатели на разные элементы не являются указателями на элементы (подобъекты) одного и того же массива. Это правило не действует.

(4.2) If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the pointer to the later declared member is required to compare greater provided the two members have the same access control ([class.access]), neither member is a subobject of zero size, and their class is not a union.

Это правило связывает указатели только на элементы данных одного и того же объекта, но не другого объекта.

(4.3) Otherwise, neither pointer is required to compare greater than the other.

Таким образом, вы не получаете никаких гарантий от стандарта. Другой вопрос, сможете ли вы найти реальную систему, в которой вы получите результат, отличный от ожидаемого.

Я бы просто добавил, что для стандартные классы верстки указатель на его экземпляр / объект может быть интерпретирован как указатель на его первую нестатическую переменную-член. И все другие переменные-члены должны храниться по большим адресам. Это означает, что оба условия OP гарантированно выполняются, но только в этом частном случае.

Daniel Langr 10.12.2018 13:49

Кроме того, для тривиально копируемые классы все элементы данных должны храниться между указателем на объект и тем же, увеличенным на его размер в байтах (что подразумевает то же самое). В противном случае нам бы не разрешили memcpy такие объекты.

Daniel Langr 10.12.2018 13:54

Это был бы мой неожиданный ответ («не связанные объекты, никаких гарантий»); но когда мы считаем, что «отдельные объекты эквивалентны массивам размером 1» плюс «1-последний определяется как большее», мы, как я полагаю, получаем ответ PW, потому что «& x + 1» не является «несвязанным» "но один-прошедший-массив, к которому применяется 4.1.

Peter - Reinstate Monica 10.12.2018 14:14

@ PeterA.Schneider Что, если бы для нетривиально копируемого класса нестандартной компоновки указатель на его экземпляр был бы внутренне определен как адрес его последнего байта в памяти. Есть ли что-нибудь в Стандарте, запрещающее такое применение?

Daniel Langr 10.12.2018 14:24

@DanielLangr Указатель на объект (т.е. кусок типизированной памяти) всегда указывает на его первый байт. [Также: как бы вы «внутренне определили»?] Не уверен, что могу указать для этого стандартное место; но нарушение этой основной идеи, похоже, нанесет ущерб ряду инвариантов (::free(void *) !?). То, что мы находим там, не гарантируется, и предположения OP верны только для нестатического, ну, данные-члены, но я не вижу требования для «тривиально копируемого» или «стандартного макета». [ctd.]

Peter - Reinstate Monica 10.12.2018 14:42

[ctd .:] 12.2 / 17 в n4659 прямо говорит: «Нестатические элементы данных класса (не объединенного) с тем же контролем доступа (пункт 14) выделяются так, чтобы более поздние члены имели более высокие адреса внутри объекта класса (выделено мной) Здесь особо не о чем говорить.

Peter - Reinstate Monica 10.12.2018 14:43

@ PeterA.Schneider Это может быть чрезвычайно педантично, но где в стандарте указывается, что означает «первый» в "его адрес такой же, как и адрес его первого нестатического элемента данных"? Конечно, все мы знаем, что имеется в виду, но разве это где-нибудь уточняется?

Max Langhof 10.12.2018 15:01

@MaxLanghof Педантичный ответ, конечно, таков: это элемент, адрес которого совпадает с адресом объекта. ;-) Но если серьезно: есть гарантия, что элементы расположены в порядке их объявления (в отличие, например, от оценки параметров функции, которая не указана, IIRC).

Peter - Reinstate Monica 10.12.2018 15:06

@ PeterA.Schneider Эта гарантия существует только для членов данных с тем же уровнем доступа. В частности, не уточняется, приходят ли ваши участники private раньше членов public или наоборот. Таким образом, я мог довести педантизм до крайности и сказать, что «первый» всегда означает «частные члены», даже несмотря на то, что в моей реализации частные члены имеют более высокие значения указателей, чем публичные.

Max Langhof 10.12.2018 15:10

@DanielLangr "будет внутренне определен как адрес" Что значит "внутренне"? Представление указателя - это деталь реализации: вы не можете интерпретировать указатель как другой тип указателя и ожидать чего-либо разумного. У вас могут быть указатели на 8-байтовый выровненный тип, у которого для развлечения установлены 3 младших бита, но всегда маскируются при разыменовании. Вы увидите, что эти биты устанавливаются только с помощью каламбура или проверки представления (reinterpret_cast<void**>, union, memcpy) или, возможно, reinterpret_cast<int>.

curiousguy 14.12.2018 00:49

IOW, можно ли измерить внутренне определенный адрес с помощью конструкции с четко определенным результатом? Что будет делать неявное преобразование этих указателей в void* или reinterpret_cast<char*>? Будет ли результат указывать на последний байт или это будет изменено, чтобы скрыть «внутреннее представление»?

curiousguy 14.12.2018 00:51

"Что делать, если для нестандартного макета нетривиально копируемого класса" класс под названием T "указатель на его экземпляр будет внутренне определен как адрес его последнего байта в памяти" Что произойдет с таким кодом? void *raw = operator new(sizeof T); T *p = new(raw) T; p->~T(); operator delete(/*note: not raw*/ p);

curiousguy 14.12.2018 00:57

@curiousguy Согласитесь, это, скорее всего, неверный пример. Просто примечание: не будет ли здесь delete p; неопределенным поведением? Аргументом operator delete может быть только указатель, полученный operator new, которым здесь является raw.

Daniel Langr 14.12.2018 11:33

@DanielLangr Я никогда не рассматривал возможность того, что new(raw) T != raw.

curiousguy 14.12.2018 11:53

Значение объекта находится в его представлении, и это представление представляет собой последовательность символов без знака: [basic.types] / 4

The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T). The value representation of an object of type T is the set of bits that participate in representing a value of type T.[...]

Итак, для фундаменталистов формализма верно, что ценность не определено, но этот термин появляется в определении доступ: [defns.access]:

read or modify the value of an object

Так является ли значение подобъекта частью значения полного объекта? Полагаю, это то, что предусмотрено стандартом.

Сравнение должно быть верным, если вы приводите указатели объектов к unsigned char*. (Это обычная практика, которая не соответствует спецификации основной выпуск # 1701)

Да для любого объекта полный.

curiousguy 11.12.2018 23:09

@curiousguy Ты про тип?

Oliv 12.12.2018 09:04

Вы даже не можете выполнять арифметические операции с указателем неполного типа, AFAIK, поэтому нет p+1. Я имею в виду законченный объект.

curiousguy 12.12.2018 22:16

Незавершенного объекта нет! См. intro.object. Но действительно, если ссылка или указатель относятся к неполному типу, с ними мало что можно сделать.

Oliv 12.12.2018 22:27

Что вы имеете в виду под «законченным объектом»?

curiousguy 12.12.2018 22:33

«Да для любого готового объекта» Я имел в виду, что «Объектное представление объекта типа T ...» должно читать «полный объект». [basic.types] / 4 здесь неверно (IMNSHO)

curiousguy 12.12.2018 22:39

"Объект создается определением, новым выражением ([expr.new]), при неявном изменении активного члена объединения или при создании временного объекта ([conv.rval], [class.porary] )" Итак, вы согласны с тем, что каждый учебник, когда-либо написанный на C++, ошибочен, Stroustrup ошибается, std неверен WRT, конвертирующий программы C в C++ (смеется). Вы действительно хотите поддержать это утверждение? Юрист никогда не собирался относиться к тексту всерьез по отношению к любому другому источнику на C++.

curiousguy 12.12.2018 22:42

Кроме того, «при неявном изменении активного члена союза» НЕ присутствовало более ДЕСЯТИЛЕТИЯ. Так кто-нибудь серьезно относится к этому предложению? Действительно? Это так неправильно! Кроме того, «объект создается определением» даже не вычисляется, поскольку определение является синтаксической конструкцией, а объект существует во время выполнения. Он мог бы сказать оценку, но я не уверен, что определения «оценены». Это плохой стандарт!

curiousguy 12.12.2018 22:46

@curiousguy Вы заставляете меня переделывать себя до того, как меня сформировали, за счет того, что я много читал стандарт. Я думаю, что я принял это «экзистенциалистское» использование глагола «быть» для объектов (в стандарте, когда он написан ... есть объект. Это означает, что объект был построен, но не был уничтожен, и его хранилище не было освобождено). Но как бы то ни было, возможно иметь выражение, ссылочный тип которого является неполным: объект создается в единице перевода, где тип завершен, а затем упоминается в другой единице перевода, где тип является неполным, и в этом случае, как вы говорите,

Oliv 12.12.2018 22:59

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

curiousguy 13.12.2018 03:14

Нет, вот контрпример

#include <iostream>

struct A
{
    int a_member[10];    
};

struct B : public virtual A
{
    int b_member[10];
    void print_b() { std::cout << static_cast<void*>(this) << " " << static_cast<void*>(std::addressof(this->a_member)) << " " << static_cast<void*>(this + 1) << std::endl; }
};

struct C : public virtual A
{
    int c_member[10];    
    void print_c() { std::cout << static_cast<void*>(this) << " " << static_cast<void*>(std::addressof(this->a_member)) << " " << static_cast<void*>(this + 1) << std::endl; }
};

struct D : public B, public C 
{
    void print_d() 
    { 
        print_b();
        print_c();
    }
};

int main()
{
    D d;
    d.print_d();
}

С возможным выводом (как видел здесь)

0x7fffc6bf9fb0 0x7fffc6bfa010 0x7fffc6bfa008
0x7fffc6bf9fe0 0x7fffc6bfa010 0x7fffc6bfa038

Обратите внимание, что a_member находится за пределами B, на который указывает this в print_b.

И рассматривать такой базовый подобъект как элемент массива (как воображаемый массив из одного элемента) на самом деле не имеет смысла. Не уверен, что это должно быть законно.

curiousguy 11.12.2018 23:08

Другие вопросы по теме