2016-10-13 3 views
-2

Я новичок в C и задаю некоторые вопросы о указателе.Различия в инициализации указателей C

Вопрос 1 В чем отличия b/w следующие два? Какой способ лучше инициализировать указатель и почему?

int *p=NULL; 
int *p; 

#include <stdio.h> 
void main() 
{ 
    char *s = "hello"; 
    printf("%p\t%p",s); 
    //printf("%p\t%p",&s) it will give me unpredictable result every time 
    //printf("%p\t%p",(void *)&s) it will be fine 
    //Question3: why? 
} 

Вопрос 2: Я стараюсь Google, что делает %p. Согласно моему чтению, предполагается напечатать указатель. Это означает, что он печатает адрес указателя?

+0

Он печатает адрес, содержащий указатель, а не адрес переменной указателя. – Barmar

ответ

1

Вопрос 1, это определения указателя p. Один инициализирует указатель на NULL, другой оставляет его неинициализированным (если это локальная переменная в функции, а не глобальная переменная, глобальные переменные по умолчанию инициализируются 0). Инициализация с помощью NULL может быть хорошей, или это может быть плохо, потому что компилятор может предупредить вас об использовании неинициализированных переменных и помочь вам найти ошибки. С другой стороны, компилятор не может обнаружить все возможное использование неинициализированной переменной, поэтому инициализация до NULL в значительной степени гарантирует получение ошибки сегментации, если она используется, которую вы можете легко поймать и отладить отладчиком очень легко. Лично я бы всегда начинал инициализацию, когда определялась переменная, с правильным значением, если это возможно (если инициализация слишком сложна для одного оператора, добавьте вспомогательную функцию для получения значения).

Вопрос 2, %p печатает значение адреса, переданное printf. Так printf("%p", pointer); получает значение переменной pointer, и оно печатает это, в то время как printf("%p", &pointer); (обратите внимание, что дополнительный & там) получает переданный адрес переменной pointer, и он печатает это. Точный числовой формат %p - это реализация, она может быть напечатана просто как простой номер.

Вопрос 3 о неопределенном поведении, поскольку в строке формата есть больше предметов, чем то, что вы фактически переходите на printf. Короткий ответ: поведение не определено, нет «почему». Более длинный ответ: запустите приложение с помощью отладчика машинного кода и проследите за выполнением в режиме разборки, чтобы узнать, что на самом деле происходит, чтобы понять, почему. Обратите внимание, что результаты могут отличаться для разных прогонов, и поведение может отличаться в зависимости от отладчика и работает нормально, потому что память может иметь разные значения байтов в разных прогонах по разным причинам.

1

1) Во-первых, инициализация (с NULL в данном случае) вторая является лишь декларацией p как указатель на int, не начальное значение не присваивается p в этом случае. Вы всегда должны предпочесть инициализацию для предотвращения неопределенного поведения.

2) Вы должны указать void* при использовании %p, чтобы распечатать указатель (будьте осторожны, что вы используете его слишком много раз в своем спецификаторе формата). Адрес памяти, на который печатается p точек.

+0

Почему вы рекомендуете кастинг на 'void *'? (это честный вопрос, если есть причина, я тоже хотел бы знать) – hyde

+1

@hyde Поскольку 'void *' - это то, что ожидает спецификатор формата '% p' –

+0

После расследования, кажется, ничего не подразумевается указатели при передаче функции varargs, поэтому приведение к 'void *' - это правильная вещь, связанная с'% p'. – hyde

0

1)

int *p = NULL 

определяет и инициализирует указатель 'р' к NULL. Это правильный способ инициализации указателей, чтобы получить «Seg Fault», если вы забыли назначить действительный адрес этому указателю позже.

int *p 

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

2) «% p» печатает адрес, на который указывает указатель. Поскольку указатель содержит адрес, тогда «% p» печатает этот адрес.

printf("%p\t%p",s); 

Так что первый «% р» напечатает адрес, где указатель «S» точки, это адрес, который хранит строку «Hello». Однако обратите внимание, что вы используете дважды «% p», но вы предоставляете только один указатель для печати своего адреса! Большинство компиляторов не будут кричать об этом, потому что это неэффективно; однако старайтесь избегать этого.

+0

«Большинство компиляторов не будут кричать об этом, потому что это неэффективно» ... Если ваш компилятор не кричит о предупреждении об обнаруженных ошибках строки формата 'printf' и т. Д., То вам нужно подняться до уровня предупреждения! '-Wall -Wextra' является хорошим началом для * gcc * и * clang *. Если ваш компилятор не может предупредить о таких вещах, вам следует серьезно подумать о переключении компиляторов, по крайней мере, для разработки, или, по крайней мере, запустить отдельный статический анализатор. Furhtermore, это страшно Неопределенное поведение, и вы не можете действительно полагаться на то, что это бесполезно. – hyde

+0

Извините, я ошибся с другим случаем. – user1888149

+0

Эта строка даст вам ошибку, компилятор попытается напечатать второй «% p» и не найдет указателя на печать, чтобы он выдавал ошибку, а не предупреждение! – user1888149

0

Ответ1:

int *p=NULL; 

р является указателем на Int переменной инициализируется с NULL. Здесь NULL означает, что указатель p не указывает на любую допустимую ячейку памяти.

int *p; 

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

ANSWER2:

Он печатает содержимое указателя.Я имею в виду базовый адрес строки «hello»

+0

Я ясно объяснил контекст как локальную переменную. внимательно прочитайте. –

+0

«Это означает, что если вы попытаетесь использовать (разыменовать) ** инициализированный ** указатель, вы имеете неопределенное поведение». сделайте исправление, оно должно быть «неинициализировано» –

+0

@Bolov Изменения включены. –

0

Главное отличие состоит в том, что в *p = NULL NULL является заранее определенным и стандартным «местом», где указывает указатель. Чтение из Википедии,

The macro NULL is defined as an implementation-defined null pointer constant, 
which in C99 can be portably expressed as the integer value 0 
converted implicitly or explicitly to the type void*. 

Это означает, что «ячейка памяти» называется p содержит MACRO значение NULL. Если вы только что пишете int *p, вы называете ячейку памяти именем p, но эта ячейка пуста.

Смежные вопросы