Ссылаясь на литералы встроенного типа - PullRequest
0 голосов
/ 03 сентября 2018

Итак, мы столкнулись с провальным тестом в Linux, который, как я полагаю, связан с неверными предположениями с моей стороны относительно правильности указателей, ссылающихся на встроенные литералы. Код выглядит аналогично этому псевдокоду:

auto obj = func( 'c', "str" ); // (1)
big_type big_object;           // (2) 

В (1) func() возвращает объект, в котором хранится указатель const на символьный литерал и один на строковый литерал. Проверка в отладчике показывает, что оба верны.

В (2) отладка показывает, что то, что раньше было 'c' в памяти, на которую ссылается const char* в obj, перезаписывается.

Теты показывают, что это также происходит с литералами int и double. Это происходит в GCC 5.4.1, это не происходит в GCC 4.1.2.

Проработав C ++> 25 лет, я научился предполагать, что компилятор обычно прав, а я неправ; так что я делаю это и здесь.

Однако, хотя я знаю, что, если это касается только литералов небольших встроенных типов, я могу это исправить (скопировав их вместо ссылки на них), если это может произойти с объектами произвольного размера ("str" ) а у нас довольно большая проблема.

Так может кто-нибудь, пожалуйста, объясните точные правила относительно этого?

Ответы [ 4 ]

0 голосов
/ 03 сентября 2018

Для точных правил, вероятно, достаточно (хотя стандартная цитата Квентина явно более авторитетна) указать следующее предложение на строковых литералах

Строковые литералы имеют статическую длительность хранения и, таким образом, существуют в памяти на время жизни программы

, который отсутствует ни для одного из других типов литералов .

Другой способ взглянуть на это - пересмотреть ваш код

object func(char c, const char *s)
{
    return object{&c, s};
}

и обратите внимание, что строковый литерал не передается по значению. Поскольку (как только он распадается на указатель), вы просто передаете адрес первого символа - этот массив должен оставаться действительным, по крайней мере, некоторое время, и поскольку нет способа узнать, каким должно быть время жизни, статическая продолжительность это нормальное значение по умолчанию.

0 голосов
/ 03 сентября 2018

С [ expr.prim.literal§1 ]:

A литерал является основным выражением. Его тип зависит от его формы. Строковый литерал представляет собой lvalue ; все остальные литералы являются значениями.

Дополнительные сведения об этих значениях можно найти в [ lex.string§16 ]:

Оценка string-literal приводит к строковому литералу с статической продолжительностью хранения , инициализированному из заданных символов, как указано выше. [...]

Что напрямую решает проблему: строковые литералы являются единственными литералами, которые имеют статическую длительность хранения, и, таким образом, на них могут ссылаться указатели, которые переживают выражение, в котором они появляются.

0 голосов
/ 03 сентября 2018

Предполагая, что func определено примерно так:

some_class_type func(const char& ch, const char* str)
{
    some_class_type some_object;
    some_object.pch = &ch;
    some_object.pstr = str;
    return some_object;
}

Затем вы сохраняете указатель на временную переменную с помощью &ch.

Время жизни ch будет не полной программой, только до конца полного выражения (т.е. вызова func('c', "str")), тогда временная переменная прекратит свое существование и у вас остался бездомный указатель.

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

0 голосов
/ 03 сентября 2018

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

Чтобы взглянуть на это по-другому: ваша функция получает два аргумента: один - символьное значение, а другой - указатель на строковый литерал. Создание копии указателя на строку - это хорошо, но создание указателя на значение, которое было передано в качестве аргумента, не в порядке. Точно так же, как если бы вы создали указатель на строку-указатель, у вас возникли бы проблемы. Аргументы функции уничтожаются после завершения вызова функции. В случае символа это означает, что символ исчез, тогда как в случае указателя на строку вы сделали копию и сохранили ее.

...