c passing unsigned long long value - c

How can the last two lines of one function be
printf("disk_free_blocks returning %llu\n",item_int3);
return (item_int3);
and out put
disk_free_blocks returning 233012428800
returning to calling function as
part_avail=disk_free_blocks(DiskParts[part_index].part_name,DISK_AVAIL);
if (DEBUG) printf("DiskParts[%d].part_name=%s has %llu free.\n",part_index,DiskParts[part_index].part_name,part_avail);
and output be
DiskParts[0].part_name=/dev/sda1 has 1084194816 free.
??
unsigned long long part_avail, item_int3;

The two output numbers are:
0x00000036409F8000
and
0x409F8000
It appears that the return type (which you haven't shown) isn't large enough to accomodate a 64-bit value, so the compiler simply truncates (which is the behavior required by the standard, for narrowing conversions on unsigned integers).
I tend to think these are both wrong: 233 billion blocks, with a 512 byte block, that is 100 TB. Not likely. 1 billion blocks is then around 512 GB, which might or might not match your actual /dev/sda1 free space.
unsigned long long part_avail, item_int3;
This doesn't make sense, since the usage of these two variables is in different scopes. Are there two variables with the same name? Or are you using global variables?
Possibly item_int3 inside the function is only 32 bits, printf reads past the end of the argument list during vararg processing, and the stack just happened to have 0x00000036 on it, so that got printed by the %llu specifier. When returned as an unsigned long long, the compiler properly zero-extends the value, and then the caller pushes the full 64-bit value onto the stack, which is retrieved and printed correctly during printf vararg processing of the %llu format code.

The return value was truncated to 32 bits somewhere along the way. Perhaps disk_free_blocks is declared as returning an int?

RESOLVED
disk_free_blocks() resided in a different file than process_copy_out(), the function making the call to disk_free_blocks.
The fix was adding a function prototype to inform the compiler.
Thank you everyone for your help.

Related

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.

Variable affected by type conversion

I have always thought that when a variable is typecast, a copy of it results and any changes affect that temporary variable. But, the screenshot below indicates otherwise. Apparently, the original variable is what changes. Why? I am curious because I have never seen anything similar.
sprintf nul-terminates the string it outputs into the provided buffer. As the %010lu format specifier requests a number padded to be at least 10 digits long, you are consistently overflowing crc_buf and triggering UB. In your specific case, the least significant byte of crc gets trampled.
Make crc_buf 11 characters or more, and use snprintf instead of sprintf to catch this class of errors. For maximal portability, you could also use the PRIu32 format macro instead of casting:
snprintf(crc_buf, sizeof crc_buf, "%10" PRIu32, crc);

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.

strange behavior of scanf for short int

the code is as follows:
#include <stdio.h>
main()
{
int m=123;
int n = 1234;
short int a;
a=~0;
if((a>>5)!=a){
printf("Logical Shift\n");
m=0;
}
else{
printf("Arithmetic Shift\n");
m=1;
}
scanf("%d",&a);
printf("%d\n", m);
}
after the line scanf("%d",&a); the value of m becomes 0.
I know it may be caused by the scanf: a's type is short and the input's type is int. But How can this affect the value of m ?
Thanks a lot !
The most likely reason for m being 0 in your snippet is because you assign m to have this value in the body of your if-statement, but since the code contains undefined behavior no one can say that for sure.
The likely story about passing a short* when scanf expects an int*
Assuming sizeof(short) = 2 and sizeof(int) == 4.
When entering your main function the stack on which the variables reside would normally look something like the below:
_
|short int (a) : scanf will try to read an int (4 bytes).
|_ 2 bytes : This part of memory will most
|int (n) : likely be overwritten
| :..
|
|_ 4 bytes
|int (m)
|
|
|_ 4 bytes
When you read a %d (ie. an int) into the variable a that shouldn't affect variable m, though n will most likely have parts of it overwritten.
Undefined Behavior
Though it's all a guessing game since you are invoking what we normally refer to as "undefined behavior" when using your scanf statement.
Everything the standard doesn't guarantee is UB, and the result could be anything. Maybe you will write data to another segment that is part of a different variable, or maybe you might make the universe implode.
Nobody can guarantee that we will live to see another day when UB is present.
How to read a short int using scanf
Use %hd, and be sure to pass it a short*.. we've had enough of UB for one night!
Assuming that int and short are four- and two-byte integers, respectively, on your platform (which is a likely assumption, but not guaranteed by the standard), you're asking scanf to read in an integer and store it in four bytes: the two bytes of b, and whatever two bytes follow it in memory. (Well, technically this is undefined behavior, and no specific behavior is guaranteed; but that's what it's likely to do.) Apparently your compiler is using the two bytes after b as the first two bytes of m. Which is a bit surprising — I certainly wouldn't expect b and m to be adjacent, and it rather implies that your compiler isn't aligning shorts and ints to the beginning of four-byte blocks — but perfectly legal.
You can see better what's going on if you add
printf("&a: %08X\n&m: %08X\n", (int)&a, (int)&m);
which will show you where a and m are stored, relative to each other. (Just as a test, I mean. You wouldn't want that in "real" code.)
You are correct, %d expects and writes an int. If you enter a value less than 65535, it fits in the bytes outside short, so you see 0 when you print a back. I tried reading a short and printing it back; I entered 65536123, and got 123, which makes perfect sense (65536 occupies precisely 16 bits; you see the remaining 123 through the two bytes of the short). This behavior is dangerous, because the other two bytes of the short end up in a "variable next door" to the short, which is very, very bad. I hope this should convince you not to do it.
P.S. To read a short with scanf, declare a temporary int variable, read the value into it using scanf, and then cast it to short.
You're invoking Undefined Behavior when passing a pointer to a non-int to scanf's %d.
Likely, the compiler introduces padding bytes for alignment purposes and the values get stored in the padding bytes and not the "useful" bytes.
However, the compiler is free to do anything from raise a segfault / access violation to invoke nasal demons.
If you had actually used variable n, then it would probably have been the one that got clobbered, rather than m. Since you didn't use n, the compiler optimized it away, and that means that it was m that got clobbered by scanf() writing 4 bytes (because it was told that it got a pointer to a (4-byte) integer) instead of the 2 bytes. This depends on quite a lot of details of your hardware, such as endian-ness and alignment (if int had to be aligned on a 4-byte boundary, you wouldn't see the problem; I guess you are on an Intel machine rather than, say, PowerPC or SPARC).
Don't fib to your compiler - even accidentally. It will get its own back.

Integer Overflow

I have an unsigned long long that I use to track volume. The volume is incremented by another unsigned long long. Every 5 seconds I print this value out and when the value reaches the 32 bit unsigned maximum the printf gives me a negative value. The code snippet follows:
unsigned long long vol, vold;
char voltemp[10];
vold = 0;
Later...
while (TRUE) {
vol = atoi(voltemp);
vold += vol;
fprintf(fd2, "volume = %llu);
}
What am I doing wrong? This runs under RedHat 4 2.6.9-78.0.5.ELsmp gcc version 3.4.5
Since you say it prints a negative value, there must be something else wrong, apart from your use of atoi instead of strtoull. A %llu format specifier just doesn't print a negative value.
It strongly looks like the problem is the fprintf call. Check that you included stdio.h and that the argument list is indeed what is in the source code.
Well I can't really tell because your code has syntax errors, but here is a guess:
vol = atoi(voltemp);
atoi converts ascii to integer. You might want to try atol but that only gets it to a long, not a long long.
Your C standard library MIGHT have atoll.
You can't use atoi if the number can exceed the bounds of signed int.
EDIT: atoll (which is apparently standard), as suggested, is another good option. Just keep in mind that limits you to signed long long. Actually, the simplest option is strtoull, which is also standard.
Are you sure fprintf can take in a longlong as a parameter, rather than a pointer to it? It looks like it is converting your longlong to an int before passing it in.
I'd guess the problem is that printf is not handling %llu the way you think it is.
It's probably taking only 32 bits off the stack, not 64.
%llu is only standard since C99. maybe your compiler likes %LU better?
For clarification the fprintf statement was copied incorrectly (my mistake, sorry). The fprintf statement should actually read:
fprintf(fd2, "volume = %llu\n", vold);
Also, while admittedly sloppy the maximum length of the the array voltemp is 9 bytes (digits) which is well within the limits of a 32-bit integer.
When I pull this code out of the program it is part of and run it in a test program I get the result I would expect which is puzzling.
If voltemp is ever really big, you'll need to use strtoull, not atoi.

Resources