2017-02-21 6 views
4

Я заметил некоторую потенциальную проблему с макросом va_arg, который используется для получения неназванного параметра из переменной функции. Рассмотрим следующий упрощенный пример:Сопоставляет ли const invism UB, когда аргумент указателя получен va_arg?

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

void foo(int n, ...) 
{ 
    va_list ap; 
    const char *s; 

    va_start(ap, n); 
    for (s = va_arg(ap, const char *); *s != '\0'; s++) 
     putchar(*s); 
    va_end(ap); 
} 

int main(void) 
{ 
    char str[] = "xyz"; 
    foo(1, str); 
    return 0; 
} 

Ссылка для va_arg макро состояний, (курсив мой):

Если va_arg вызывается, когда нет больше аргументов в ар, или если тип следующий аргумент в ар (после продвижения по службе) не совместим с T, поведение не определено (...)

Мой U nderstanding является то, что const char * и char * типов не совместим. Когда char массив передается в foo в виде char указателя (т.е. без изменений по умолчанию аргумент поощрений), то выражение, которое принимает константный квалифицированный указатель:

s = va_arg(ap, const char *) 

может ссылаться на «технический» UB. Такая же ситуация возникла бы, когда arr определяется как массив const, и аргумент получен как char *, а также для других типов, например. int * и const int *.

+0

В этом случае несовместимость является проблемой размера, а не проблемой 'const'. когда размер неправильный, вы меняете сканирование va_args, и он сильно падает. Если бы он один раз пропустил 0 вместо 'NULL' на 64-битной машине. –

ответ

3

Раздел 6.2.7 C11 language standard определяет совместимость типа , раздел 6.7.6.1 далее определяет его для указателей указателей и раздел 6.7.3 для классификаторов типов. Последнее (ограничение 10) говорит:

Для два квалифицированных типов, чтобы быть совместимыми, то оба имеет одинаково квалифицированную версию совместимого типа; порядок квалификаторов типа в списке спецификаторов или определителей не влияет на указанного типа.

Кроме того, раздел 6.7.6.1 говорит:

Для двух типов указателей совместимы, и должны быть одинаково квалифицированы и как должны быть указатели на совместимые типы.

В соответствии с этими двумя, я понимаю, что const char* и char* не совместимы (потому что const char и char не совместимы). В соответствии с определением va_arg в разделе 7.16.1.1, который требует совместимости типов (с несколькими исключениями, которые здесь не применяются), я считаю, что в этом случае у вас есть неопределенное поведение.

Теперь, если мы перестанем играть адвоката, я не вижу, как это может быть вредно относиться к char* как const char* --- это ограничит только юридические операции. Поэтому я считаю, что спецификация va_arg слишком консервативна.

+0

Я уже поддержал, но я согласен с тем, что раздел о 'va_arg' мог бы сделать с разъяснением для этого – StoryTeller

3

n1570/6.2.5p28, кажется, предположить, что это должно быть хорошо на практике:

... Точно так же, указатели на квалифицированные или неквалифицированные версии совместимых типов должны иметь то же требование, представительские и выравнивание ...

Поскольку char совместим с самим собой, указатель на char будет иметь такое же представление, как указатель на const char. Поскольку переменная функция аргумента по существу принимает представление каждого последующего параметра, это сначала проявляется четко.

Что касается наоборот, если вы используете указатель на символ, чтобы модифицировать константный символ, то есть на самом деле UB за n1570/6.7.3p6:

Если попытка изменить объект, определенный с тип const-qual, используя значение lvalue с неконстантированным типом, поведение не определено.


Чтобы продолжить анализ с ответом вы связаны в комментариях, n1570/6.7.6.1p2:

Для два типа указателей совместимы, и должны быть одинаково квалифицированы и как должны быть указатели совместимых типов.

Поскольку оба типа указателя сами не сопзИте, они одинаково квалифицированы, единственный вопрос, который остается, если const char совместят с char для наших целей.

И кажется, что они не ... как заметил ник. Так, с одной стороны, это может быть истолковано как UB.

+1

Я согласен, что здесь нет настоящей проблемы, потому что два указателя должны иметь одинаковые представления.Но если бы это было наоборот ('const char *' обрабатывается как 'char * 'и, возможно, назначается), я был бы более доволен неопределенным поведением. Согласно стандарту, однако, кажется, что это не вопрос представления, а совместимость типов, как это определено там. – nickie

+0

Мое предположение было основано на C11 6.7.3/10: 'Для двух квалифицированных типов, которые должны быть совместимы, оба должны иметь идентичную версию совместимого типа;', а также [это] (http://stackoverflow.com/a/18458745/586873) ответ. Тем не менее, я согласен, что это скорее теоретическая проблема, потому что представление 'const' такое же, как вы указали. –

+0

@GrzegorzSzpetkowski - Но у вас нет двух квалифицированных типов. Указатели не квалифицированы, только указатели. Стандарт слишком расплывчатый в этом вопросе ... – StoryTeller

1

Да, они несовместимы и используют va_arg с типом const char *, когда фактический тип char *, и наоборот, является неопределенным поведением.

Тип типа совместим только с самим собой .

Кроме того, раздел о квалификаторах, являющийся константой, поддерживает это .


(Цитируется по ISO/IEC 9899: 201x)

(6.2.7 Совместимость типа и составной тип 1)
Два типа имеет совместимый тип, если их типы и те же

(6.7.3 Тип классификаторов 10)
Для двух квалифицированных типов, которые должны быть совместимыми, оба должны иметь идентичную версию совместимой тип; порядок классификаторов типов в списке спецификаторов или квалификаторов не влияет на указанный тип.

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