Безопасно ли хранить указатели строковых литералов?

#c #static #object-lifetime

Вопрос:

В соответствии со стандартом:

5.13.5 Строковые литералы [лекс.строка]

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

и:

6.6.4.1 Продолжительность статического хранения [basic.stc.static]

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

Я предполагаю, что безопасно хранить указатели на строковые литералы, что-то вроде:

 struct Base
{
    Base(const char* name)
        : _name(name)
    {
    }
    
    void print()
    {
        std::cout<<_name<<std::endl;
    }
    
    const char* _name = nullptr;
};

struct MyDerived : public Base
{
    MyDerived () : Base("MyDerived")
    {
    }
};

 

Хорошо ли определен приведенный выше код? Есть ли какие-то темные уголки стандарта, о которых я должен знать?

Комментарии:

1. Да, это четко определено и безопасно. Просто убедитесь, что вы не пытаетесь изменить символы или освободить память, на которую указывают.

2. И не думайте , что после const char* x = "abcd"; const char* y = "abcd"; x == y этого это будет правдой.

Ответ №1:

Хорошо ли определен приведенный выше код?

ДА.

Есть ли какие-то темные уголки стандарта, о которых я должен знать?

Возможно, в стандарте нет темного угла, но одна проблема заключается в том, что у вас есть указатель, и вы допускаете создание Base экземпляра и использование таким образом:

 Base foo(nullptr);
foo.print();
 

От operator<< :
«Поведение не определено, если s является нулевым указателем».

Несколько более безопасный конструктор:

 template<size_t N>
constexpr Base(const char(amp;name)[N]) : _name(name) {}
 

Я говорю несколько, потому что вы все еще можете это сделать:

 auto Foo() {
    const char scoped[] = "Fragile";
    Base foo(scoped);
    foo.print();     // OK
    return foo;
}                    // "scoped" goes out of scope, "_name" is now dangling

int main() {
    auto f = Foo();
    f.print();       // UB
}