Каков правильный способ печати неформатированной строки, не заканчивающейся новой строкой?

printf("foo: "); 

против

fputs("foo: ", stdout);

Как упоминалось в заголовке, строка не форматируется и не заканчивается на новой строке (поэтому не ставится). Я не уверен насчет накладных расходов и оптимизации, так каково практическое правило в этом случае?

Обновлено: Нормально ли использовать printf с неформатированными строками?

Если вы беспокоитесь об оптимизации этой простой вещи, то в этом нет необходимости. Компилятор с включенной оптимизацией может даже распознать вызов printf и выдать вам вызов fputs. А если нет, то разница будет незначительной и даже неизмеримой.

Some programmer dude 02.04.2024 10:24

@Someprogrammerdude Я только начинаю и не знаю об этих оптимизациях. Поэтому искал лучшие практики. Тогда я просто остановлюсь на printf и позволю компилятору сделать все остальное. Спасибо.

GrandeKnight 02.04.2024 10:27

Эмпирическое правило: «Преждевременная оптимизация — корень всех зол».

moooeeeep 02.04.2024 10:28

Забавно, что printf и fprintf(stdout, обрабатываются оптимизатором по-разному.

gulpr 02.04.2024 10:35

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

GrandeKnight 02.04.2024 11:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
104
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Как указано в заголовке, строка не форматируется и не закончить на новой строке (чтобы не помещалось). Я не уверен насчет накладных расходов и оптимизация, так каково практическое правило в этом случае?

  • printf — гораздо более сложная функция, чем fputs. Но обычно время процессора не является основным узким местом при использовании функций ввода-вывода.

Но если вы используете fprintf вместо printf, большинство современных компиляторов оптимизируют ваш fprintf до fwrite. Поэтому используйте fprintf вместо printf, если вы хотите, чтобы ваш код был оптимизирован в этом случае.

Вопрос «что лучше» скорее основан на мнениях. Я предпочитаю использовать printf для обеспечения единообразия. Но «золотого правила» в данном случае нет.

void foo(void)
{
    fprintf(stdout, "Hello World");
}

void zoo(void)
{
    fputs("Hello World", stdout);
}

void bar(void)
{
    printf("Hello World");
}

и полученный код:

.LC0:
        .string "Hello World"
foo:
        mov     edx, 11
        mov     esi, 1
        mov     edi, OFFSET FLAT:.LC0
        mov     rcx, QWORD PTR stdout[rip]
        jmp     fwrite
zoo:
        mov     edx, 11
        mov     esi, 1
        mov     edi, OFFSET FLAT:.LC0
        mov     rcx, QWORD PTR stdout[rip]
        jmp     fwrite
bar:
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        jmp     printf

https://godbolt.org/z/jKboo51br

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

GrandeKnight 02.04.2024 11:09

@GrandeKnight, ты сформулировал это совершенно ясно - накладные расходы и оптимизация.

gulpr 02.04.2024 11:16

ЛМАО, а как насчет предыдущей части «Я не уверен». Я понятия не имею об этом и не спрашивал об этом с этой точки зрения. Значит я действительно неправильно выразился.

GrandeKnight 02.04.2024 11:27
Ответ принят как подходящий

Каков правильный путь

Оба способа «правильные», оба будут работать одинаково и нормально.

Рассмотрим правила оптимизации. Не оптимизируйте, если в этом нет необходимости. printf("foo: "); совершенно нормально, читаемо, понятно, все знают, что это значит. Оставь это. printf("foo :"); это здорово.

Если вы действительно хотите оптимизировать и действительно заботитесь о скорости, вы можете вызвать fwrite со статически рассчитанными значениями:

fwrite("foo: ", 1, sizeof("foo: ") - 1, stdout);

Но я сомневаюсь, что оптимизация чего-то стоит. Если бы вас действительно заботила скорость, вы бы все равно вызывали функции, специфичные для ОС, например, в Linux у нас есть write или splice или tee или vmsplice или copy_file_range или sendfile.

если использование printf для неформатированных строк является обычной практикой

Давайте возьмем какой-нибудь большой проект и проверим. Анализируя исходный код gcc, можно обнаружить такое количество строк, которые соответствуют fputs буквальной строке:

$ rg -g '*.[ch]' 'fputs\s*\(\s*"[^"]*"' gcc/ | wc -l
263
$ rg -g '*.[ch]' 'fputs\s*\(\s*"[^"]*"' gcc/ | head
gcc/defaults.h:    fputs (":\n", (FILE));                       \
gcc/defaults.h:    fputs (":\n", (FILE));                       \
gcc/defaults.h:      fputs (", ", STREAM);                              \
gcc/defaults.h:      fputs (", .-", STREAM);                            \
gcc/config/csky/csky.h:      fputs ("\t.comm\t", STREAM);                               \
gcc/config/csky/csky.h:  fputs ("\t.bss\t", (STREAM));                                  \
gcc/config/xtensa/xtensa.h:    fputs ("@pcrel", FILE);                                          \
gcc/config/c6x/c6x.h:       fputs ("\t.long ", FILE);                   \
gcc/config/visium/visium.h:  do { assemble_name (STREAM, NAME); fputs (":\n", STREAM); } while (0)
gcc/config/visium/visium.h:( fputs ("\n\t.comm  ", (STREAM)),     

И столько строк, соответствующих printf, с буквенной строкой и без аргументов:

$ rg -g '*.[ch]' 'printf\s*\(\s*"[^"]*"\);' gcc/ | wc -l
1792

Есть так много строк с printf, в которых нет символов новой строки в строке форматирования:

$ rg -g '*.[ch]' 'printf\s*\(\s*"[^"]*"\);' gcc/ | grep -v '"[^"]*\\n[^"]*"' | wc -l
470
$ rg -g '*.[ch]' 'printf\s*\(\s*"[^"]*"\);' | grep -v '"[^"]*\\n[^"]*"' | head
zlib/contrib/minizip/miniunz.c:          printf(" ");
libdecnumber/decNumber.c:    if (decNumberIsInfinite(dn)) printf("Infinity");
libdecnumber/decNumber.c:      if (dn->bits&DECSNAN) printf("sNaN");  /* signalling NaN */
libdecnumber/decNumber.c:       else printf("NaN");
libdecnumber/decNumber.c:    printf(" ");
libdecnumber/decNumber.c:    printf(":");
libdecnumber/decCommon.c:       if ((num->lsd-ub)%3==0 && ub!=num->lsd) printf(" "); /* 4-space */
libdecnumber/decBasic.c:  printf("----- div = ");
libdecnumber/decBasic.c:  printf("DivQuo:");
libdecnumber/decBasic.c:    printf("CoeffbL:");

Эти цифры выглядят достаточно высокими, я бы сказал, что любое является «правильным». Глядя на это, printf("literal") даже больше, чем fputs("literal", anything).

Я не согласен с мнением, что printf("foo: "); — это нормально. Это не нормально, читабельно и понятно. Да, люди могут знать, что это значит, но, ИМХО, с семантической точки зрения это плохая практика. Когда я вижу printf, я предполагаю, что кто-то печатает что-то отформатированное, значение или что-то в этом роде. На мой взгляд, fputs лучше, или для последовательности printf("%s", "foo: ");, или что-то в этом роде.

pschulz 02.04.2024 10:54

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

GrandeKnight 02.04.2024 11:10

это должен быть принятый ответ, поскольку он больше устраняет сомнения ОП

pochopsp 02.04.2024 11:17

@pochopsp вопрос в данном случае не по теме, так как он основан на мнениях

gulpr 02.04.2024 11:24

@pschulz Я согласен с вами: известно, что printf назван в честь «форматировать печать», а puts — в честь «поместить строку». И мы можем быть уверены, что какой-то (дополнительный) код будет выполняться в поисках (отсутствующих) printf спецификаторов в строке формата.

arfneto 02.04.2024 15:52

В Python print будет на миллионы инструкций больше. Shell echo используется в любое время, но это будет на миллионы инструкций больше. Преждевременная оптимизация здесь является злом, если только вы не нацеливаетесь на микроконтроллер и не профилируете свой код. Аргументом является только правильность, с чем я согласен. Противодействием может быть то, что в настоящее время у нас есть предупреждения компилятора и линтеры для диагностики % без аргументов. Я не вижу веских аргументов в пользу соблюдения одного стиля, поэтому и рекомендую.

KamilCuk 02.04.2024 16:42

какое практическое правило в этом случае?

Лучше отдать предпочтение fputs(), чтобы избежать строк с "%". Возможно, не сегодня, но возможно в будущем.

В противном случае напишите наиболее понятный код — и это зависит от окружающего кода.

Другие вопросы по теме