2013-03-12 3 views
1

здесь программа:Почему этот оператор печати изменяет предыдущий указатель?

#include <stdio.h> 

main() { 
    int * i; 
    int * j; 
    printf("%d\n", i); 
    printf("%d\n", j); 
} 

Я скомпилировал и запустил его, и выход был:

888086464 
0 

Однако, если я закомментировать второй PRINTF

#include <stdio.h> 

main() { 
    int * i; 
    int * j; 
    printf("%d\n", i); 
    //printf("%d\n", j); 
} 

затраченные положить:

0 

Мне интересно, почему второй printf меняет указатель i.
И, как C инициализирует указатели? Насколько я знаю, если указатель не инициализирован, он будет иметь значение Null, равное 0, правильно? Почему в первом выпуске i был инициализирован?

EDIT: После предложения от вас, ребята, я сделал некоторые изменения:

#include <stdio.h> 

main() { 
    int * i; 
    int * j; 
    printf("%p\n", i); 
    printf("%p\n", j); 
} 

, который дает

0x7fff67a361b0 
(nil) 

Но когда я закомментировать второй Printf

#include <stdio.h> 

main() { 
    int * i; 
    int * j; 
    printf("%p\n", i); 
    //printf("%p\n", j); 
} 

выход равен нулю. Мне любопытно, почему в первой версии с двумя printf только j ничтожно, а я нет.

+0

Кроме использования неинициализированных данных (которые не определены), вы передаете указатели на 'printf()' в формате '% d', который также не определен. Вам было бы лучше использовать '% p' или явно преобразовать из указателя в' int'. –

ответ

2

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

I подозреваемый, что во второй версии, j не создается вообще (поскольку он нигде не используется), поэтому в слоте создается i, который использовался бы для j. По какой-то причине, ваш стек настроен таким образом, что верхний 64 бит 0x0000000000000000 следует 0x00007fff67a361b0

IOW, в первой версии, ваш стек выглядит

Item  Address   00 01 02 03 04 05 06 07 
----  -------   -- -- -- -- -- -- -- -- 
    j  0x8000    00 00 00 00 00 00 00 00 
    i  0x8008    00 00 7f ff 67 a3 61 b0 

, тогда как во второй версии, он выглядит такие как

Item  Address   00 01 02 03 04 05 06 07 
----  -------   -- -- -- -- -- -- -- -- 
    i  0x8000    00 00 00 00 00 00 00 00 
      0x8008    ?? ?? ?? ?? ?? ?? ?? ?? 

(значения адреса приведены только для иллюстрации и не соответствуют какой-либо реальной архитектуре).

Для смешков, измените операторы печати, чтобы быть

printf("%p: %p\n", &i, i); 
printf("%p: %p\n", &j, j); 

в первой версии и

printf("%p: %p\n", &i, i); 

во втором. Я поставил деньги, что значение, напечатанное для &i (адрес переменной i) во второй версии будет таким же, как значение, напечатанное для &j (адрес переменной j) в первой версии.

ПРИМЕЧАНИЕ - это не имеет ничего общего с C языка, и все, что с вашей конкретной реализации (компилятор, линкер и т.д.). Я не убежден, что вы вызываете неопределенное поведение (вы не получаете доступ к какой-либо памяти с помощью этих недействительных указателей), но вы видите, что они явно не инициализируют значения указателя.

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

+0

Это именно то объяснение, которое я ищу, большое спасибо! – octref

+0

Я просто понял, что нет стека между двумя прогонами, кроме первого элемента, всего 0. Возможно, вы захотите запустить отладчик для проверки стека для двух версий. Но опять же, это все о вашей конкретной реализации *, а не вообще C. Нет никакой гарантии, что один и тот же код будет вести себя одинаково с другим компилятором. –

3

Если вы не инициализировали указатель, он может иметь любое значение.

Вы должны инициализировать их. В противном случае вы получите undefined behavior.

Кроме того, правильный способ печати указателей:

printf("%p", pointer); 
4

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

+0

Почему я указываю на значение для мусора, а j нет? в первом выводе. – octref

+2

Они оба значения мусора. Просто потому, что один из них оказывается «0», это не значит, что он еще не мусор. – Xymostech

+0

Но каждый раз, когда я запускаю его, второй всегда будет 0. Я не думаю, что это совпадение, не так ли? – octref

0

«Не инициализировано» очень отличается от «инициализировано до 0» (или NULL).

Переменные i и j не инициализированы. У них осталось то, что у него есть в памяти.

Когда вы закомментируете второй printf, переменная j больше не используется. Вероятно, он оптимизирован компилятором (что совсем не определено). В таком случае i может находиться в другом месте, имеющем другое значение .

Это фактически не определено поведение. Мой последний раздел - это чистое предположение, вы никогда не можете быть уверены.

+0

Но с двумя printf, где i и j оба используются, почему один может быть nil и один имеет адрес? – octref

0

Указатели могут содержать что угодно. C не инициализирует локальные (автоматические) переменные, если вы не сообщите об этом.Если вы определяете их как статические, они инициализируются с помощью NULL.

Также вы должны использовать %p для печати указателей, а не %d.

0

вы получите неопределенное поведение если вы не инициализировать адрес указателя

здесь вы печатаете адрес указателя как междунар и это не правильно, вы должны использовать "%p" вместо "%d"

+0

'% u' и'% x' ожидают 'unsigned int' и так же ошибочны, как'% d'. –

+1

@EricPostpischil Доступ к неопределенной памяти вызывает неопределенное поведение на C90 и рассматривается как неопределенное поведение современных компиляторов независимо от точной формулировки более поздних стандартов. http://kqueue.org/blog/2012/06/25/more-randomness-or-less/ –

+0

@EricPostpischil (1) 1.6 ОПРЕДЕЛЕНИЯ ТЕРМИНОВ [...] Неопределенное поведение - поведение при использовании непереносимого или ошибочную конструкцию программы, ошибочных данных или неопределенно значимых объектов, [...] –

0

And, how does C initialize pointers?

C не делает, вы делаете это.

As far as I know, if a pointer is not initialized, it would have value Null which is equal to 0, correct?

Неправильно. Если он не инициализирован, он не имеет определенных значений. Это будет мусор. Печать неинициализированных переменных приведет к мусору, поэтому вы не можете ожидать выход.

Why in the first output, the i was initialized?

Это не было, это просто случилось, указывая на 0.

Side Примечание: вы должны быть печать с %p, когда вы печатаете указатели.

+1

Хороший вопрос о '% p'! Я забыл об этом. –

0

C только автоматически инициализирует static и глобальные переменные (глобальные переменные объявляются за пределами функции) до нуля. Автоматические переменные (это то, что вы называете двумя вашими указателями) не инициализируются автоматически, поэтому вы печатаете «шум стека» (т. Е. Какие бы значения не находились в стеке, когда ваша функция была инициализирована). . для запуска вашей программы Если вы хотите, чтобы быть инициализированы NULL вам необходимо сообщить компилятору:

int *i = NULL; 
int *j = NULL; 
Смежные вопросы