C/C++

Различие указателей int *; и char *;

Int *a;
здесь объявляется переменная a как указатель на объект с типом int

char *b;
здесь объявляется переменная b как указатель на объект с типом char

Разве указателю (переменной) не всё равно на какой тип переменной она ссылается?
То есть в указателе будет в любом случае адрес (шестнадцатеричное число) что с указателем на int, что с указателем на char.
В коде будет выдавать ошибку если перепутать int *; и char * но в них хранится ведь адрес, может даже один и тот же.
Запускаю по очереди int * и char *, выдаёт один и тот же адрес указателя.
Похоже на правило: так надо делать, потому что так делать надо.
Самому указателю вообще все равно. Он действительно просто указывает на байт в памяти, а вот компилятору нужно размер знать. Переменные разных типов могут иметь разный размер. Обычно int - 4 байта, char - 1 байт.

Для массивов это особенно важно.
int* p;
char* c;
p++ -сдвинет на 4 байта
с++ -сдвинет на 1 байт
Действие одно - переход к следующему элементу, а вот адреса уже разные получатся.

При разыменовывании получаются разные типы.

Вообще, тебе ничего не мешает сделать даже так (см скрин):
int someValue = 0x64726553;
char* p = reinterpret_cast(&someValue);
std::cout << *p << *(p + 1) << *(p + 2) << *(p + 3) << std::endl;
Но тут стоит учитывать Порядок байтов (endianness) https://ru.wikipedia.org/wiki/Порядок_байтов
И вообще такая практика не приветствуется в современной разработке

P.s. Не нравятся разные типы - пиши на ассемблере :)
ВГ
Виктор Голунов
17 671
Лучший ответ
И.л.ь.я. По сути здесь в компиляцию вмешивается оптимизатор, ведь, строго говоря, адресация отдельных байтов принципиально невозможна, всегда адресуется целое машинное слово! В таком случае каждый байт располагался бы в отдельном слове (а в массиве — тем более).

Однако, в действительности байты в памяти хранятся сжато, занимая группами по одному целому слову (например, если слово 64-разрядное, то в каждом — 8 байт). Но как же тогда адресовать отдельно взятый байт? Судя по всему, оптимизатор добавляет к адресу всего слова некоторый индекс, который указывает конкретный байт внутри слова. И инкремент указателя на байт приводит в первую очередь к изменению этого внутреннего индекса. А как изменить всего один из байтов, не меняя другие байты того же слова? Никак, реально изменяется всё машинное слово.
И.л.ь.я. Как вы считаете, сколько байт динамической памяти будет выделено при выполнении инструкции char * cp = new char [10]; ?

По правилам можно ожидать, что 10, а в действительности это зависит от размера машинного слова.
Переменные-указатели хранят адреса в памяти, да.
Но при разыменовании указателей (то есть при получении значения, на которое указатель указывает) получаем значения разных типов: в одном случае это int, а в другом — char.
"Похоже на правило: так надо делать, потому что так делать надо."

Сразу видно, что ты:

1) не видел Rust. В котором этих правил больше, чем в C++ и Си вместе взятых. А понять, зачем они нужны, и зачем такой странный язык- еще сложнее.

2) любитель. Профессионалу не столь важно, работает ли его код, ему важно, чтобы его код приняли, как соответствующий правилам. И в общем-то чем больше замечаний сделает компилятор, тем меньше сделает тимлид или сеньор, который будет смотреть код.

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

Но вот положено, чтобы над проектом работала именно команда (а не ты один), и в нее не будут набирать идеальных гениев. Более того, в команде есть не только программистские задачи. Например, у любого тимлида есть функция "рабовладелец" и это более важная функция для тимлида, чем "умный программист". Однако тимлиду все равно приходится работать и с кодом, а значит код должен быть понятен и дураку.
Нет не все равно, у разных типов объем разный, и длинна ссылки соответственнно тоже разная.
Олег Браун Я смотрел адреса указателей на char и int. Они одинаковые.
Вот пример из моего компилятора.

Это адрес из указателя на тип int 00722FBC
Это адрес указателя на тип char 00EB2FBC
-----------------------------
Сейчас заметил что одинаковые не полностью. Но это один тип данных.
Олег Браун В чём заключается различие структуры указателей int* и char*. Видимо не в цифрах адреса?
Олег Браун И правда)) Кмпилируется, выдаёт только предупреждение, но не критическую ошибку))
int main()
{
int a=10;
char * arr= &a;
printf("%d\n", *arr);
return 0;
}
Олег Браун Пример на С++ у вас. Я С учу.
Не совсем понял явное преобразование, аналог сделать не смог.
Олег Браун Нет, какие задания. Я сам учу.
Будет что интересное по теме добавить, пишите.
Sergey Iglin Вы абсолютно правы. В общем случае внутреннее представление указателя может быть разным в зависимости от типа. Язык С гарантирует только в некоторых случаях, что представление указателей будут совпадать, например такая гарантия есть для void* и char*, для указателей на struct type. Для С++ этот нюанс сохраняется. При этом не имеет значения, как именно выводятся адреса на консоль - вывод не отражает внутреннее представление, это лишь результат преобразования.
Сомневающимся можно продолжить ликбез здесь:
https://stackoverflow.com/questions/66102053/can-pointers-to-different-types-have-different-binary-representations
И здесь: http://www.c-faq.com/null/machexamp.html
Естественно, никто не говорит, что если представление адреса *может* отличаться, то оно *будет отличаться всегда*.
Такой запрет со стороны компилятора вовсе не ДОГМА, а правило ДИСЦИПЛИНЫ, такое же, как запрет на пересечение двойной сплошной в ПДД.
Допустим, Вы пересекли двойную сплошную, но никому не причинили ущерба. Но по правилам даже при таких обстоятельствах вас ожидает наказание, поскольку с Вашей стороны опасно не разовое нарушение правил, а нарушение дисциплины при движении по проезжей части.
Вот так же и компилятор следит за дисциплинированностью разработчика, не позволяя напрямую смешивать разные типы (а указатели на разные базовые типы считаются разными типами).

И — да, внутреннее представление любого указателя на любой тип одинаково. Несущественная разница может быть между far и near, но эти модификаторы применимы к указателям любого типа.
Qosqar Salahov
Qosqar Salahov
16 172
Qosqar Salahov Ещё точнее пример с ремнями безопасности.

Вы можете считать себя дисциплинированным водителем и быть уверенным, что Вам никогда не придётся тормозить резко. Увы, реально дорога принадлежит не одному только Вам, и невозможно отвечать за других участников движения. Любая случайность может привести к ситуации, в которой Вам внезапно придётся резко затормозить, и Вы рискуете лишиться здоровья, а то и жизни. Пристёгнутый ремень безопасности это страховочная мера, как и подушка безопасности.

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