storen
storen

Reputation: 1035

Why can't I set printf's output format with dynamic args?

I want to control the printf() functions output format with dynamic parameter, just as the code showed below:

    #include<stdio.h>

    int main(int argc,char ** argv)
    {
        printf(argv[1],"hello,world");
        return 0;
    }

Then I compile and run it:

$ gcc -o test test.c
$ ./test "\t%s\n"

The result is strange:

\thello,world\n$

Why "\n" and "\t" has no effect?

Upvotes: 12

Views: 2190

Answers (4)

Peter - Reinstate Monica
Peter - Reinstate Monica

Reputation: 16049

The sequence \n in a string or character literal in C/C++ is a single byte with the numeric value 10 (on an ASCII system). When output on a terminal (try putchar(10)!) it just sets the output position for the next character on the terminal to the beginning of the next line (on *nix; on MacOS, I think, you need an additional \r, or 13 for carriage return to have the output position at the beginning of the line).

Similarily, a \t is the notation for a single byte with the value 9, which makes most terminals advance their cursor to the next tabulator position.

What you need is to insert a single byte of these values in the command line. How that can be done depends on your shell; in bash you can keep the shell from interpreting special characters by pressing Ctrl-V beforehand. That outputs e.g. a tab, displayed by showing some empty space (instead of making the shell show possible string continuations or whatever tab does in bash). bash strings in single or double quotes can include newlines without further efforts -- simply press enter.

Here is a sample run in a cygwin terminal with bash. I pressed the indicated keys at the indicated positions; I finished the command as usual with [return] after the closing single quote on the second line.

pressed Ctrl-v,[TAB] here | pressed [return] there 
                    v     v
$ ./printf-arg.exe '    %s
> '
        hello,world

The > in the second line was output by the shell after I pressed enter within the string delimited by single quotes. (Which inserts a newline in the string). It is an indication that the string being edited is continued on that line.

As an aside, it is probably unsafe to use command line arguments this way in potentially hostile environments. Carefully crafted strings could access memory which is not meant to be accessed and e.g. redirect return addresses, thus corrupting the program.

Upvotes: 6

Michał Šrajer
Michał Šrajer

Reputation: 31182

if you pass interpreted "\t%s\n" to command it will work. However it is tricky to construct such string in shell. The easiest way I know is:

./test $'\t%s\n'

See ANSI quoting in man bash for the $'magick'

Upvotes: 3

unwind
unwind

Reputation: 400009

Because the escapes you use (\t and \n) are interpreted inside string literals by the C compiler, not by printf(). This:

const char *newline1 = "\n", newline2[] = { '\n', 0 };

would generate the exact same content in newline1 and newline2, regardless of whether or not these are ever passed to printf(); the strings are there anyway.

Your code behaves just like this would:

printf("\\t%s\\n", "hello,world");

Here, I've double-escaped the special characters to generate a string with the same actual content as your command-line argument, i.e. "\t%s\n" (six characters rather than four).

The proper way to dynamically control printf() is to build the format string in code. If you want C-like escapes at runtime, you need to interpret them yourself in some way.

Upvotes: 9

Some programmer dude
Some programmer dude

Reputation: 409384

It's because the compiler handles the escape sequences like "\n" etc., and it does it in string or character literals only.

Upvotes: 4

Related Questions