Выделение 50% из привязанного указателя (массив массива)

Это новый вопрос в моей коллекции «Я не понимаю указателей в C и C ++».

Если я смешиваю биты двух указателей с равными значениями (указывающими на один и тот же адрес памяти), у них должно быть одно и то же представление битов, когда кто-то разыскивается, а один – один за другим, что говорит стандарт?

#include  #include  #include  // required: a == b // returns a copy of both a and b into dest // (half of the bytes of either pointers) int *copy2to1 (int *a, int *b) { // check input: // not only the pointers must be equal assert (a == b); // also the representation must match exactly int *dest; size_t s = sizeof(dest); assert(memcmp(&a, &b, s) == 0); // copy a and b into dest: // on "exotic" architectures, size does't have to be dividable by 2 size_t half = s/2; // = floor(s/2), char *pa = (char*)&a, *pb = (char*)&b, *pd = (char*)&dest; // copy half of a into dest: memcpy (pd, pa, half); // copy half of b into dest: memcpy (pd+half, pb+half, s-half); // s-half = ceil(s/2) //printf ("a:%pb:%p dest:%p \n", a, b, dest); // check result assert(memcmp(&dest, &a, s) == 0); assert(memcmp(&dest, &b, s) == 0); return dest; } #define S 1 // size of inner array int main(void) { int a[2][S] = {{1},{2}}; int *past = a[0] + S, // one past the end of inner array a[0] *val = &a[1][0], // valid dereferenceable pointer *mix = copy2to1 (past, val); #define PRINT(x) printf ("%s=%p, *%s=%d\n",#x,x,#x,*x) PRINT(past); PRINT(mix); PRINT(val); return 0; } 

Я действительно хочу понять: что означает «p указывает на объект x»?

СМОТРИТЕ ТАКЖЕ

Этот вопрос является лучшей версией моих предыдущих вопросов о массиве массивов:

  • Является ли memcpy указателя таким же, как присвоение? что является вариацией моего другого вопроса:
  • Выделение указателя, не связанного с привязкой, который содержит адрес объекта (массив массива)

и другие связанные вопросы о действительности указателя:

  • Переменные указателя просто целые с некоторыми операторами или они «мистические»?
  • и вопрос на C ++: переписывание объекта с объектом того же типа

В [basic.compound]:

Если объект типа T расположен по адресу A , указатель типа cv T* , значение которого является адресом A как говорят, указывает на этот объект, независимо от того, как это значение было получено .

past и val имеют одинаковый адрес, поэтому они указывают на один и тот же объект. Не имеет значения, что один «один конец» первой строки, а второй – первый элемент второй строки. На этом адресе есть действительный объект, поэтому все здесь совершенно разумно.


В C ++ 17, начиная с P0137 , это сильно меняется. Теперь [basic.compound] определяет указатели как:

Каждое значение типа указателя является одним из следующих:
указатель на объект или функцию (указывается, что указатель указывает на объект или функцию) или
указатель мимо конца объекта (5.7) или
– значение нулевого указателя (4.11) для этого типа или
недопустимое значение указателя .

Итак, past – это значение второго типа (указатель за концом), но val – значение 1-го типа (указатель на). Это разные категории ценностей и не сопоставимы:

Значение типа указателя, которое является указателем на конец объекта или мимо него, представляет адрес первого байта в памяти (1.7), занятый объектом или первым байтом в памяти после окончания хранения, занимаемого объектом , соответственно. [Примечание: указатель за конец объекта (5.7) не считается направленным на несвязанный объект типа объекта, который может быть расположен по этому адресу. Значение указателя становится недействительным, когда хранящееся хранилище заканчивается его длительностью хранения; см. 3.7. -End note]

past не указывает на что-то, поэтому просмотр его содержимого, как если бы он был таким же, как val , больше не имеет смысла.

Второе утверждение

 assert(memcmp(&a, &b, s) == 0); 

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

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

Если два таких объекта являются memcmp ‘d равными, они имеют одно и то же значение (которое сильнее, чем «сравнивать равным»: они взаимозаменяемы, а указатели, которые сравнивают одинаковые, не обязательно). По крайней мере, это дух стандарта, я не буду ручаться за письмо.

Я действительно хочу понять, что означает «p указывает на объект x».

Объект p содержит значение, соответствующее местоположению объекта x в памяти.

Вот и все. Это все это значит. Вы, похоже, решили сделать это более сложным, чем это должно быть.

Типы указателей не являются арифметическими типами и не должны быть произвольно подобранными. Допустимые значения указателя получают с помощью оператора unary & на lvalue, используя выражение массива, которое не является операндом оператора sizeof или unary & или вызовом библиотечной функции, которая возвращает значение указателя.

Все помимо этого (размер, представление, физическое и виртуальное и т. Д.) Является детальностью реализации , а реализации сильно различаются, когда речь идет о представлении адресов. Вот почему стандарты ничего не говорят о том, чего ожидать, когда вы играете доктора Франкенштейна со значениями указателя.

Если вы хорошо знакомы с соглашениями об адресах вашей платформы (как виртуальными, так и физическими), и знаете, как ваша реализация содержит элементы в памяти и как они представляют типы указателей, и у вас есть допустимый прецедент для взлома значений указателя таким образом, затем взломайте содержимое вашего сердца – ни один языковой стандарт не может что-либо сказать по этому вопросу.