Вопрос: Хранение переменных и разыменование их


На основе следующего фрагмента в C

int c1,c2;
printf("%d ",&c1-&c2);

Output : -1
  1. Почему этот код не возвращает предупреждение о том, что формат% d ожидает типа int, но вместо этого он получает (void *).
  2. Почему он возвращает -1 в качестве ответа (а не 1)? Даже если он вычитает адреса, он должен быть -4, а не -1. Когда я меняю инструкцию printf на printf("%d ",&c2 - &c1) Я получаю 1, а не любое случайное значение! Зачем?
  3. Если я изменю инструкцию printf как printf("%d ",(int)&c1 - (int)&c2) Я типизирую адрес на целое значение? Означает ли это, что значение адреса, сохраненного как шестнадцатеричное, теперь преобразуется в int и затем вычитается?

4


источник


Ответы:


1) Он получает int, Указатель на что-то минус указатель на что-то является целым значением (тип ptrdiff_t). Он сообщает вам, сколько элементов находится в двух указателях, указывающих на один и тот же массив.

2) Поскольку два указателя не указывают на один и тот же массив, разница не определена. Любое значение может быть получено.

3) Да. Но «шестнадцатеричная» часть неверна. Адреса хранятся в битах / двоичных (как целые). Какие изменения интерпретируются вашей программой. Это не зависит от представления (hex / dec / oct / ...).


4



Здесь есть несколько случаев неопределенного поведения.

Если мы перейдем к стандартному разделу проекта C99 6.5.6  Аддитивные операторы  он говорит ( акцент мой ):

Когда два указателя вычитаются, оба должны указывать на элементы   тот же объект массива, или один за последним элементом объекта массива;   результатом является разность индексов двух массивов   элементы. Размер результата определяется реализацией, а его   type (целочисленный тип со знаком) является ptrdiff_t, определенным в   заголовок. Если результат не представляется в объекте этого типа,   поведение не определено.

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

Правильный спецификатор формата при использовании printf для ptrdiff_t было бы %td что дает нам второй случай неопределенного поведения, поскольку вы указываете неверный спецификатор формата.


1



1) Пустота * - не что иное, как адрес. Адресом является число (длинное). Там, адрес неявно приводится к int.

2) В памяти ваша переменная не хранится в том же порядке, что и в ваш код ;). По той же причине:

int a[2];
a[0] = 3;
*(a + 1) = 5; // same that "a[1] = 5;"

Этот код поставит «5» во втором случае. Потому что это действительно так:

*(a + 1 *sizeof(*a)) = 5; 

3) Шестнадцатеричный - это числовое представление. Это может быть магазин в int! Пример :

int a = 0xFF;
printf("%d\n", a); // print 255

Надеюсь, что я ответил на ваши вопросы.


0



1) Некоторые компиляторы выдают предупреждения для искаженных printf аргументов, но как переменная функция, время выполнения не имеет возможности проверить, что аргументы имеют тип, указанный в строке формата. Любое несоответствие будет выдаваться неопределенное поведение  поскольку функция пытается привести такой аргумент к некорректному типу.

2) Вы говорите, что результат должен быть -4, но это неверно. Гарантируется, что их массивы будут смещаться последовательно. Вы не можете предположить, что c2Я сидел (&c1 + 1),

3) (int)&c1 преобразует адрес c1 для int, Это снова, в общем, неопределенное поведение, так как вы не знаете, что int достаточно велика, чтобы удерживать значение адреса указателя. ( int может быть 32 бита на 64-битном чипсете). Вы должны использовать intptr_t вместо int,


0