2015-12-23 4 views
0

У нас есть много устаревшего кода на C++ с функциями, которые принимают переменное количество аргументов указателя. В каждом случае нулевые указатели сигнализируют конец списка аргументов. В устаревшем коде для нулевого указателя используется буква 0. Поскольку функции списка аргументов переменных C++ не прототируют типы аргументов переменной, завершающий аргумент передается как целое число 0. В некоторых 64-разрядных средах целые числа являются 32-разрядными, а указатели - 64-разрядными. Существует ли риск того, что вызываемая функция увидит ненулевой указатель (т. Е. 32-разрядное слово младшего разряда равно нулю, но 32-разрядные разряды высокого порядка содержат мусор)? Изменение всех аргументов 0 в nullptr, по-видимому, было бы правильным подходом. Мой вопрос: это необходимо? т. е. существует ли реальный риск ошибки, если мы этого не сделаем?завершение переменных аргументов с нулевым указателем

class mytype; 
void foo(mytype*, ...); 

mytype a, b, c; 
foo(&a, &b, &c, 0); //is this ok ? 
foo(&a, &b, &c, nullptr); //or must it be so ? 
+1

Примечание: 'nullptr' становится значением' void * 'при передаче как нетипизированный аргумент, а не' int'. Это отличается от передачи '0', которая (и остается)' int'. –

+1

Будет ли это решение для создания шаблона вариационной функции? Вы можете сохранить семантику вызова. –

+0

Я бы добавил к предложению Керрека, что вы можете переименовать существующую функцию 'foo()', например. 'foo_impl()', а затем создать либо шаблон, либо кучу перегрузок (сколько раз вам нужно количество аргументов?), которые затем делегируют 'foo_impl()' с правильными типами параметров. Вставляя это, ваш код не должен даже расти в размерах или замедляться. –

ответ

3

У вас есть miseducated ваших пользователей. Если каждый аргумент распаковывается как va_arg(ap, mytype*), пользователи должны сигнализировать о завершении списка (mytype*)(0), а не 0. У вашего существующего кода всегда было неопределенное поведение. Даже nullptr не подходит, потому что он будет передан как void*, когда это нетипизированный аргумент, поэтому правильный аргумент равен static_cast<mytype*>(nullptr).

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

+0

Знаете ли вы о текущих машинах, где представление void * и mytype * отличается? (Я использовал машины в прошлом, где они есть, но я думаю, что даже это сработало бы). Другими словами, хотя nullptr технически по-прежнему неправы, он почти всегда будет работать. –

+2

@MartinBonner: Я не хочу записывать утверждения обо всех аппаратных средствах для всех будущих времен. Не пишите код с UB: -S –

+2

«Можете ли вы представить себе, как мой код с UB может выйти из строя?» Ну, может быть, я не могу. Но мое воображение довольно ограничено, и я могу дать вам длинный список примеров людей, которые были сильно сожжены, сделав подобные предположения. Иногда они были правдивыми, когда они были сделаны. –

-3

По-моему, использование 0 - это не переносное решение. В зависимости от компилятора он может быть либо задан для типа данных элемента массива, либо как int (0).

Таким образом, лично я бы рекомендовал всегда использовать NULL в этом случае.

(частичное) решение
+1

Литеральный '0' имеет тип' int', а 'int' больше не продвигается при передаче в многоточие. Нет ничего двусмысленного. –

+1

Это не зависит от компилятора (если он полностью не сломан), он обязательно передается как 0. Кроме того, в C++ NULL должен расширяться до интегральной константы с номером 0. Обычно это 0, но это может быть (3- 15/5). То, что не может быть, - это что-то отличное от void *. –

+0

Кроме того, я нашел удобство обслуживания кода намного лучше, используя NULL в этом случае. Если в будущем тип данных элементов массива изменяется кем-либо без соответствующей адаптации кода, и он иногда становится целым типом данных, вы получите подсказку компилятора с использованием NULL или ошибку времени выполнения, используя 0. – dmi

0

Потенциальные будет использовать перегрузку операторов:

void saferfoo(mytype* a0, mytype* a1, ..., mytype* an, int zero) { 
    foo(a0, a1, ..., an, static_cast<mytype*>(nullptr)); 
} 

здесь ... является не многоточие от языка C++, но многоточие из нашего человеческого языка.

Вам нужно будет определить функцию для каждого количества аргументов, присутствующих в коде, вам также придется перекомпилировать соответствующий код. Поскольку это только прошлое/настоящее неправильное использование foo, которое затронуто, вы можете сделать это и использовать бейсбольную биту, чтобы справиться с будущим неправильным использованием foo.

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