Разница указателей между двумя указателями int

int main(void) {
    int *a;
    int *b = a++;
    printf("%d\n",b-a);
    return 0;
}

Я ожидал, что на выходе будет 1, почему это -1?

Предположим, что в приведенном выше вопросе вместо a++ у меня есть ++a.

Это все еще неопределенное поведение?


person Gopi    schedule 21.11.2016    source источник
comment
Что ты получил?   -  person ThisaruG    schedule 21.11.2016
comment
Потому что b = a++ присваивает a b, а затем увеличивает a (что не влияет на b).   -  person Cairnarvon    schedule 21.11.2016
comment
Подсказка: printf("%d\n",b-a);? Да ладно, остальное ты знаешь :)   -  person Sourav Ghosh    schedule 21.11.2016
comment
Разница между несвязанным указателем вызывает неопределенное поведение. Оба должны указывать на один и тот же массив или один из них должен указывать на тот, что находится за этим массивом.   -  person haccks    schedule 21.11.2016
comment
@hackks Что вы подразумеваете под несвязанными указателями? Оба имеют один и тот же тип int   -  person Gopi    schedule 21.11.2016
comment
Единственное неопределенное поведение здесь — использование неинициализированного a.   -  person Carl Norum    schedule 21.11.2016
comment
@hacks прав. арифметика указателей имеет смысл только тогда, когда оба указателя указывают на элементы одного и того же массива. В данном случае это может быть понятно, но нельзя игнорировать тот факт, что это неопределённое поведение по отношению к стандарту.   -  person Haris    schedule 21.11.2016
comment
@SouravGhosh Я не понял твоего намека. Пожалуйста, объясни   -  person Gopi    schedule 21.11.2016
comment
@Карл; C11-§6.5.6/8: Если и операнд-указатель, и результат указывают на элементы одного и того же объекта-массива или один за последним элементом объекта-массива, оценка не должна вызывать переполнение; в противном случае поведение не определено   -  person haccks    schedule 21.11.2016
comment
сообщать кому-то, что их код имеет неопределенное поведение, когда указанное неопределенное поведение тривиально исправить и в любом случае не влияет на реальную проблему, о которой спрашивает OP, довольно неконструктивно. Спасибо @Cairnarvon за помощь OP.   -  person Carl Norum    schedule 21.11.2016
comment
Может оказаться полезным обсуждение на странице Проверьте, указывает ли указатель на заданный массив. Кроме того, имейте в виду, что на некоторых аппаратных средствах (когда возникает проблема, обычно это аппаратные средства типа мейнфреймов со сложной организацией указателей) даже чтение недопустимого (неинициализированного) указателя — без его разыменования — может привести к неожиданному поведению (дампы ядра, так далее).   -  person Jonathan Leffler    schedule 21.11.2016
comment
@CarlNorum, я думаю, если бы указанное неопределенное поведение не влияло на результат, ответ был бы 0. поправьте меня если я ошибаюсь.   -  person Haris    schedule 21.11.2016
comment
@Haris-- Нет. Если a были инициализированы, оператор b = a++ присваивает значение a b, а затем увеличивает значение a. В этом случае b - a не 0.   -  person ad absurdum    schedule 21.11.2016
comment
Если бы a был инициализирован в приведенном выше коде и игнорировал другое неопределенное поведение (хотя мне очень сложно это сделать), то a - b было бы 1, а b - a было бы -1. Хм, мой плохой.   -  person Haris    schedule 21.11.2016
comment
@Haris-- Я не думаю, что здесь есть неопределенное поведение, если a инициализирован, а спецификатор формата в printf() исправлен. Тогда a можно было бы безопасно присвоить b, и совершенно законно увеличить a на единицу, а также законно затем выполнить вычитание b - a.   -  person ad absurdum    schedule 21.11.2016


Ответы (6)


Во-первых, ваш код вызывает неопределенное поведение, потому что он читает и изменяет неинициализированные переменные.

Также следует использовать спецификатор %td для вывода разницы указателей.

Предположим, что указатель a действительно указывает на допустимый объект:

int i;
int *a = &i;
int *b = a++;
printf("%td\n",b-a);

Постфиксный оператор ++ дает текущее значение операнда, а затем увеличивает операнд на единицу. Приведенный выше код идентичен:

int i;
int *a = &i+1;
int *b = &i;
printf("%td\n",b-a);

Указатель a указывает на объект i, а указатель b указывает на объект i. Вычитание из-за арифметики указателя даст -1. Если бы операция была: a-b, то результат был бы 1.

Это определенное поведение.

person 2501    schedule 21.11.2016

Первая проблема, как я здесь вижу, в первую очередь связана с

 int *a;

a является автоматическим локальным, а начальное значение неопределенно (без явной инициализации), поэтому выполнение a++ (или ++a, если на то пошло) вызывает неопределенное поведение.

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

Наконец, вычитание двух указателей дает тип ptrdiff_t, вы должны использовать %td для вывода результата. В противном случае вы снова вызываете UB.


[Как указано в комментариях ниже]

Предполагая, что указатели инициализированы правильно, что-то вроде

int k[5] = {0};
int * a = &(k[0]);
int *b = a++;
printf("%td\n",b-a);

в этом случае результатом будет -1, так как это разница в индексах обоих элементов (помните, что a++ — это пост-инкремент).

Глава §6.5.6/9, C11 стандарт

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

person Sourav Ghosh    schedule 21.11.2016
comment
Я не голосующий против. Просто хотел спросить, а зачем здесь a++ UB? Насколько я знаю, здесь a — это указатель (и уже помещенный в стек), а a++ увеличит адрес, хранящийся в a, на 4 или 8 или что-то еще. Я думаю, что (*a)++ будет UB. Пожалуйста, скажите мне, что мне не хватает. - person babon; 21.11.2016
comment
@babon ты путаешь a с &a? a++ — это UB, основанный на той же логике, если бы a тоже был бы int. - person Sourav Ghosh; 21.11.2016
comment
И что было бы в результате, если бы указатель был правильно инициализирован? ОП ожидает 1... - person Bob__; 21.11.2016
comment
@Bob__ Будет -1. для a++ (b - это before инкремент), ++a не имеет особого смысла, в любом случае оба будут указывать на один и тот же элемент. - person Sourav Ghosh; 21.11.2016
comment
Конечно, и я думаю, вы должны добавить это к ответу. - person Bob__; 21.11.2016
comment
Спасибо за объяснение. - person babon; 21.11.2016
comment
@Bob__ Не очень уверен, что это слишком много предположений с нашей стороны, но в любом случае обновлено. - person Sourav Ghosh; 21.11.2016

Разница двух указателей (в данном случае (b-a)) не определена в C, если только два указателя не указывают на адреса одного и того же массива.

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

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

person Akash Mahapatra    schedule 21.11.2016
comment
И что было бы в результате, если бы указатель был правильно инициализирован? ОП ожидает 1... - person Bob__; 21.11.2016
comment
@Bob__ Обычно мы делаем (b-a), чтобы получить количество элементов массива между этими двумя указателями. но из-за int *b = a++; результат будет -1, что бесполезно - person Akash Mahapatra; 21.11.2016

Когда вы увеличиваете указатель, он не добавляет значение к 1. Он просто говорит указателю указывать на следующее местоположение. Кроме того, ваши указатели никогда не были инициализированы, и они просто указывают на случайные числа мусора.

person tigris_kn    schedule 21.11.2016

int *a — это переменная с автоматическим сроком хранения, и ее адрес никогда не используется (&a). Поэтому код вызывает неопределенное поведение (согласно 6.3.2.1, см. это), и ваша программа не может иметь предсказуемый результат.

Использование спецификатора формата %d для ptrdiff_t также приводит к неопределенному поведению (7.21.6.1/9). Вы должны использовать %td для ptrdiff_t и %p для указателей.

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

person Lundin    schedule 21.11.2016

Это нет неопределенного поведения

когда вы объявили *a, a указывает на ячейку памяти, *b = a++; b, чтобы указать на то же местоположение, что и a, И увеличивает a, чтобы указать на местоположение, в 1 раз превышающее предыдущее местоположение, на которое указывает b.

b - a = -1 //правильно

я приведу пример

int main(void)
     {
        int *a; // 0x408
        int *w = a++; // 0x400
        int *b = a++; // 0x404

        int c = (b-a);
        printf("%d\n",b-a);// -1
        printf("%d\n",w-a); // -2

        return 0;
    }

я представляю w, чтобы лучше проиллюстрировать

Адрес памяти во время выполнения переменной будет похож на приведенный выше.

b - a ( 0x404 - 0x408) == -1( -4 );

размер int (в данном случае) составляет 4 байта. и указатели, содержащие адрес int, будут иметь свой адрес, изменяющийся (+/-) с коэффициентом 4, поскольку в этом случае речь идет о int, 4 байта содержат одно целое число (1 единица)

w - a == (0x400 - 0x408) = -2( -8 )

если у тебя есть

a - w == (0x408 - 0x400) = 2( 8 )

person ytobi    schedule 21.11.2016