Почему я не могу установить формат вывода printf с динамическими аргументами?
Я хочу управлять выходным форматом функций printf()
с динамическим параметром, как показано ниже:
#include<stdio.h>
int main(int argc,char ** argv)
{
printf(argv[1],"hello,world");
return 0;
}
Затем я компилирую и запускаю его:
$ gcc -o test test.c
$ ./test "\t%s\n"
Результат странный:
\thello,world\n$
Почему "\n"
и "\t"
не действуют?
Ответы
Ответ 1
Поскольку используемые вами escape-последовательности (\t
и \n
) интерпретируются внутри строковых литералов компилятором C, а не printf()
. Это:
const char *newline1 = "\n", newline2[] = { '\n', 0 };
будет генерировать то же самое содержимое в newline1
и newline2
, независимо от того, переданы ли они когда-либо printf()
; строки все равно.
Ваш код ведет себя так:
printf("\\t%s\\n", "hello,world");
Здесь у меня есть двойное экранирование специальных символов для генерации строки с тем же фактическим содержимым, что и аргумент командной строки, т.е. "\t%s\n"
(шесть символов, а не четыре).
Правильный способ динамического управления printf()
- это построить строку формата в коде. Если вы хотите использовать C-подобные экраны во время выполнения, вам нужно каким-то образом интерпретировать их.
Ответ 2
Последовательность \n
в строковом или символьном литерале в C/С++ представляет собой один байт с числовым значением 10 (в системе ASCII). Когда вы выходите на терминал (попробуйте putchar(10)
!), Он просто устанавливает выходную позицию для следующего символа на терминале в начале следующей строки (в * nix; на MacOS, я думаю, вам нужен дополнительный \r
, или 13 для возврата каретки, чтобы иметь выходное положение в начале строки).
Аналогично, a \t
- это обозначение для одного байта со значением 9, что делает большинство терминалов продвигающим их курсор к следующей позиции табулятора.
Вам нужно вставить один байт этих значений в командной строке. Как это можно сделать, зависит от вашей оболочки; в bash вы можете заставить оболочку интерпретировать специальные символы, предварительно нажав Ctrl-V. Это выводит, например. вкладку, отображаемую путем отображения некоторого пустого пространства (вместо того, чтобы заставить оболочку показывать возможные продолжения строки или любую вкладку в bash). bash строки в одиночных или двойных кавычках могут включать новые строки без дополнительных усилий - просто нажмите enter.
Вот пример прогона в терминале cygwin с bash. Я нажал указанные клавиши в указанных положениях; Я завершил команду, как обычно, с помощью [return] после закрывающей одинарной кавычки во второй строке.
pressed Ctrl-v,[TAB] here | pressed [return] there
v v
$ ./printf-arg.exe ' %s
> '
hello,world
>
во второй строке был выведен оболочкой после того, как я нажал enter в строке, разделенной одинарными кавычками. (Который вставляет новую строку в строку). Это указывает на то, что редактируемая строка продолжается на этой строке.
В стороне, вероятно, небезопасно использовать аргументы командной строки таким образом в потенциально агрессивных средах. Тщательно обработанные строки могут иметь доступ к памяти, которая не предназначена для доступа, и, например, перенаправлять обратные адреса, тем самым искажая программу.
Ответ 3
Это потому, что компилятор обрабатывает escape-последовательности, такие как "\n"
и т.д., и делает это только в строковых или символьных литералах.
Ответ 4
если вы передадите интерпретируемую "\ t% s\n" команду, она будет работать. Однако сложно построить такую строку в оболочке. Самый простой способ, который я знаю, это:
./test $'\t%s\n'
См. цитирование ANSI в man bash для $'magick'