Length modifier vs conversion specifier in C - c

I'm new to C and I'm struggling to understand the difference between the two. Can someone use an example with both? Please correct my logic if I'm wrong but this is the way I understand the following:
int a = 10;
printf("%d\n", a);
The purpose of %d is to notify the compiler that the variable we want to print is of int type. At least, that's the way I've been thinking of it so far. Thank you.

In a format string like %ld, the letter l would be the length modifier, which indicates to the standard library function (not the compiler) that you want the associated argument to be interpreted as a long int. There's a handy chart showing the standard interpretations made by various length/conversion character combinations here on cplusplus.com.
The compiler knows the types of all your variables at compile time, but the printf function doesn't have a way to determine the types of arguments at run time because of how variadic functions work. You can experiment for yourself and see how different combinations of length modifiers and conversion specifiers can yield completely different results for the same data passed to printf.

The *printf() family of functions take variable number of arguments, so you need to pass a format string with specifiers that let the function know the type of the currently parsed argument.
A simple example would be like this1
#include <stdio.h>
#include <stdarg.h>
int xprintf(const char *format, ...)
{
char chr;
int count;
va_list va;
count = 0;
va_start(va, format);
while ((chr = *format++) != '\0')
{
if ((chr == '%') && ((chr = *format++) == 'd'))
{
int argument;
argument = va_arg(va, int);
count += printf("%d", argument);
}
else
{
fputc(chr, stdout);
count += 1;
}
}
return count;
}
int
main(void)
{
xprintf("example %d\n", 4);
return 0;
}
The compiler does not need to know anything about it, but somtimes it does and it helps you know when you pass the wrong argument type by mistake, but code with wrong, extra, less arguments can compile and then the behavior of the program can't be specified in those cases.
1Notice that I've used the standard printf() as an auxiliary function to print the integer.

The conversion specification tells printf both the type of the argument and how you want to format the output for it. The length modifier is a part of the conversion specification, and it gives printf additional type information for the corresponding argument.
printf is a variadic function, which means arguments of certain types are promoted to a more limited set of types; arguments of type char and short are promoted to int, arguments of type float are promoted to double, etc. The length modifier helps you communicate the original type to printf, so it will properly convert the promoted argument back to the original type.
So, examples:
int aRegularInt = 64;
short aShortInt = 64;
char aReallyShortInt = 64;
printf( "aRegularInt = %d\n", aRegularInt );
printf( "aShortInt = %hd\n", aShortInt );
printf( "aReallyShortInt = %hhd\n", aReallyShortInt );
printy( "aReallyShortInt = %c\n", aReallyShortInt );
The conversion specification %d indicates that the argument has type int and that the output should be a string of decimal digits with a leading - for negative values. There is no length modifier.
The conversion specification %hd indicates that the argument has type short int. The output is the same as above. h is the length modifier.
The conversion specification %hhd indicates that the argument has type char. The output is the same as above. In this case the length modifier is hh.
The conversion specification %c indicates that the argument has type int, and that the output should be the glyph corresponding to that character code (in ASCII, the character for code 64 is #).

Related

Narrowing conversion from 'long' to signed type 'char' is implementation-defined (strtol function in C)

I'm trying to convert an inputted character to an integer by using strtol. Here's part of the source code:
char option;
char *endptr;
printf("=========================================Login or Create Account=========================================\n\n");
while(1) {
printf("Welcome to the Bank management program! Would you like to 1. Create Account or 2. Login?\n>>> ");
fgets(&option, 1, stdin);
cleanStdinBuffer();
option = strtol(&option, &endptr, 10);
In the strtol function, I'm getting a warning saying:
Clang-Tidy: Narrowing conversion from 'long' to signed type 'char' is implementation-defined
Can someone tell me what I'm doing wrong?
Clang-Tidy is warning you about the implicit conversion you are doing here where you are assign the long return value of strtol to a char:
option = strtol(&option, &endptr, 10);
If this is intentional and you are sure the value will be in the [-128,127] range that isn't necessarily an issue (it's just a warning), but even then I would advice to explicitly cast the return-type of strtol, use int8_t instead of char and not reuse the option variable for the return value. In other words:
int8_t value = (int8_t)strtol(&option, &endptr, 10);
If it wasn't intentional I would recommend you to simply use long as type for the variable you assign the return value of strtol, so:
long value = strtol(&option, &endptr, 10);
What Clang-tidy doesn't warn you about is that the first argument to strtol should be a pointer to a char buffer containing a 0-terminated string, not a pointer to a single char. This is also an issue for fgets. There are two ways to solve this, either:
Make option a char array of at least two chars,
Use fgetc instead and modify your code into something like this:
int option = fgetc(stdin);
if (option == '1') {
/*Create Account */
} else if (option == '2') {
/* Login */
}
else {
/* Error */
}
I think the latter looks much cleaner.
char can only hold a very little subset of the long values. strtol returns long and you assign it to char.
This call of fgets
fgets(&option, 1, stdin);
always sets the character option to the terminating zero character '\0' provided that the user did not interrupt the input.
Here is a demonstrative program.
#include <stdio.h>
int main(void)
{
char c = 'A';
printf( "Before calling fgets c = %d\n", c );
fgets( &c, 1, stdin );
printf( "After calling fgets c = %d\n", c );
return 0;
}
The program output is
Before calling fgets c = 65
After calling fgets c = 0
independent on what the user will enter. Here is the value 65 is the ASCII code of the character 'A' that was stored in the variable c before calling fgets.
If you want to enter a character then you should use
scanf( " %c", &input );
Pay attention to the blank before the conversion specifier.
After this call you can check whether the user typed a digit like
#include <ctypes.h>
//...
if ( isdigit( ( unsigned char )input ) ) input = input - '0';
and set the variable input to the corresponding integer value in the range [0, 9].

how to correctly sscanf a decimal to a char

PVS-Studio gave me a warning about this :
char c;
sscanf(line, "%d", &c);
I changed %d to %c but this created a bug because "c" now contains the ASCII value of the number and not the decimal one, so I went back to "%d".
So what's the correct specifier to ? is there another solution ?
c is a char. You asked to scan an int. PVS-Studio did right in warning you. Change the type of c to int and scan for a %d.
There are multiple solutions for your problem:
you can specify the correct destination type:
char c;
if (sscanf(line, "%hhd", &c) == 1) {
/* successful conversion */
...
}
you can use an intermediary variable:
char c;
int cc;
if (sscanf(line, "%d", &cc) == 1) {
/* successful conversion */
c = cc;
...
}
you can use different conversion function:
#include <stdlib.h>
...
char c;
c = atoi(line); // no error handling, return 0 if not a number
Note however that in all cases, if the numeric value converted by sscanf() or atoi() is outside the range of type char, the behavior is undefined. Most current system will just use the low order byte of the conversion result, but the C Standard does not guarantee it.

Some more explanation about an array example I found online

I'm just starting out to learn program in C and am trying to get my head around arrays. I found this example online but don't understand how it works. Here is the code:
#include <stdio.h>
int main () {
int n[ 10 ]; /* n is an array of 10 integers */
int i,j;
/* initialize elements of array n to 0 */
for ( i = 0; i < 10; i++ ) {
n[ i ] = i + 100; /* set element at location i to i + 100 */
}
/* output each array element's value */
for (j = 0; j < 10; j++ ) {
printf("Element[%d] = %d\n", j, n[j] );
}
return 0;
}
What I don't understand here is how int j links to int i. In the output, at the printf line, I don't understand why it is not:
printf("Element[%d] = %d\n", i, n[i]);
Why do we need another int j?
And another thing I don't understand is the %d's in that line. Why %d and not %i? d hasn't been declared anywhere right?
Any deeper explanation than on the website where I found this example would be appreciated. Thank you.
Why do we need another int j?
You don't. I'm assuming the author was just demonstrating that you can use any variable to index into an array, as long as the variable has the right type (pretty much any integral type) and holds a value in the correct range.
Array indices can be any integral expression, whether it's an integral constant (a[5]), a variable (a[i], a[j]), or a more complex expression (a[i+j], a[foo(i)], a[1+j*k/2], etc.). All that matters is that the expression has an integral type, and that its result is in the correct range (0 to N-1, where N is total number of elements in the array).
And another thing I don't understand is the %d's in that line. Why %d and not %i? d hasn't been declared anywhere right?
%d is a conversion specifier - it has nothing to do with any variable, and does not need to be declared separately. Conversion specifiers in the format string tell printf the number and types of additional arguments, and how to format those values for display.
In the line
printf("Element[%d] = %d\n", i, n[i]);
the %d specifiers in the format string tell printf that i and n[i] both have type int, and that they are to be formatted as decimal integers (as opposed to hex or octal, for example).
Here's an incomplete list of conversion specifiers:
Specifier Argument Type Output format
--------- ------------- -------------
%d,%i int decimal integer
%u unsigned int decimal integer (non-negative)
%f double decimal floating point
%x,%X unsigned int hexadecimal integer (non-negative)
%o unsigned int octal (non-negative)
%c char single character
%s char * text
Check your handy C reference manual for a complete list. If you don't have a handy C reference manual, I recommend Harbison & Steele's C: A Reference Manual.
Agreeing with # Eugene Sh, that in this case, j is indeed an extra variable.
The coding would be as shown below, and would return the same output.
for (i = 0; i < 10; i++) {
printf("Element[%d] = %d\n", i, n[i]);
}
And another thing I don't understand is the %d's in that line.
The printf() family of functions uses % character as a placeholder. This is a format specifier and does not require initialization. When a % is encountered, printf reads the characters following the % to determine what to do:
%s - Take the next argument and print it as a string
%d - Take the next argument and print it as an int
Sooner or later you might also encounter %s, which indicates that you're expecting a String to be your first print parameter.
Hope this helps.
What I don't understand here is how int j links to int i
They don't "link" and there is actually no connection between the two. The author simply used another integer (j) to avoid confusion.
As for your suggestion:
In the output, at the printf line, I don't understand why it is not:
printf("Element[%d] = %d\n", i, n[i]);
It is perfectly fine to do so if you make sure to use i as the loop variable for the second loop as well like so:
for (i = 0; i < 10; i++ ) {
printf("Element[%d] = %d\n", i, n[i] );
}
And another thing I don't understand is the %d's in that line. Why %d and not %i? d hasn't been declared anywhere right?
%d is a type modifier that tells the printf function to replace it with a decimal number when printing. It has nothing to do with some sort of variable named d. Your confusion might originated from the fact that there is also a %i modifier for printf but it is less commonly used. Refer to this wiki for more details.

%n not working in printf

I have a problem, %n in printf doesn't work, i'm using Dev-Cpp 5.3.0.4 on win7
#include<stdio.h>
int main(void)
{
int n;
char *x;
gets(x);
printf("\n%s%n\n",x,&n);
printf("n: %d\n",n);
return 0;
}
output:
hello how are you?
hello how are you?n: 2046
--------------------------------
Process exited with return value 0
Press any key to continue . . .
why? how can i solve? thanks in advance ;)
Have a look at the printf manpage:
n The number of characters written so far is stored into the integer indicated by the int
* (or variant) pointer argument. No argument is converted.
So, you'll have to pass a pointer to an int. Also, as Xavier Holt pointed out, you'll have to use a valid buffer to read into. Try this:
#include <stdio.h>
int main(void)
{
int n;
char x[1000];
fgets(x, 1000, stdin);
printf("\n%s%n\n",x,&n);
printf("n: %d\n",n);
return 0;
}
This code works for me.
You need to pass a pointer to n.
The argument to n needs to be a pointer to a signed int, not a singed int.
That's not how to use the "%n" specifier.
See the C99 Standard.
n
The argument shall be a pointer to signed integer into which is written the number of characters written to the output stream so far by this call to fprintf. No argument is converted, but one is consumed. If the conversion specification includes any flags, a field width, or a precision, the behavior is undefined.
Also you need some place to store the input (hint: initialize x)

How to use fprintf with a dynamically allocated unsigned char* array in C

I'm trying to write a unsigned char* array to a file.
A minimal working example of the code that I've tried so far is (assume fp is correctly initialised):
unsigned char* x; int i; int j; int sizeOfx;
for (i=0; i<n; i++) {
x = // getter function with parameter i
sizeOfx = // getter function that returns the number of elements in x
for (j=0; j<sizeOfx; j++) {
fprintf(fp,"%s",x[j]);
}
}
i.e. I'm going through the char array one element at a time and writing it to the file.
However, I get the error
format ‘%s’ expects argument of type ‘char*’, but argument 3 has type ‘int’ [-Wformat]
How can I fix this?
Thank you very much in advance!
%s is used to print a string, so you would need to change x[j] to x to 'fix' your error.
As you really seem to want to write each char separately, you need to think how you want to store the 'elements' (characters of the string).
You can use %c to store their value as an ASCII value in the file (which is basically identical when using %s and x, unless you want to write more/less than the complete string).
Or you can store the 'element values' as integers, ie textual values using the characters 0-9, using %d. Or maybe hexadecimal using %x using the characters 0-9 and a-f.
So it is up to you how you want to store the 'elements' of x.
Try %c (for character printing) instead of %s.
Alternatively you could write the print line as follows:
fprintf(fp,"%s",(char*)x[j]);
which statically casts that pointer at x[j] back to a string (essentially), then if the string coming into that loop were "abcdef", then the output would be as follows:
"abcdefbcdefcdefdefeff"
This is where c is completely open to do what you want to do.

Resources