Problem with using scanf function under stm32 - c

I've came across on some interesting thing during use of sscanf() for STM32F437:
uint8_t var;
st = sscanf(&line[off], "%x", &var);
st = sscanf(&line[off], "%hhx", &var);
When I'm trying to compile first line I get suggestion from gcc to use "%hhx" instead of "%x". But when I changed this line to the second one - suggestion from gcc disappeared, but the result of scanning is wrong.
When &line[off] points to following string: 52 then the first scanf(..."%x"...) is working correctly giving 0x52, but the second scanf(..."%hhx"...) produces result 0x34.
Seems like scanf("..."%hhx"...") interpretes 52 as a decimal value 52 and then converts it to hexadecimal value 0x34.
I am using arm-none-eabi-gcc version 9.2.0.
Did I miss something or this is some bug in scanf()?

You are linking against what is commonly refereed as "newlib-nano". Nano version of newlib comes with limited support from the standard library - it doesn't support all C99 length modifiers, like ll or hh in both printf and scanf.
The solution is to link against full implementation of newlib, so remove -specs=nano.specs or similar from the linking options, or don't use hh length modifier when compiling with newlib-nano or use other method of converting a string to an integer.

%x without a prefix before the x means scanf is expecting a pointer to unsigned int
%hh is used for signed char or unsigned char.
%hhx is used for signed char or unsigned char in hex format.
"%"SCNu8 is used for scanning uint8_t.
"%"SCNx8 is used for uint8_t in hex format.
uint8_t is most likely 100% equivalent to unsigned char on any system.
This means that here "%x", &var you lie to the compiler and (assuming 32 bit CPU) you tell it "go ahead and read 32 bit large integer), then pass a memory address where only 8 bits of valid data are stored. This is undefined behavior and anything can happen.
Speculating about why undefined behavior bugs manifest themselves in a certain way for your specific system is rather meaningless.
Instead, do this:
#include <inttypes.h>
uint8_t var;
st = sscanf(&line[off], "%"SCNx8, &var);
Please note that sscanf is a terribly slow and dangerous function and should not be used on embedded systems. Always use strtoul instead.

Related

Get last two characters from char array elements

Trying to print the array elements, like below
typedef struct Info
{
char MacAdd[2];
} Info;
char MAC[100];
sprintf(MAC, "%x:%x%c", Info->MacAdd[0], Info->MacAdd[1],'\0');
printf("MAC %s",MAC);
Got output is --> ffffff98:ffffffa4
How can get output like ---> 98:a4
The MacAdd array in your Info structure is declared as an array of char. But char is usually a signed type.
When you call printf, certain "default argument conversions" take place. Among other things, type char is promoted to int. And since 0x98 as a signed char is actually negative (it's -104), it is automatically sign extended when that happens -- that's where the extra ff's come from. The value ffffff98 is the 32-bit signed version of -104, so the value has been preserved.
There are at least three ways to fix this, in what I'd consider the order of most to least attractive:
Redeclare the MacAdd array as an array of unsigned char.
Change the printf format to "%hhx:%hhx". The hh tells printf that your value was originally a char, and this asks printf to undo the promotion and, in effect, strip the unwanted ff's back off.
Change the call to sprintf(MAC, "%x:%x", Info->MacAdd[0] & 0xff, Info->MacAdd[1] & 0xff);
As #Aconcagua points out in a comment, once you get rid of the ff's, you will probably also want to tweak your printf format a little bit more, to take care of single-digit addresses.
Footnote: Solution 3 above is reasonably terrible, and I wouldn't seriously recommend it. It's what we used to use back in the days of Ritchie's original C compiler, when neither unsigned char nor %hhx had been invented yet.

Why can I printf with the wrong specifier and still get output?

My question involves the memory layout and mechanics behind the C printf() function. Say I have the following code:
#include <stdio.h>
int main()
{
short m_short;
int m_int;
m_int = -5339876;
m_short = m_int;
printf("%x\n", m_int);
printf("%x\n", m_short);
return 0;
}
On GCC 7.5.0 this program outputs:
ffae851c
ffff851c
My question is, where is the ffff actually coming from in the second hex number? If I'm correct, those fs should be outside the bounds of the short, but printf is getting them from somewhere.
When I properly format with specifier %hx, the output is rightly:
ffae851c
851c
As far as I have studied, the compiler simply truncates the top half of the number, as shown in the second output. So in the first output, are the first four fs from the program actually reading into memory that it shouldn't? Or does the C compiler behind-the-scenes still reserve a full integer even for a short, sign-extended, but the high half shall be undefined behavior, if used?
Note: I am performing research, in a real-world application, I would never try to abuse the language.
When a char or short (including signed and unsigned versions) is used as a function argument where there is no specific type (as with the ... arguments to printf(format,...))1, it is automatically promoted to an int (assuming it is not already as wide as an int2).
So printf("%x\n", m_short); has an int argument. What is the value of that argument? In the assignment m_short = m_int;, you attempted to assign it the value −5339876 (represented with bytes 0xffae851c). However, −5339876 will not fit in this 16-bit short. In assignments, a conversion is automatically performed, and, when a conversion of an integer to a signed integer type does not fit, the result is implementation-defined. It appears your implementation, as many do, uses two’s complement and simply takes the low bits of the integer. Thus, it puts the bytes 0x851c in m_short, representing the value −31460.
Recall that this is being promoted back to int for use as the argument to printf. In this case, it fits in an int, so the result is still −31460. In a two’s complement int, that is represented with the bytes 0xffff851c.
Now we know what is being passed to printf: An int with bytes 0xffff851c representing the value −31460. However, you are printing it with %x, which is supposed to receive an unsigned int. With this mismatch, the behavior is not defined by the C standard. However, it is a relatively minor mismatch, and many C implementations let it slide. (GCC and Clang do not warn even with -Wall.)
Let’s suppose your C implementation does not treat printf as a special known function and simply generates code for the call as you have written it, and that you later link this program with a C library. In this case, the compiler must pass the argument according to the specification of the Application Binary Interface (ABI) for your platform. (The ABI specifies, among other things, how arguments are passed to functions.) To conform to the ABI, the C compiler will put the address of the format string in one place and the bits of the int in another, and then it will call printf.
The printf routine will read the format string, see %x, and look for the corresponding argument, which should be an unsigned int. In every C implementation and ABI I know of, an int and an unsigned int are passed in the same place. It may be a processor register or a place on the stack. Let’s say it is in register r13. So the compiler designed your calling routine to put the int with bytes 0xffff851c in r13, and the printf routine looked for an unsigned int in r13 and found bytes 0xffff851c.
So the result is that printf interprets the bytes 0xffff851c as if they were an unsigned int, formats them with %x, and prints “ffff851c”.
Essentially, you got away with this because (a) a short is promoted to an int, which is the same size as the unsigned int that printf was expecting, and (b) most C implementations are not strict about mismatching integer types of the same width with printf. If you had instead tried printing an int using %ld, you might have gotten different results, such as “garbage” bits in the high bits of the printed value. Or you might have a case where the argument you passed is supposed to be in a completely different place from the argument printf expected, so none of the bits are correct. In some architectures, passing arguments incorrectly could corrupt the stack and break the program in a variety of ways.
Footnotes
1 This automatic promotion happens in many other expressions too.
2 There are some technical details regarding these automatic integer promotions that need not concern us at the moment.

Different output with printf

I am trying to print the value of a particularly large value after reading it from the console. When I am trying to print it from two different ways, one directly after assigning, one with the return value from the strtol function, I get different output! Can someone please explain me why I am noticing two different outputs?
Input value is: 4294967290
Here is the code snippet.
long int test2 = strtol(argv[1], &string, 10);
printf("the strtol value is %ld\n", test2);
printf("the strtol function value is %ld\n", strtol(argv[1], &string, 10));
Output
the strtol value is -6
the strtol function value is 4294967290
You need to do two things:
Add the line #include <stdlib.h> at the beginning of your program. That will cause strtol to be declared. (You also need #include <stdio.h> in order to declare printf.)
Add -Wall to your compiler flags (if you are using gcc or clang). That will cause the compiler to tell you that you need to declare strtol, and it might even suggest which header to include.
What is going on is that you haven't declared strtol, with the result that the compiler assumes that it returns an int.
Since 4294967290 is 232-6, it is the 32-bit Two's-complement representation of -6. Because the compiler assumes that strtol returns an int, the code it produces only looks at the low-order 32 bits. Since you are assigning the value to a long, the compiler needs to emit code which sign-extends the int. In other words, it takes the low-order 32 bits as though they were a signed integer (which would be -6) and then widens that -6 to a long.
In the second call to printf, the return value of strtol is inserted in the printf argument list without conversion. You tell printf that the argument is a long (by using the l flag in %ld), and by luck the entire 64 bits are in the argument list, so printf reads them out as a long and prints the "correct" value.
Needless to say, all this is undefined behaviour, and the actual output you are seeing is in no way guaranteed; it just happens to work that way with your compiler on your architecture. On some other compiler or on some other architecture, things might have been completely different, including the bit-length of int and long. So the above explanation, although possibly interesting, is of no practical value. Had you simply included the correct header and thereby told the compiler the real return type of strtol, you would have gotten the output you expected.

Issues with sprintf

I have been trying to use sprintf to add " to the start and end of a integer, however when i use more than 10 digits the program returns the wrong number:
int data2 = 12345678910;
char data3[2];
sprintf(data3,"\"%i\"", data2);
send(data3);
The send function outputs the integer to the screen.
The result i am getting back is :
"-108508098"
The send function works as i use it elsewhere and it does what it is suppose to.
Before your edit, your issue is not only with sprintf (which BTW you should not use, prefer snprintf), it is with integral numbers in C (they have a limited amount of bits, e.g. 64 bits at most on my Linux desktop....; read wikipages on computer number format & C data types).
Your use of sprintf is completely wrong (you've got a buffer overflow, which is an undefined behavior). You should code:
char buffer[32];
snprintf(buffer, sizeof(buffer), "%i", data2);
sendsomewhere(buffer);
Notice that on POSIX send needs 4 arguments. You should rename your function to sendsomewhere
You should read more about <stdint.h> and <limits.h>
You probably want to use bignums (or at least int64_t or perhaps long long to represent numbers like 12345678910). Don't reinvent bignums (they are difficult to implement efficiently). Use some library like gmplib
If 64 bits are enough for you (so if your numbers would always be between -263 i.e. −9223372036854775808 and 263-1 i.e. 9223372036854775807), consider using long long (or unsigned long long) numbers of C99 or C11:
long long data2 = 12345678910;
char buffer[32];
snprintf(buffer, sizeof(buffer), "%lld", data2);
sendsomewhere(buffer);
If 64 bits are not enough, you should use bigints (but some recent compilers might provide some _int128_t type for 128-bits ints)
Don't forget to enable all the warnings & debug info when compiling (e.g. with gcc -Wall -Wextra -g), then learn how to use the debugger (e.g. gdb)
data2 is overflown by the value to which you have initialized it to.
data3 should be able to hold atleast 11 bytes if data2 is of int datatype (+1 for NULL termination), for which you have allocated only 2 bytes.
Here is an example code snippet:
#include <stdio.h>
int main(void)
{
unsigned long long data2 = 12345678910;
char data3[32];
snprintf(data3, sizeof(data3), "\"%llu\"", data2);
printf("%s\n", data3);
return 0;
}

printf with sizeof on 32 vs 64 platforms: how do I handle format code in platform independent manner?

I have some code that prints the amount of memory used by the program. The line is similar to this:
printf("The about of RAM used is %u", anIntVariable*sizeof(double) );
where anIntVariable is an int variable for the number of elements of the double array. Anyhow, on 32-bit systems I never had any problems but on 64-bit systems, I get a compiler warning about using "%u" for a unsigned long integer. Using "%lu" as the format code fixes the problem on 64-bit but causes the compiler to complain on 32-bit because the type is back to unsigned int. I've found that, indeed, sizeof(double) returns a different value on 32 vs 64 bit systems. I've found some webpage guides to convert code from 32 bit to 64 bit But I'd rather have code that works on both instead of just converting back and forth.
How do I write this line in a platform independent way? I know many ways I could do it using preprocessor directives but that seems like a hack. Surely there's an elegant way that I'm not realizing.
Portable printf identifiers are provided in the include file inttypes.h or here.
This include file has many portable identifiers for your specific runtime. For your example, you want PRIuPTR, which means "PRintf Identifier unsigned with size of up to a pointer's size".
Your example will then be:
printf("The amount of RAM used is %" PRIuPTR, anIntVariable*sizeof(double) );
Results on 64bit Linux with GCC 4.3 (int anIntVariable = 1):
$ gcc test.c -m32 -o test && ./test
The amount of RAM used is 8
$ gcc test.c -o test && ./test
The amount of RAM used is 8
For completeness sake, there are identifiers for scanf too, whose prefixes are SCN.
The return value of sizeof is a size_t. If you're using a C99 compliant compiler it looks like you can use %zd%zu for this.
D'oh: %zu (unsigned) of course. Thanks, ony.
First of all, you should match the "%" specifier with the actual data type you want to print. sizeof returns the data type size_t, and just as you shouldn't try to print a float using a "%d" specifier, you shouldn't try to print a size_t with "%u" or "%d" or anything that doesn't really mean size_t.
The other replies have given some good ways to handle this with newer compilers ("%z" and PRIu32), but the way we used to do this was simply to cast the size_t to unsigned long, and then print it using "%lu":
printf("The amount of RAM used is %lu", (unsigned long)(anIntVariable*sizeof(double)) );
This will not work on systems where size_t is wider than a long, but I don't know of any such systems, and I'm not even sure if the standard allows it.

Resources