В этом ответе мы можем прочитать следующее:
Я предполагаю, что есть небольшая разница между использованием
'\n'
или использованием"\n"
, но последний представляет собой массив (двух) символов, который должен быть напечатан символ за символом, для которого должен быть установлен цикл, что сложнее, чем вывод одного символа.
emphasis mine
Это имеет смысл для меня. Я думаю, что для вывода const char*
требуется цикл, который будет проверять нулевой терминатор, который должен вводить больше операций, чем, скажем, простой putchar
(не подразумевающий, что std::cout
с char
делегирует вызов - это просто упрощение, чтобы представить пример).
Это убедило меня использовать
std::cout << '\n';
std::cout << ' ';
а не
std::cout << "\n";
std::cout << " ";
Здесь стоит упомянуть, что я знаю, что разница в производительности в значительной степени незначительна. Тем не менее, некоторые могут утверждать, что предыдущий подход имеет намерение фактически передать один символ, а не строковый литерал, который оказался длиной char
(два char
, если считать '\0'
).
В последнее время я сделал небольшой пересмотр кода для кого-то, кто использовал последний подход. Я сделал небольшой комментарий по делу и пошел дальше. Затем разработчик поблагодарил меня и сказал, что он даже не думал о такой разнице (в основном, сосредоточившись на намерениях). Это не было никакого воздействия (неудивительно), но изменение было принято.
Затем я начал задаваться вопросом, насколько точно это изменение значимо, поэтому я побежал к Годболту. К моему удивлению, он показал следующие результаты при тестировании на GCC (транк) с флагами -std=c++17 -O3
. Сгенерированная сборка для следующего кода:
#include <iostream>
void str() {
std::cout << "\n";
}
void chr() {
std::cout << '\n';
}
int main() {
str();
chr();
}
меня удивило, потому что кажется, что chr()
фактически генерирует ровно вдвое больше инструкций, чем str()
:
.LC0:
.string "\n"
str():
mov edx, 1
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
jmp std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
chr():
sub rsp, 24
mov edx, 1
mov edi, OFFSET FLAT:_ZSt4cout
lea rsi, [rsp+15]
mov BYTE PTR [rsp+15], 10
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
add rsp, 24
ret
Почему это? Почему оба они в конечном итоге вызывают одну и ту же функцию std::basic_ostream
с аргументом const char*
? Означает ли это, что литеральный подход char
не только не лучше, но на самом деле хуже строкового литерала?