Why are conversion characters needed? - c

When I run this:
#include <stdio.h>
int main() {
int x = 1;
printf(x, "\n");
return 0;
}
It gives me these errors:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x1)
Format string is not a string literal (potentially insecure)
Treat the string as an argument to avoid this
Incompatible integer to pointer conversion passing 'int' to parameter of type 'const char *'
And it outputs:
(lldb)
However, when I change it to:
printf("%s", x);
It works perfectly fine. (outputs "1", as expected)
Why are the conversion characters (e.g. %s, %d, etc...) needed?

They're needed to tell the printf function what the types (and number) of the parameters are that you are passing.
The language has no mechanism to allow a function to determine this dynamically, so the format string gives it the clues to decode them.
The format string is always the first parameter, because the called function can always access that in the same place. Functions like printf are still written in C typically, so can only use those functions that the language provides.

They parameters are not "conversion characters". I think your confusion comes from the fact that you think printf simply prints all its arguments and automatically deduces how to print each. However, printf cannot work like that because C does not have support for overloading.
Concretely, the first argument is not like the rest. It is not something to print, but a format string. You can see easily what it means by trying this:
printf("My friend %s has %d coins!", "John", 123);
which will print:
My friend John has 123 coins!
%s here specifies the first argument (after the format string) will be interpreted as a string, and %d means the second argument (again, after the format string) will be understood as an integer. Both will be replaced with the actual value from the argument.

Related

Is it valid/recommended to use string literals as the result of an expression using the ternary operator?

Is the following code valid/good practice in C?
int x = 1;
printf(x == 1 ? "%d second elapsed" : "%d seconds elapsed", x);
It compiles fine, so I assume it is fine (also since it is just syntactical sugar for an if-else block), but if anyone has some additional insight, I would appreciate it, thank you.
P.S., I assume the same goes for C++?
Yes!
Opinionated: After wading though tons of code written by programmers writing kernels, drivers and misc. utility programs that are usually considered "good", I think the consensus is that "it's fine". Not only is it safe - it can also be made easily readable.
A small, non-opinionated, note: Compilers are allowed to make string literals overlap. If you have two string literals, "Hello world" and "world" and compile your program will full optimization, you may find that the pointer to 'w' in "world" is actually within the "Hello world" string literal, since they are both valid, null-terminated strings. Example:
ptr1
|
V
hello world\0
^
|
ptr2
However, that optimization cannot be applied to strings like yours, since the different part is in the middle of the string literal.
I therefore suggest:
printf("%d second%s elapsed", x, x == 1 ? "" : "s");
Another opinion of mine is that doing it like this also makes it easier to read.
TL,DR: pass a string literal as the format string unless you really have no choice.
Is it valid C? Yes, sure. printf wants its first argument to be a string (a null-terminated array of characters), and it's getting it. Whatever the value of x is, this string contains exactly one printf format, which expects an int argument. And printf receives an int argument. So all is fine, as far as the C language is concerned.
Is it good practice? No. There are several advantages to passing a string literal as the format string to functions like printf, scanf, etc. If you pass a string literal, that gives the compiler (and other static analyzers) the opportunity to complain if you aren't passing correctly typed arguments. If you pass something more complex, the compiler may not be able to tell. Furthermore, passing a string literal gives the compiler more optimization opportunities: many compilers will break down printf calls into lower-level functions when the format string is constant: printf("%d seconds elapsed", x) will be compiled into something like __builtin_printf_int(x) + fputs("", stdout) (plus error checking). If the format string is variable, it has to be parsed at runtime.
In this simple example, compilers are likely to recognize that there are only two possible format strings and to perform the check and optimization. In fact, let's make the program wrong:
#include <stdio.h>
void foo(long x) {
printf(x == 1 ? "%d second elapsed" : "%d seconds elapsed", x);
}
Here's what GCC has to say about it:
a.c: In function ‘foo’:
a.c:3:23: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long int’ [-Wformat=]
printf(x == 1 ? "%d second elapsed" : "%d seconds elapsed", x);
~^
%ld
a.c:3:45: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long int’ [-Wformat=]
printf(x == 1 ? "%d second elapsed" : "%d seconds elapsed", x);
~^
%ld
You're getting two warnings, one for each of the two formats. But in more complex cases, the compiler would give up and just pass the dynamically constructed string to printf at runtime, with no opportunity to bail out if the parameters are wrong.
As long as your program hard-codes English for messages, there's a simple solution: make the pluralization a separate argument.
printf("%d second%s elapsed", x, x == 1 ? "" : "s");
This doesn't work well with internationalization, since different languages have different rules (for example some languages need the singular for 0, some have a different form for 2 and 3-or-more, some need a plural on “elapsed”, etc.). If you need internationalization, you aren't passing a constant string to printf anyway.

Clear all lines in a range in c

I need to clear all lines between StartLine and ScreenHight in c, But i get the compiler error:
warning: too many arguments for format [-Wformat-extra-args]
Code:
void clearScreen(int startLine, int screenHight)
{
for (int i=startLine; i<screenHight - 1; ++i)
{
printf("\x1b[",i,";1H\33[2K");
}
}
The first argument of printf is a format string. Your format string "\x1b[" does not have any format specifiers, so printf won't expect any of the other arguments you provided. Thus, it will only print the format string.
To specify the other two arguments, use %d (to print the integer i) and %s (to print the string ";1H\33[2K"):
printf("\x1b[%d%s",i,";1H\33[2K");
You were trying to contcatenate a string for using as a paremeter in the
printf call. But that is not how you craete a string in c.
Use another way to build your string parameter (e.g. sprintf) and
then use that string as a parameter to the printf call.

printf() working without double quotes, prints random characters [duplicate]

This question already has answers here:
Behaviour of printf when printing a %d without supplying variable name
(6 answers)
Closed 4 years ago.
I stumbled upon this C code in a college test and when I tested it on Dev-C++ 5.11 compiler, it printed random characters. I can't understand how or why this code works. Can someone enlighten me?
int main() {
char a[10] = "%s" ;
printf( a ) ;
}
This question has two parts: the missing quotes and the random characters.
printf() is just a function. You can pass strings and other values to functions as arguments. You don't have to use a literal. You can use both char *a = "something"; printf(a) (passing a variable as an argument) and printf("something") (passing a string literal as an argument).
printf() is also a variadic function. This means it can accept any number of arguments. You can use printf("hello world"), printf("%s", "hello world") and even printf("%s %s", "hello", "world"). Some older compilers don't verify you actually passed the right number of arguments based on the first argument which is the format string. This is why your code compiles even though it's missing an argument. When the program runs the code goes over the format string, sees "%s" and looks for the second argument to print it as a string. Since there is no second argument it basically reads random memory and you get garbage characters.
printf function signature is:
int printf(const char *format, ...);
It expects format string as the first argument and variable number of arguments that are handled and printed based on the format specifiers in the format string. variable a in your question is providing it the format string. Reason for random characters is that the argument for format specifier %s is missing. Following will correctly print a string:
printf( a, "Hello World!" );
A list of format specifiers can be seen here https://en.wikipedia.org/wiki/Printf_format_string
Why does it compile?
Because variadic arguments accepted by printf are processed at run time. Not all compilers do compile time checks for validating arguments against the format string. Even if they do they would at most throw a warning, but still compile the program.
It's using the string "%s" as a format string, and using uninitialized memory as the "data".
The only reason it does "something" is because the compiler was apparently not smart enough to recognize that the format string required one parameter and zero parameters were supplied. Or because compiler warnings were ignored and/or errors were turned off.
Just an FYI for anybody who bumps into this: "Always leave all warnings and errors enabled and fix your code until they're gone" This doesn't guarantee correct behaviour but does make "mysterious" problems less likely.

C variable assignment issue

I think the title does not suit well for my question. (I appreciate it, if someone suggests an Edit)
I am learning C with "Learn C The Hard Way.". I am using printf to output values using format specifiers. This is my code snippet:
#include <stdio.h>
int main()
{
int x = 10;
float y = 4.5;
char c = 'c';
printf("x=%d\n", x);
printf("y=%f\n", y);
printf("c=%c\n", c);
return 0;
}
This works as I expect it to. I wanted to test it's behavior when it comes to conversion. So everything was ok unless I made it to break by converting char to float by this line:
printf("c=%f\n", c);
Ok, I'm compiling it and this is the output:
~$ cc ex2.c -o ex2
ex2.c: In function ‘main’:
ex2.c:13:3: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
printf("c=%f\n", c);
^
The error clearly tells me that It cannot convert from int to float, But this does not prevent the compiler from making an object file, and the confusing part is here, where I run the object file:
~$ ./ex2
x=10
y=4.500000
c=c
c=4.500000
As you can see printf prints the last float value it printed before. I tested it with other values for y and in each case it prints the value of y for c. Why this happen?
Your compiler is warning you about the undefined behaviour you have. Anything can happen. Anything from seeming to work to nasal demons. A good reference on the subject is What Every C Programmer Should Know About Undefined Behavior.
Normally, int can convert to double just fine:
int i = 10;
double d = i; //works fine
printf is a special kind of function. Since it can take any number of arguments, the types have to match exactly. When given a char, it is promoted to int when passed in. printf, however, uses the %f you gave it to get a double. That's not going to work.
Here is how one would implement their own variadic function, taken from here:
int add_nums(int count, ...)
{
int result = 0;
va_list args;
va_start(args, count);
for (int i = 0; i < count; ++i) {
result += va_arg(args, int);
}
va_end(args);
return result;
}
count is the number of arguments that follow. There is no way for the function to know this without being told. printf can deduce it from the format specifiers in the string.
The other relevant part is the loop. It will execute count times. Each time, it uses va_arg to get the next argument. Notice how it gives va_arg the type. This type is assumed. The function needs to rely on the caller to pass in something that gets promoted to int in order for the va_arg call to work properly.
In the case of printf, it has a defined list of format specifiers that each tell it which type to use. %d is int. %f is double. %c is also int because char is promoted to int, but printf then needs to represent that integer as a character when forming output.
Thus, any function that takes variadic arguments needs some caller cooperation. Another thing that could go wrong is giving printf too many format specifiers. It will blindly go and get the next argument, but there are no more arguments. Uh-oh.
If all of this isn't enough, the standard explicitly says for fprintf (which it defines printf in terms of) in C11 (N1570) §7.21.6.1/9:
If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
All in all, thank your compiler for warning you when you are not cooperating with printf. It can save you from some pretty bad results.
Since printf is a varargs function, parameters cannot be converted automatically to the type expected by the function. When varargs functions are called, parameters undergo certain standard conversions, but these will not convert between different fundamental types, such as between integer and float. It's the programmer's responsibility to ensure that the type of each argument to printf is appropriate for the corresponding format specifier. Some compilers will warn about mismatches because they do extra checking for printf, but the language doesn't allow them to convert the type -- printf is just a library function, calls to it must follow the same rules as any other function.
Here is a very general description, which may be slightly different depending on the compiler in use...
When printf("...",a,b,c) is invoked:
The address of the string "..." is pushed into the stack.
The values of each of the variables a, b, c are pushed into the stack:
Integer values shorter than 4 bytes are expanded to 4 bytes when pushed into the stack.
Floating-point values shorter than 8 bytes are expanded to 8 bytes when pushed into the stack.
The Program Counter (or as some call it - Instruction Pointer) jumps to the address of function printf in memory, and execution continues from there.
For every % character in the string pointed by the first argument passed to function printf, the function loads the corresponding argument from the stack, and then - based on the type specified after the % character - computes the data to be printed.
When printf("%f",c) is invoked:
The address of the string "%f" is pushed into the stack.
The value of the variable c is expanded to 4 bytes and pushed into the stack.
The Program Counter (or as some call it - Instruction Pointer) jumps to the address of function printf in memory, and execution continues from there.
Function printf sees %f in the string pointed by the first argument, and loads 8 bytes of data from the stack. As you can probably understand, this yields "junk data" in the good scenario and a memory access violation in the bad scenario.

Implicit conversion in C?

What's going on here:
printf("result = %d\n", 1);
printf("result = %f\n", 1);
outputs:
result = 1
result = 0.000000
If I ensure the type of these variables before trying to print them, it works fine of course. Why is the second print statement not getting implicitly converted to 1.00000?
In the second case you have a mismatch between your format string and the argument type - the result is therefore undefined behavio(u)r.
The reason the 1 is not converted to 1.0 is that printf is “just” a C function with a variable number of arguments, and only the first (required) argument has a specified type (const char *). Therefore the compiler “cannot” know that it should be converting the “extra” argument—it gets passed before printf actually reads the format string and determines that it should get a floating point number.
Now, admittedly your format string is a compile-time constant and therefore the compiler could make a special case out of printf and warn you about incorrect arguments (and, as others have mentioned, some compilers do this, at least if you ask them to). But in the general case it cannot know the specific formats used by arbitrary vararg functions, and it's also possible to construct the format string in complex ways (e.g. at runtime).
To conclude, if you wish to pass a specific type as a “variable” argument, you need to cast it.
An undefined behavior. An int is being treated as float
The short answer is that printf isn't really C++. Printf is a C function which takes a variable argument list, and applies the provided arguments to the format string basis the types specified in the format string.
If you want any sort of actual type checking, you should use streams and strings - the actual C++ alternatives to good old C-style printf.
Interesting, presumably it's fine if your put '1.0'
I suppose the printf only gets the address of the variable, it has no way of knowing what it was. But I would have thought the compiler would have the decency to warn you.

Resources