Делает (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);

результаты в:

  1. Первый ptr инициализируется с помощью константы нулевого указателя. Для этого стандарт C гарантирует, что ptr не указывает на какой-либо действительный объект. (* 1)

    Стандарт С11 (проект) 6.3.2.3/3:

    Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно сравнивается с неравным указателем на любой объект или функцию.

  2. Затем во втором заявлении с выполнением *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");
Другие вопросы по тегам