#c #testing #null #compiler-optimization #zero
Вопрос:
Я хочу адаптировать фрагмент кода для работы в системах, где нулевой указатель не равен 0 в своем физическом представлении. До сих пор в жизни я полностью игнорировал различие между нулем и 0 — и до сих пор сегодня я не работаю ни над одной системой, которая имеет это различие на практике, — но все же.
Итак, я пошел и снова прочитал часто задаваемые вопросы по C о нулях, и особенно 5.19, о том, как получить указатель «фактического нуля». Ну, я хочу сделать это в модульном тесте, который я пишу, но — я беспокоюсь, что некоторые из этих «трюков» на самом деле будут оптимизированы разумным компилятором.
Например, используя memset()
и `memcmp(): И GCC, и Clang видят это насквозь, так что это:
#include <stdio.h>
#include <string.h>
int foo(void* p)
{
void* ref;
memset(amp;ref, 0, sizeof(ref));
return (memcmp(amp;p, amp;ref, sizeof(p)) == 0 ) ? 123: 456000;
}
int bar(void* p)
{
memset(amp;p, 0, sizeof(p));
return (p == 0) ? 123: 456000;
}
становится, скажем:
foo: # @foo
test rdi, rdi
mov ecx, 123
mov eax, 456000
cmove eax, ecx
ret
bar: # @bar
mov eax, 123
ret
Итак, кроме компиляции без оптимизации, как я могу надежно «принудительно обнулить» указатель?
Примечание: Спасибо @Lundin за указание на то, что, если я использую memset()
, я также, вероятно, захочу использовать memcmp()
).
Комментарии:
1.
memset_s
? Это единственный стандартный способ, несмотря на то, что он необязателен. godbolt.org/z/evW7Tb3f82. @Рассказчик-UnslanderMonica: Интересный вариант. Вероятно, тоже стоит ответа. Однако… существует хорошая корреляция между наличием ненулевого нуля и отсутствием поддержки C11 🙁
3.
bar
Оптимизированный код asm логичен, какp
и локальный аргументbar
, и вы ничего не делаете с ним, кроме как устанавливаете его равным нулю и тестируете его против нуля. Может быть, вы сможете получить более значимые результаты, если вернетесьp
и назначитеbar
возврат вызывающей локальной/статической переменной ?4. Вы можете вызвать внешнюю функцию-оболочку, чтобы предотвратить
memset
оптимизацию вызова.5. Он оптимизируется, потому что компилятор использует ноль в качестве представления нулевого указателя… это правильная и четко определенная оптимизация.
Ответ №1:
memset
это правильное решение. И поскольку вы компилируете с компиляторами, которые используют ноль в качестве реализации нулевого указателя, может быть довольно сложно заблокировать оптимизацию.
Однако у тебя есть ошибка.
- Проверка указателя, например,
if(p)
проверяет, является ли этот указатель нулевым указателем. - Сравнение
p == 0
p == (void*)0
илиp == NULL
проверяет , является ли этот указатель нулевым указателем.
Поэтому p == 0
не проверяет, равно ли внутреннее представление нулю. Вам придется использовать что-то вроде:
void* ref;
memset(amp;ref, 0, sizeof ref);
if(memcmp(amp;p, amp;ref, sizeof p)==0)
Что опять же, вероятно, не приведет ни к чему революционному в системе, использующей ноль для представления нулевого указателя.
Комментарии:
1. Я изменяю рассматриваемый код, чтобы использовать memcmp (), поскольку в этом случае также происходит оптимизация, и вопрос касался не моего примера кода, а общей проблемы.
2. @einpoklum Да, но memset-это правильный способ, и оптимизация правильна, так как компилятор, который вы использовали, использует нули для представления нулевого указателя. Чтобы извлечь из этого что-то еще, вам нужно использовать перекрестный компилятор для системы, которая не использует нули для нулевого указателя. В этом случае оптимизация была бы неправильной.
3. И в этой экзотической системе, которая использует, скажем, 0xFFFF для представления нулевого указателя, затем
p = 0;
установитp
значение 0xFFFF.4. Я предположил
p == 0
, что проверка OP была преднамеренной. Т. е. она проверяет, является ли указатель нулевым после установки его байтового представления для всех нулей. Интересно порассуждать о том, чтоp == 0
даст для всех нулей ненулевое значение указателя. Я предполагаю, что результат должен быть равен 0 для согласованности, если только реализация не имеет нескольких представлений null, одно из которых-все нули.