Что означает "if (* ((void **) & (_ ptr))!= (Void *) NULL)" проверить?
Я натолкнулся на часть кода следующим образом:
/* Allocate memory for _ptr */
if(*((void **) &(_ptr)) != (void *) NULL)
{
/* free _ptr */
}
Чем он отличается от следующего?
/* Allocate memory for _ptr */
if (_ptr != NULL )
{
/* free _ptr */
}
EDIT: _ptr может быть любым типом, на самом деле это макрос следующим образом:
#define RETURN_MEM_CHK(_ptr) \
{if(*((void **) &(_ptr)) != (void *) NULL){/* free _ptr */}
Извините, что вы путаете.
Ответы
Ответ 1
Для чего это стоит:
Я не мог понять это самостоятельно, поэтому я обсуждал это с моим компилятором, и он говорит, что условие эквивалентно * на if (_ptr != NULL)
:
% gcc -Wall -O2 -g -c convoluted.c; objdump -d -M intel -S convoluted.o
convoluted.o: file format elf32-i386
Disassembly of section .text.startup:
00000000 <main>:
#include <stdlib.h>
int main(void)
{
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 e4 f0 and esp,0xfffffff0
6: 83 ec 10 sub esp,0x10
void* _ptr=malloc(1024);
9: c7 04 24 00 04 00 00 mov DWORD PTR [esp],0x400
10: e8 fc ff ff ff call 11 <main+0x11>
if(*((void **) &(_ptr)) != (void *) NULL)
15: 85 c0 test eax,eax
17: 74 08 je 21 <main+0x21>
{
free(_ptr);
19: 89 04 24 mov DWORD PTR [esp],eax
1c: e8 fc ff ff ff call 1d <main+0x1d>
}
return 0;
}
21: 31 c0 xor eax,eax
23: c9 leave
24: c3 ret
% gcc -Wall -O2 -g -c kindanormal.c; objdump -d -M intel -S kindanormal.o
kindanormal.o: file format elf32-i386
Disassembly of section .text.startup:
00000000 <main>:
#include <stdlib.h>
int main(void)
{
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 e4 f0 and esp,0xfffffff0
6: 83 ec 10 sub esp,0x10
void* _ptr=malloc(1024);
9: c7 04 24 00 04 00 00 mov DWORD PTR [esp],0x400
10: e8 fc ff ff ff call 11 <main+0x11>
if(_ptr != NULL)
15: 85 c0 test eax,eax
17: 74 08 je 21 <main+0x21>
{
free(_ptr);
19: 89 04 24 mov DWORD PTR [esp],eax
1c: e8 fc ff ff ff call 1d <main+0x1d>
}
return 0;
}
21: 31 c0 xor eax,eax
23: c9 leave
24: c3 ret
Примечание
Сама проверка тоже не нужна, как указывали другие. Более естественным способом было бы просто:
свободный (_ptr); _ptr = NULL;
* На этой машине с этой ОС и этой версией GCC и этим CPU и только тогда, когда звезды выравниваются правильно...
Ответ 2
Один пример, где он мог бы дать разные результаты (и сделал, в моей конкретной системе, когда я только что попробовал):
int _ptr = 0;
int whatever = 17;
if (*((void **) &(_ptr)) != (void *) NULL) {
printf("Not equal (1)\n");
}
if (_ptr != NULL) {
printf("Not equal (2)\n");
}
Первая версия делает вид, что целочисленная переменная _ptr является указателем на пустоту и обращается к ее памяти, как если бы она была указателем на пустоту. На моем компьютере, где ints - 32 бита, а указатели - 64 бита, это означает чтение памяти вне переменной. Это, конечно, поведение undefined, и в этом случае это привело к условию, равному true.
Вы получите похожие результаты, если _ptr - это указатель типа, отличного от void *, в системе, где этот тип указателя имеет разный размер или представлен иным образом указатель void.
Ответ 3
Ну, какая разница зависит от типа _ptr
.
if (_ptr != NULL )
не работает, если _ptr
не является типом указателя (и NULL
- это константа нулевого указателя, которая включает в себя преобразование в void*
, она может работать, если NULL
- это просто целочисленная константа со значением 0 даже если _ptr
не имеет типа указателя).
Если _ptr
имеет тип указателя, if (_ptr != NULL )
сравнивает _ptr
с нулевым указателем. Simples.
if(*((void **) &(_ptr)) != (void *) NULL)
если он не вызывает поведение undefined, интерпретирует байты sizeof (void*)
, начиная с адреса &_ptr
, как void*
и сравнивает результат этой переинтерпретации с нулевым указателем типа void*
.
Он мог бы вести себя иначе, если _ptr
- значение типа указателя с другим представлением, чем void*
.
Он работает, если _ptr
не относится к типу указателя.
Во всех разумных ситуациях, однако, это был бы более сложный способ сказать
if ((void*)_ptr != NULL)
Ответ 4
*((void **) &(_ptr)) != (void *) NULL
Эта проверка также работает там, где _ptr
не является типом указателя, например. если _ptr
был uintptr_t
или что-то еще. В этом случае простое сравнение _ptr != NULL
может не обрабатывать системы, где значения нулевого указателя не имеют представления "все ноль".
Конечно, чтение целого числа как указателя не переносимо, так что этот код обрабатывает один набор проблем для другого набора проблем.
Ответ 5
*((void **) &(_ptr)
выражение выполняет переинтерпретацию сырой памяти области памяти, занятой объектом _ptr
. Первые sizeof(void *)
байты интерпретируются как объект типа void *
. Между тем сам объект _ptr
может иметь абсолютно любой тип. Естественно предположить, что он предназначен для объекта того же размера, что и void *
(или большего размера).
Например, _ptr
может быть объектом некоторого целочисленного типа соответствующего размера. Очевидно, что в этом случае if (_ptr == NULL)
может просто отказаться от компиляции в реализации, которая определяет NULL
как (void *) 0
.
Ответ 6
Если тип _ptr
имеет void*
, тогда код нарушает строгие правила псевдонимов и имеет поведение undefined:
Объект должен иметь сохраненное значение, доступное только с помощью значения lvalue выражение, которое имеет один из следующих типов: 76)
- совместимый тип с эффективным типом объекта,
- квалифицированная версия типа совместимый с эффективным типом объекта,
- тип, который является подписанный или неподписанный тип, соответствующий эффективному типу object - тип, который является подписанным или неподписанным типом, соответствующим квалифицированный вариант эффективного типа объекта,
- anaggregate или тип объединения, который включает один из вышеупомянутых типов среди его членов (в том числе, рекурсивно, сообщено о суммировании или содержащее объединение), или
- тип символа.
В код _ptr
открывается через lvalue типа void*
, который совместим только с самим собой, поэтому ни одно из указанных выше условий не является истинным.
Есть хороший шанс, что он работает как _ptr != NULL
, но использование такого кода по-прежнему является ужасной практикой.