Делает (ptr && *ptr) хороший способ проверить, есть ли какое-либо значение, назначенное для ptr? мой просто продолжает падать
Я не совсем уверен, что использование ptr&&(*ptr)
Сначала я не был уверен, проверяется ли это на NULL ptr или на наличие заданного значения.
Но теперь, после запуска приведенного ниже кода, я обнаружил, что в обоих случаях он просто не работает: произошла ошибка во время выполнения!
Может кто-нибудь объяснить, что внутри происходит противная вещь?
GCC 5.3.0
#include <stdio.h>
int main(void){
int* ptr=NULL;
// ptr=NULL;
int res = (ptr && *ptr);
//assigning NULL to *ptr or ptr address results in runtime error. What's happening?
//compiled with gcc.exe (GCC) 5.3.0 on windows
printf("address: %d \n val: %d \n",ptr,*ptr);
printf("isNullptr? ptr&& *ptr %d",res);
return;
}
5 ответов
Следующая строка полностью в порядке, так как короткое замыкание предотвращает ptr
от разыменования, если оно равно NULL
,
int res = (ptr && *ptr);
Но следующий код безоговорочно разыменовывает его, что приводит к краху.
int* ptr=NULL;
// ...
printf("address: %p \n val: %d \n",(void *)ptr,*ptr);
// ^~~~
Также обратите внимание, что правильный способ печати указателя - использование %p
вместо %d
и указатель должен быть приведен к void *
, Я изменил ваш printf()
соответственно.
printf("address: %d \n val: %d \n",ptr,*ptr);
Вы пытаетесь разыменовать нулевой указатель. Это неопределенное поведение.
От n1256 - §6.5.3.3:
Среди недопустимых значений для разыменования указателя с помощью унарного оператора * есть нулевой указатель, адрес, неправильно выровненный для типа объекта, на который указывает объект, и адрес объекта после окончания его времени жизни.
Дела
int * ptr = NULL
...
printf("address: %d \n val: %d \n",ptr,*ptr);
результаты в:
Первый
ptr
инициализируется с помощью константы нулевого указателя. Для этого стандарт C гарантирует, чтоptr
не указывает на какой-либо действительный объект. (* 1)Стандарт С11 (проект) 6.3.2.3/3:
Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно сравнивается с неравным указателем на любой объект или функцию.
Затем во втором заявлении с выполнением
*ptr
разыменовывается, то есть он пытается получить доступ к тому, на что он указывает, что является недопустимым (см. 1. выше), что приводит к неопределенному поведению, с тех пор может произойти все, что угодно, включая сбой программы.Стандарт С11 (проект) 6.5.3.2/4:
Если неверное значение было присвоено указателю, поведение унарного
*
Оператор не определен.
Технически, у вас неопределенное поведение. Это очень плохо.
(помните, что язык программирования - это спецификация, написанная в каком-то отчете, а не программное обеспечение. Для C11 читайте n1570)
На практике вы получаете ошибку сегментации. Вы разыменовываете - в вашем первом звонке printf
- (нулевой) указатель, который всегда находится за пределами вашего виртуального адресного пространства.
Вы должны скомпилировать все предупреждения и отладочную информацию, например, с gcc -Wall -g
при использовании GCC. Тогда используйте отладчик (gdb
) и детектор утечки памяти, как Valgrind...
Вы должны прочитать намного больше. Внимательно прочитайте несколько книг по программированию (возможно, начните с SICP) и несколько книг по программированию на Си. Вы также можете прочитать об операционных системах.
Как вы думаете *ptr
(в принтф) делает? Разыменовывает указатель, который был назначен с NULL
в инициализации. Если вы передадите NULL функции, ожидающей ненулевое значение, ваша программа вызовет неопределенное поведение. Правильный способ печати адресов - это использование %p
спецификатор вместе с ptr-to-void:
if (ptr != NULL)
printf ("Pointer ptr points to address %p\n", (void *)ptr);
else
printf ("ptr is NULL\n");