C printf спецификатор с переменными аргументами.В какой момент неопределенное поведение проблематично? - PullRequest
1 голос
/ 13 марта 2019

https://godbolt.org/z/qZVO3a

Это минимальное воспроизведение предупреждений, которые я вижу. Очевидно, что UB может быть плохим, но я думаю, что хотя многие из приведенных ниже ситуаций в порядке, есть некоторые очень неприятные применения, и мне нужно определить, какие из них требуют корректирующих действий.

#include <stdarg.h>
#include <stdio.h>
#include <limits.h>

typedef struct _thing {

    char  first[4];
    char  second[10];
    char  last[111];
}THING;


void custom_printf(char* _format, ...) __attribute__((format(printf, 1,2)));
void custom_printf(char* _format, ...) 
{
    // get buffer from some source
    char buffer[1024];
    va_list ap;
    va_start(ap, _format);
    vsnprintf(buffer, 1024, _format, ap);
    va_end(ap);
    // use buffer for some purpose

}

int main(){

    custom_printf("HI THERE%d");
    custom_printf("HI THERE", 1);
    custom_printf("val: %d", (void*)0);
    custom_printf("val: %p", 0);
    custom_printf("val: %lld", 1);
    custom_printf("val: %s", (THING){"A", "AA", "CCCC"});
    custom_printf("val: %0.30s","HI");
    custom_printf("val: %d",LLONG_MAX);
}

См. Предупреждения включают:


<source>: In function 'main':

<source>:26:5: warning: format '%d' expects a matching 'int' argument [-Wformat]

<source>:27:5: warning: too many arguments for format [-Wformat-extra-args]

<source>:28:5: warning: format '%d' expects argument of type 'int', but argument 2 has type 'void *' [-Wformat]

<source>:29:5: warning: format '%p' expects argument of type 'void *', but argument 2 has type 'int' [-Wformat]

<source>:30:5: warning: format '%lld' expects argument of type 'long long int', but argument 2 has type 'int' [-Wformat]

<source>:31:5: warning: format '%s' expects argument of type 'char *', but argument 2 has type 'THING' [-Wformat]

<source>:32:5: warning: '0' flag used with '%s' gnu_printf format [-Wformat]

<source>:33:5: warning: format '%d' expects argument of type 'int', but argument 2 has type 'long long int' [-Wformat]

<source>:34:1: warning: control reaches end of non-void function [-Wreturn-type]

Compiler returned: 0

Насколько я понимаю, вышеизложенное имеет много разновидностей UB. Посмотрев вокруг, я увидел, что должен просто исправить вышесказанное. Теперь я хочу в конечном итоге исправить их все, но сейчас мое любопытство заставляет меня задуматься, какой сценарий наихудший. Я бы предположил, что такие случаи, как первый, где я не передаю достаточно предметов.

Насколько я понимаю, в вышесказанном у меня есть:

  1. Вытеснение стека, который не существует
  2. Недостаточно выскочить из стека
  3. Заполнение строки ведущими нулями
  4. Приведение целого числа к указателю
  5. Приведение структуры, которая может быть преобразована в

Исходя из вышесказанного, я вполне уверен, что все, что выпадет из стека, которого не существует, приведет к худшему сценарию. Но мне также интересно, каковы другие серьезные случаи.

1 Ответ

3 голосов
/ 13 марта 2019

В какой момент проблематично неопределенное поведение?

Все UB проблематично.

Определение эффектов UB конкретной версии компилятора имеет некоторыезаслуга в решении проблем.Тем не менее, никогда не следует полагаться на этот эффект UB для сохранения.

Мой ответ основан на C в целом, а не на gcc 4.7.


Учтите, что объекты не обязательно передаются с использованиемодин и тот же механизм для разных типов. Соответствующий истинный пример : float/double передается в стек FP и других типов через обычный стек.printf("%llx\n", 1.234); может плохо провалиться, даже если переданный размер равен 8 и ожидается 8, но они находятся в разных местах.Аналогичная разница может возникнуть между типами указателей и целыми числами (хотя это звучит как платформа единорога).


Оставить UB в коде неэффективным в разработке.
Подумайте, нашел ли кто-то UB, который работалотлично в выбранном случае, следующая компиляция или версия может привести к другим результатам.Исправляя, вы экономите время, не пытаясь объяснить, как «этот UB в порядке, я знаю, что проверил его» во время проверки кода.Также сэкономьте время, не нуждаясь в том, чтобы найти способ успокоить предупреждение об этом "хорошем" UB.Команда разработчиков, которая должна поддерживать ваш код UB, будет бормотать злые слова о предыдущем кодере.


UB Отсутствует соответствующий аргумент.

custom_printf("HI THERE%d");
<source>:26:5: warning: format '%d' expects a matching 'int' argument [-Wformat]

Не UB.С дополнительными аргументами все в порядке, но, скорее всего, это неверный шаг кодирования - отсюда и предупреждение. @ melpomene

custom_printf("HI THERE", 1);
<source>:27:5: warning: too many arguments for format [-Wformat-extra-args]

UB.int и void * могут отличаться по размеру, допустимым значениям и механизмам передачи функций,

custom_printf("val: %d", (void*)0);
<source>:28:5: warning: format '%d' expects argument of type 'int', but argument 2 has type 'void *' [-Wformat]

UB.так же, как строка 28

custom_printf("val: %p", 0);
<source>:29:5: warning: format '%p' expects argument of type 'void *', but argument 2 has type 'int' [-Wformat]

UB.int и long long могут отличаться размерами и функциями передающих механизмов,

custom_printf("val: %lld", 1);
<source>:30:5: warning: format '%lld' expects argument of type 'long long int', but argument 2 has type 'int' [-Wformat]

UB.Типы могут отличаться по размеру, допустимым значениям и механизмам передачи функций,

custom_printf("val: %s", (THING){"A", "AA", "CCCC"});
<source>:31:5: warning: format '%s' expects argument of type 'char *', but argument 2 has type 'THING' [-Wformat]

UB: Неверный стандартный спецификатор %0.30s, может произойти что угодно.Хорошо ведет себя в некоторых системах, которые определяют поведение для этого нестандартного спецификатора.

custom_printf("val: %0.30s","HI");
<source>:32:5: warning: '0' flag used with '%s' gnu_printf format [-Wformat]

UB, как строка 30

custom_printf("val: %d",LLONG_MAX);
<source>:33:5: warning: format '%d' expects argument of type 'int', but argument 2 has type 'long long int' [-Wformat]

Не UB с main().Только проблема UB с функциями в целом, если вызывающий код использует возвращаемое значение.Тем не менее, main() особенный в том, что код действует, как если бы return 0; был в конце - если эта функция не заканчивается return.

<source>:34:1: warning: control reaches end of non-void function [-Wreturn-type]
...