size_t
is declared as unsigned int
so it can't represent negative value.
So there is ssize_t
which is the signed type of size_t
right?
Here's my problem:
#include <stdio.h>
#include <sys/types.h>
int main(){
size_t a = -25;
ssize_t b = -30;
printf("%zu\n%zu\n", a, b);
return 0;
}
why i got:
18446744073709551591
18446744073709551586
as result?
I know that with size_t
this could be possible because it is an unsigned type but why i got a wrong result also with ssize_t
??
In the first case you're assigning to an unsigned type - a
. In the second case you're using the wrong format specifier. The second specifier should be %zd
instead of %zu
.
First of all you should check the actual size of the two types.
Something like the following snippet should do:
#include <stdio.h>
#include <unistd.h>
int main() {
printf( "sizeof( size_t ) = %d bytes\n",(int) sizeof( size_t) );
printf( "sizeof( ssize_t ) = %d bytes\n",(int) sizeof( ssize_t) );
return 0;
}
I get (64bit Linux, GCC v7.2) "8 bytes" in both cases, which is the same as long int and long long int, the maximum CPU-native integer value.
When the sizes are the same (and they should always be), size_t
can have "2x larger absolute values" than ssize_t
which, in turn, can have signed (that's either positive or negative) values.
If they were different, then the larger one would be ... larger and could thus accommodate for larger values.
But in the end, ssize_t
and size_t
are two different types used to "talk" about sizes, lengths, amounts of memory and so on.
The former is just ditching 1 bit for the value in order to gain the sign needed to signal some sort of error.
Finally, the two types are not interchangeable, not always at least.
When the size can go past 2^63 bytes/items the difference is clear.
size_t
won't overflow while ssize_t
will.
Under "normal" circumstances you can cast from one to the other one.
For the cases I mentioned earlier, you should never mix them.
Just as a reference, both strlen()
and malloc()
use size_t
, while both read()
and readv()
use ssize_t.
So, ssize_t
is not the signed version of size_t
as they have non-overlapping realms.
Then, to your questions, the two numbers you see differ by 5 units, that's exactly what you'd expect. What you see is the value of those two variables when seen as unsigned long
. Try printing them as signed long
(%ld
) instead.
... why i got a wrong result also with ssize_t
??
Use
ssize_t b = -30;
printf("%jd\n", (intmax_t) b);
Use a matching specifier, which for a negative ssize_t
is not %zu
nor certainly "%zd"
.
How to use “zd” specifier with printf()
?.
ssize_t b = -30;
printf("%zu\n", b); // problem.
ssize_t
does not have a C specified print specifier. C does not even specify ssize_t
.
Various extensions to C do specify ssize_t
and in a case of Linux, the print specifier also.
Linux Programmer's Manual does have:
z: A following integer conversion corresponds to a size_t
or ssize_t
argument",
printf("%zd\n", b); // OK for that Linux
POSIX B.2.12 Data Types has
ssize_t
This is intended to be a signed analog of size_t
. The wording is such that an implementation may either choose to use a longer type or simply to use the signed version of the type that underlies size_t
.
Since ssize_t
may (uncommonly) be wider than size_t
, using "%zd"
could invoke undefined behavior (UB). It is simple enough to cast to the widest standard signed type since C99 as intmax_t
and print.
printf("%jd\n", (intmax_t) b); // OK for general use
Overflow coz size_t is a UNSIGNED
when you try to set size_t as (-val)
you get overflow and get SIZE_T_MAX - val
for example:
size_t val = -20;
//val == 18446744073709551615 - 20;