Странный способ инициализации std :: string строкой C - PullRequest
2 голосов
/ 26 февраля 2011

пока я читал исходный код nVidia CUDA, я наткнулся на эти две строки:

    std::string stdDevString;

    stdDevString = std::string(device_string);

Обратите внимание, что device_string - это символ [1024]. Вопрос заключается в следующем: зачем создавать пустую строку std :: string, а затем снова создавать ее со строкой C в качестве аргумента? Почему они не позвонили std::string stdDevString = std::string(device_string); в одну линию?

Есть ли скрытое поведение при инициализации строки, которое этот код пытается обойти / использовать? Чтобы гарантировать, что строка C внутри stdDevString остается нулевой завершенной, несмотря ни на что? Потому что, насколько я знаю, при инициализации std :: string для строки C, которая не завершена нулем, все равно будут возникать проблемы.

Ответы [ 4 ]

4 голосов
/ 26 февраля 2011

Почему они не позвонили std::string stdDevString = std::string(device_string); в одну строку?

Нет веских причин для того, что они сделали. Учитывая конструктор std::string::string(const char*), вы можете просто использовать любой из:

std::string stdDevString = device_string;
std::string stdDevString(device_string);
std::string stdDevString{device_string}; // C++11 { } syntax

Двухступенчатая конструкция по умолчанию, тогда присваивание - это просто (плохой) стиль программиста или недосмотр. Без оптимизации, это делает немного ненужной конструкции, но это все еще довольно дешево. Скорее всего, это устранено оптимизацией. Не важно - сомневаюсь, упомянул бы я об этом в обзоре кода, если бы он не был в чрезвычайно чувствительной к производительности области, но определенно лучше отложить объявление переменных до тех пор, пока не будет доступно полезное начальное значение для их конструирования, локализации его все в одном месте: он не только менее подвержен ошибкам и перекрестным ссылкам, но и минимизирует область действия переменной, упрощая обоснование ее использования.

Чтобы гарантировать, что строка C внутри stdDevString останется нулевой, независимо от того, что завершено?

Нет - это не имело значения. Начиная с C ++ 11, внутренний буфер в stdDevString будет храниться с NUL-ограничением независимо от того, какой конструктор используется, в то время как для C ++ 03 не обязательно завершается - см. Подробный заголовок C ++ 03 ниже - но есть нет гарантий независимо от того, как выполняется строительство / назначение.

Потому что, насколько я знаю, при инициализации std::string для строки C, которая не завершена нулем, все равно будут возникать проблемы.

Вы правы - любой из перечисленных вариантов построения будет только копировать текст ASCIIZ в std::string - с учетом первого NUL ('\0') терминатора. Если массив char не завершен NUL, будут проблемы.

(Это отдельная проблема для того, чтобы буфер внутри std::string оставался NUL завершенным - обсуждалось выше).

Обратите внимание, что есть отдельный конструктор string(const char*, size_type), который может создавать строки со встроенными NUL и не будет пытаться читать дальше, чем было сказано (Constructor (4) здесь )

C ++ 03 std :: строки не гарантированы внутренним NUL-завершением

Каким бы образом не создавался и не инициализировался std::string, до C ++ 11 Стандарт не требовал завершения NUL в буфере строки. std::string лучше всего представлять как набор потенциально непечатаемых (условно говоря, двоичных в смысле ввода / вывода ftp / file) символов, начинающихся с адреса data() и продолжающихся до size() символов. Итак, если у вас было:

std::string x("help");
x[4];  // undefined behaviour: only [0]..[3] are safe
x.at(4); // will throw rather than return '\0'
x.data()[4]; // undefined behaviour, equivalent to x[4] above
x.c_str()[4]; // safely returns '\0', (perhaps because a NUL was always
              // at x[4], one was just added, or a new NUL-terminated
              // buffer was just prepared - in which case data() may
              // or may not start returning it too)

Обратите внимание, что API-интерфейсу std :: string требуется c_str() для возврата указателя на значение, оканчивающееся NUL. Для этого он может либо:

  • всегда постоянно сохраняет дополнительный NUL в конце строкового буфера (в этом случае data[5] может произойти , чтобы быть безопасным для этой реализации, но код может прерваться, если реализация изменен или код перенесен в другую реализацию стандартной библиотеки и т. д.)
  • реактивно ждут, пока не будет вызван c_str(), затем:

    • , если достаточно для текущего адреса (т. Е. data()), добавьте NUL и верните то же значение указателя, которое data() вернет
    • в противном случае выделите новый, больший буфер, скопируйте данные, NUL завершите его и верните указатель на него (обычно , но необязательно этот буфер заменит старый буфер, который будет удален такой, что вызов data() сразу после этого вернет тот же указатель, который был возвращен c_str())
1 голос
/ 27 февраля 2011

Я думаю, что это невежественно отклонить как плохое кодирование.Если предположить, что эта строка была выделена в области видимости файла или в качестве статической переменной, это может быть хорошее кодирование.

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

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

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

В такой системе код размещенпо OP это правильный способ сделать это.

1 голос
/ 26 февраля 2011

Я бы сказал, что это эквивалентно написанию:

std::string stdDevString = std::string(device_string);

Или, еще проще:

std::string stdDevString = device_string;

После создания std :: string она содержит личную копиюданных в строке C.

0 голосов
/ 26 февраля 2011

Выглядит как артефакт для меня. Возможно, между ними был какой-то другой код, затем он был удален, и кто-то был слишком ленив, чтобы объединить эти две оставшиеся строки в одну.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...