2015-01-24 2 views
1
#include<cstdio> 
int main(){ 
    int a=10,b=20; 
    int *p=&a; 
    *(p-1)=100; 
    printf("%d, %d, %d\n",a,b,*(p-1)); 
// printf("%d, %d, %d\n",&a,&b,(p-1)); 
    return 0; 
} 

Почему первый printf (строка 6) показывает другой результат, независимо от того, является ли второй комментарий (строка 7) в комментариях или нет? Я использую компилятор C (TDM-GCC 4.8.1 64bit)Арифметика указателей: странный результат в программе C

+7

'* (п-1) = 100;' вызывает неопределенное поведение, – ouah

+0

Что этот код должен делать, в своем уме? –

ответ

1

p не указывает на массив, поэтому выражение *(p-1) имеет undefined behaviour. Это означает, что, как только *(p-1)=100 был выполнен, технически программа может вести себя так, как ей нравится.

Что на практике случается, так это то, что вы перезаписываете некоторую память в стеке, что может привести к возникновению всех видов побочных эффектов.

Если вы отрицательный код Valgrind, то этот инструмент отметит это.

+1

Если 'p' * * указывал на (первый элемент) массив, не было бы еще неопределенным поведением (доступ к границам внешнего массива)? – Dmitri

+1

@Dmitri: Это было бы. В логических терминах это звучит так, будто вы можете ввести в заблуждение [необходимость с достаточностью] (http://en.wikipedia.org/wiki/Necessity_and_sufficiency#Relationship_between_necessity_and_sufficiency). – NPE

+1

«Поскольку' p' не указывает на массив », подразумевается, что неопределенное поведение существует, потому что' p' не указывает на массив, что неверно. Если 'p' указывает на массив, проблема все равно будет. – Dmitri

1

Вы кодируете декремент, который p указывает и хранит там 100. Поскольку p инициализируется адресом a, вы сохраняете 100 на некотором случайном адресе памяти. Я думаю, что вы хотите:

*p = 100; 
+0

Я не думаю, что «случайный» означает то, что вы думаете, что это значит. –

1

Вы пытаетесь изменить местоположение памяти вне объекта a, из которого получен указатель p. Результат undefined поведение что означает любой результат возможен, даже казалось бы, невозможный.

Единственный способ дать окончательный ответ на ваш вопрос - проверить вывод ассемблера из вашего компилятора (если такой выход можно получить) или сгенерированный машинный код. В этом случае объект a будет обычно быть выделен в стеке, а наличие другого printf звонка , вероятно, изменяет способ, которым компилятор выделяет элементы в стеке внутри одного и того же кодового блока.

1

Ответы @valtah и @NPE верны. Если вы посмотрите еще более подробно о том, что это происходит вы можете понять, почему:

Если мы посмотрим на кадр стека (или запись активации) для этого исполнения:

 
---------------------------------------------------------- 
    SP| saved state | return address | a | b | p | 
-----------------------------------------------------|---- 
            ^   | 
             |    | 
             .-------------' 
             &a 

Вы увидите, что & в является адресом, где a хранится в стеке, и это также назначается p. Когда вы используете значение (p-1), оно может вычислять позицию в другом месте в стеке. (Хороший компилятор знал бы, что это не указатель массива и дает семантическую ошибку на этом этапе.) Многие компиляторы просто вычислили адрес. Когда вы присваиваете ему значение, возможно, что вы меняете значение одного элемента вдали от адреса a. Это может быть b, или это может быть Обратный адрес. Затем это изменяет состояние выполнения программы.

Это именно тот механизм, в котором используются атаки на инъекции кода для управления системами. Это код нарушения безопасности.

Теперь вопрос приходит на ум. Откуда появился этот код? Вы пишете его или читаете где-то. Это, безусловно, говорит специалистам о том, какой код вы работаете с ...

:-)