Почему я не могу установить формат вывода 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'