Почему указатель не может соответствовать переменной другого типа, даже если размер тот же?

Почему размер любого указателя 4 или 8 байт, но никакая другая переменная в него не помещается? Я получаю сообщение об ошибке при попытке присвоить двойному указателю значение указателя int.

int          *int_ptr{nullptr};
float        *float_ptr{nullptr};
double       *double_ptr{nullptr};
long double  *long_double_ptr{nullptr};
string       *string_ptr{nullptr};
vector<int>   *vector_ptr{nullptr};

cout << "sizeof int pointer is " << sizeof int_ptr;    //8 or 4
cout << "sizeof float pointer is " << sizeof float_ptr;    //8 or 4
cout << "sizeof double pointer is " << sizeof double_ptr;    //8 or 4
cout << "sizeof double double pointer is " << sizeof long_double_ptr;    //8 or 4
cout << "sizeof string pointer is " << sizeof string_ptr;    //8 or 4
cout << "sizeof vector int pointer is " << sizeof vector_ptr;  //8 or 4

double double_num{1020.7};
double_ptr = &int_ptr;      //cannot convert ‘int**’ to ‘double*’ in assignment

person mustafa mohamed    schedule 08.04.2019    source источник
comment
Потому что шрифт нужен не только для подгонки под размер.   -  person Federico klez Culloca    schedule 08.04.2019
comment
Может быть полезно прочитать статью Википедии о системе типов и Что такое безопасный тип?   -  person xskxzr    schedule 08.04.2019
comment
Возможно ли, что вы намеревались написать double_ptr = &double_num?   -  person Jonathan Mee    schedule 08.04.2019
comment
скажем, вы поймали рыбу того же размера, что и ваша кошка, тогда ваша кошка все еще кошка, а не рыба...   -  person 463035818_is_not_a_number    schedule 08.04.2019


Ответы (3)


C++ — это статически типизированный язык. Язык обеспечивает безопасность типов и защищает вас от ошибок, отклоняя произвольные преобразования между несвязанными типами. Значение типа не полностью определяется только его размером.

Если адрес содержит объект типа int*, то int** может указывать на этот объект. Учитывая, что адрес содержит объект типа int*, он не может также содержать объект типа double, поэтому не существует осмысленного способа преобразовать один из этих указателей в другой.

person eerorika    schedule 08.04.2019

Указатели — это адреса. Допустим, у вас есть два адреса:

  1. 53 Главный проспект
  2. 85 Неверный Путь

Первый - адрес небольшого дома с 2 спальнями. Второй - адрес в особняк, 23 спальни, 10 ванных комнат и т. д...

Вы не можете ожидать, что все люди, живущие в особняке, переедут в маленький домик, верно? но знаете что? на почте размеры коробок у них одинаковые! Вот как они работают. Они просто говорят вам, где найти вашу переменную. Это не контейнеры

person PhoenixBlue    schedule 08.04.2019
comment
Я не понимаю, как это отвечает на вопрос. Из вашего примера можно (ошибочно, конечно, но все же можно) написать адрес 2 в письме, которое предназначено для адреса 1. Кроме того, указатели - это не только адреса, как вы сказали, они содержат дополнительную информацию о типе. - person xskxzr; 08.04.2019
comment
да, это был бы хороший пример, если бы указатели были независимыми от типа, в вашей аналогии ничто не мешает мне поменять местами два адреса - person 463035818_is_not_a_number; 08.04.2019
comment
Вы можете исправить это, сказав: У вас есть листовка с рекламой мясной лавки и листовка с рекламой пекарни. Адреса, написанные на флаерах, это просто адреса, если их поменять местами, то можно найти дома, но вы не получите того, что ожидаете, флаеры становятся бесполезными. - person 463035818_is_not_a_number; 08.04.2019

Этот код является недопустимым, поскольку отсутствует "неявное преобразование" для сопоставления с &int_ptr на double_ptr, где "неявное преобразование" определяется как нечто:

Выполняется всякий раз, когда выражение некоторого типа T1 используется в контексте, который не принимает этот тип, но принимает какой-либо другой тип T2; особенно:

  • когда выражение используется в качестве аргумента при вызове функции, которая объявлена ​​с T2 в качестве параметра;
  • когда выражение используется в качестве операнда с оператором, который ожидает T2; при инициализации нового объекта типа T2, включая оператор return в функции, возвращающей T2;
  • когда выражение используется в операторе switch (T2 — целочисленный тип);
  • когда выражение используется в операторе if или цикле (T2 — логическое значение). Программа правильно сформирована (компилируется) только в том случае, если существует одна однозначная неявная последовательность преобразования из T1 в T2.

Я первоначально предложил использовать reinterpret_cast, но это также не будет работать, так как использование результата reinterpret_cast между типами допустимо только в том случае, если типы различаются только тем, подписаны ли они, приведение типа to — это byte*, char* или unsigned char*, или типы являются "похожими", которые определяются как:

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

Как видите, ни одна из этих ситуаций не применима к желанию выполнить приведение с адреса int* int_ptr к double* double_ptr. Мне трудно предсказать, как вы будете использовать этот тип приведения в строго типизированном языке, но, возможно, void* это то, что вы ищете? Он может указывать на любой допустимый int**, и в этом случае вы можете инициализировать его следующим образом: const void* ptr = reinterpret_cast<void*>(&int_ptr) или действительный double*, и в этом случае вы должны инициализировать его следующим образом: const void* ptr = reinterpret_cast<void*>(double_ptr). Конечно, чтобы использовать ptr, вам понадобится переменная, указывающая, какой тип она содержит, например:

if(is_int) {
    // recover int** (reinterpret_cast<const int* const*>(ptr)) and operate on it
} else {
    // recover double* (reinterpret_cast<const double*>(ptr)) and operate on it
}

Живой пример

Здесь я должен признать, что этот ответ несколько надуман. Лучшим решением везде, где используется этот код, скорее всего, будет шаблон.

person Jonathan Mee    schedule 08.04.2019
comment
reinterpret cast приведет к неопределенному поведению - person Caleth; 08.04.2019
comment
@Caleth В зависимости от реализации вы правы. Чтобы не вводить в заблуждение, я пошел дальше и напечатал некоторые дополнительные пояснения. - person Jonathan Mee; 08.04.2019
comment
Не в зависимости. Вы нарушаете строгое правило псевдонимов, это поведение undefined. То, что конкретное поведение, которое вы видите, соответствует вашим ожиданиям, совершенно случайно. - person Caleth; 08.04.2019
comment
@Caleth Я ценю ваши комментарии. На самом деле я знал reinterpret_cast для приведения любого типа указателя к другому типу, который не был byte*... Наверное, я просто на минуту запутался. Спасибо, что вернул меня обратно :J - person Jonathan Mee; 17.04.2019