Сохранение разницы указателей в целых числах?

Это код:

#include<stdio.h>
#include<conio.h>

int main()
{
    int *p1,*p2;
    int m=2,n=3;
    m=p2-p1;
    printf("\np2=%u",p2);
    printf("\np1=%u",p1);   
    printf("\nm=%d",m);       
    getch();
    return 0;
}

Это дает результат как:

p2= 2686792
p1= 1993645620
m= -497739707

У меня есть два сомнения по поводу кода и вывода:

  1. Поскольку «m» является целым числом, он не должен принимать p2-p1 в качестве входных данных, поскольку p1 и p2 оба являются указателями, а m является целым числом, оно должно выдавать ошибку типа «недопустимое преобразование из« int »в« int »», но это не так. Почему?

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


person S. Gupta    schedule 07.08.2013    source источник
comment
Ваши p1 и p2 унифицированы.   -  person Jonathon Reinhart    schedule 07.08.2013
comment
1) Разница между двумя указателями одинакового типа является целым числом (или, возможно, длинным, не уверенным в мелком шрифте). 2) Разница верная действительна, так как два указателя не инициализированы и, следовательно, просто мусор.   -  person Hot Licks    schedule 07.08.2013
comment
Не говоря уже о том, что арифметика указателя зависит от размера типа, на который он указывает.   -  person Jonathon Reinhart    schedule 07.08.2013
comment
Также погуглите «арифметика указателей» для большего понимания.   -  person SaganRitual    schedule 07.08.2013
comment
я сомневаюсь, что p2 и p1 получают некоторые значения мусора, но разница неверна.. как только они заданы, я думаю, что значения мусора исправляются для всего кода, поэтому разница в приведенном выше случае должна быть 2686792-1993645620 = -1990958828...   -  person S. Gupta    schedule 07.08.2013
comment
На самом деле правильно другое (для арифметики указателя). (2686792 - 1993645620) / sizeof(int) = -497739707   -  person Chris Cooper    schedule 07.08.2013


Ответы (2)


Поскольку 'm' является целым числом, он не должен принимать p2-p1 в качестве входных данных, так как p1 и p2 являются указателями, а m является целым числом, это должно привести к ошибке, например "недопустимое преобразование из 'int' в 'int ', но это не так. почему?

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

Даже после получения ввода разница недействительна. Почему?

На самом деле, разница правильная! Он использует арифметику указателя для выполнения вычислений. Итак, для этого примера..

p2= 2686792

p1= 1993645620

Поскольку указатели не инициализированы, им присваиваются какие-то мусорные значения, подобные приведенным выше. Теперь вы хотите выполнить операцию p2 - p1, т.е. вы запрашиваете адрес памяти, который идет ровно за блоками памяти p1 перед p2. Поскольку p1 и p2 являются указателями на целые числа, размер блока памяти равен sizeof(int) (почти всегда 4 байта). Следовательно:

p2 - p1 = (2686792 - 1993645620) / sizeof(int) = (2686792 - 1993645620) / 4 = -497739707

person Joel    schedule 07.08.2013

Поскольку «m» является целым числом, он не должен принимать p2-p1 в качестве входных данных, поскольку p1 и p2 оба являются указателями, а m является целым числом, оно должно выдавать ошибку типа «недопустимое преобразование из« int »в« int »», но это не так. Почему?

Спецификация C++ объявляет это допустимым.
Из спецификации C++11 5.7.6:

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

... далее в этом абзаце...

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

Результат p2 - p1 имеет тот же тип, что и std::ptrdiff_t, но ничего не говорит о том, что компилятор не может определить его как

namspace std {
    typedef ptrdiff_t int;
}

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

Что касается вашего второго вопроса, формулировка в спецификации С++ 5.7.6 предполагает, почему они работают так, как работают. Формулировка указывает на то, что авторы языка хотели, чтобы арифметика указателей поддерживала быструю арифметику при работе с массивом. Соответственно, определенный результат вычитания указателя дает удобные результаты при использовании в контексте манипулирования массивом. Вы можете создать язык, в котором разница между двумя указателями равна разнице между их адресами памяти в байтах, и у вас будет согласованный рабочий язык. Тем не менее, хороший язык всегда приносит наибольшую отдачу. Авторы считали, что чистое манипулирование массивами более ценно, чем возможность получить разницу в байтах. Например:

double* findADouble(double* begin, double* end, double valueToSearchFor)
{
    for (double* iter = begin; iter != end; iter++) {
        if (*iter == valueToSearchFor)
            return iter;
    }
    return 0;
}

В нем должен быть sizeof, и нам пришлось бы использовать += вместо ++.

double* findADouble(double* begin, double* end, double valueToSearchFor)
{
    for (double* iter = begin; iter != end; iter += sizeof(double)) {
        if (*iter == valueToSearchFor)
            return iter;
    }
    return 0;
}

Также стоит отметить в их решении: когда правило было создано на C, оптимизирующие компиляторы не очень хорошо справлялись со своей работой. iter += sizeof(double) может быть скомпилирован в гораздо менее эффективный ассемблерный код, чем ++iter, даже если эти две операции в основном делают одно и то же. У современных оптимизаторов с этим проблем нет, но синтаксис остается.

person Cort Ammon    schedule 02.09.2013