Tamir Abutbul
Tamir Abutbul

Reputation: 7661

In C, why is %s working without giving it a value?

According to my knowledge and some threads like this, if you want to print strings in C you have to do something like this:

printf("%s some text", value);

And the value will be displayed instead of %s.

I wrote this code:

char password[] = "default";
printf("Enter name: \n");
scanf("%s", password);
printf("%s is your password", password); // All good - the print is as expected

But I noticed that I can do the exact same thing without the value part and it will still work:

printf("%s is your password");

So my question is why does the %s placeholder get a value without me giving it one, and how does it know what value to give it?

Upvotes: 4

Views: 657

Answers (3)

HAL9000
HAL9000

Reputation: 2188

So, there are a lot of posts telling that you shouldn't do printf("%s is your password");, and that you were just lucky. I guess from your question that you somewhat knew that. But few are telling you the probable reason for why you were lucky.

To understand what probably happened, we have to understand how function parameters are passed. The caller of a function must put the parameters on an agreed upon place for the function to find the parameters. So for parameters 1...N we call these places r1 ... rN. (This kind of agreement is part of something we call a "Function Calling Convention")

That means that this code:

scanf("%s", password);
printf("%s is your password",password);

may be turned into this pseudo-code by the compiler

r1="%s";
r2=password;
call scanf;

r1="%s is your password";
r2=password;
call printf;

If you now remove the second parameter from the printf call, your pseudo-code will look like this:

r1="%s";
r2=password;
call scanf;

r1="%s is your password";
call printf;

Be aware that after call scanf;, r2 might be unmodified and still be set to password, therefore call printf; "works"

You might think that you have discovered a new way to optimize code, by eliminating one of the r2=password; assignments. This might be true for old "dumb" compilers, but not for modern ones.

Modern compilers will already do this when it is safe. And it is not always safe. Reasons for why it isn't safe might be thatscanf and printf have different calling conventions, r2 might have been modified behind your back, etc..

To better get a feeling of what the compiler is doing, I recommend to look at the assembler output from your compiler, at different optimization levels.

And please, always compile with -Wall. The compiler is often good at telling you when you are doing dumb stuff.

Upvotes: 3

JVMATL
JVMATL

Reputation: 2122

The printf() function uses a C language feature that lets you pass a variable number of arguments to a function. (Technically called 'variadic functions' - https://en.cppreference.com/w/c/variadic - I'll just say 'varargs' for short.)

When a function is called in C, the arguments to the function are pushed onto the stack(*) - but the design of the varargs feature provides no way for the called function to know how many parameters were passed in.

When the printf() function executes, it scans the format string, and the %s tells it to look for a string in the next position in the variable argument list. Since there are no more arguments in the list, the code 'walks off the end of the array' and grabs the next thing it sees in memory. I suspect what's happening is that the next location in memory still has the address of password from your prior call to scanf, and since that address points to a string, and you told printf to print a string, you got lucky, and it worked.

Try putting another function call (for example: printf("%s %s %s\n","X","Y","Z") in between your call to scanf("%s", password); and printf("%s is your password"); and you will almost certainly see different behavior.

Free Advice: C has a lot of sharp corners and undefined bits, but a good compiler (and static analysis or 'lint' tool) can warn you about a lot of common errors. If you are going to work in C, learn how to crank your compiler warnings to the max, learn what all the errors and warnings mean (as they happen, not all at once!) and force yourself to write C code that compiles without any warnings. It will save you a lot of unnecessary hassle.

(*) generalizing here for simplicity - sometimes arguments can be passed in registers, sometimes things are inlined, blah blah blah.

Upvotes: 4

Jean-Baptiste Yunès
Jean-Baptiste Yunès

Reputation: 36401

This is undefined behavior, anything can happen included something that looks like correct. But it is incorrect. Your compiler can probably tell you the problem if you use correct options.

Standard says (emphasized is mine):

7.21.6.1 The fprintf function

  1. The fprintf function writes output to the stream pointed to by stream, under control of the string pointed to by format that specifies how subsequent arguments are converted for output. If there are insufficient arguments for the format, the behavior is undefined. If the format is exhausted while arguments remain, the excess arguments are evaluated (as always) but are otherwise ignored. The fprintf function returns when the end of the format string is encountered.

Upvotes: 9

Related Questions