How to portably convert a string into an uncommon integer type?
Solution 1
There is one robust and portable solution, which is to use strtoimax()
and check for overflows.
That is, I parse an intmax_t
, check for an error from strtoimax()
, and then also see if it "fits" in a pid_t
by casting it and comparing it to the original intmax_t
value.
#include <inttypes.h>
#include <stdio.h>
#include <iso646.h>
#include <sys/types.h>
char *xs = "17"; /* The string to convert */
intmax_t xmax;
char *tmp;
pid_t x; /* Target variable */
errno = 0;
xmax = strtoimax(xs, &tmp, 10);
if(errno != 0 or tmp == xs or *tmp != '\0'
or xmax != (pid_t)xmax){
fprintf(stderr, "Bad PID!\n");
} else {
x = (pid_t)xmax;
...
}
It is not possible to use scanf()
, because, (as I said in a comment) scanf()
will not detect overflows. But I was wrong in saying that none of the strtoll()
-related functions takes an intmax_t
; strtoimax()
does!
It also will not work to use anything else than strtoimax()
unless you know the size of your integer type (pid_t
, in this case).
Solution 2
It depends on exactly how portable you want to be. POSIX says that pid_t
is a signed integer type used to store process IDs and process group IDs. In practice, you could assume with safety that long
is big enough. Failing that, your intmax_t
must be big enough (so it will accept any valid pid_t
); the trouble is, that type could accept values that are not legitimate in pid_t
. You're stuck between a rock and a hard place.
I would use long
and not worry very much about it except for an obscure comment somewhere that a software archaeologist of 100 years hence will find and observe gives a reason why the 256-bit CPU is creaking to a halt when handed a 512-bit value as a pid_t
.
POSIX 1003.1-2008 is now available on the web (all 3872 pages of it, in PDF and HTML). You have to register (free). I got to it from the Open Group Bookstore.
All that I see there is that it must be a signed integer type. Clearly, all valid signed integer values fit into intmax_t
. I cannot find any information in <inttypes.h>
or <unistd.h>
that indicates PID_T_MAX or PID_T_MIN or other such values (but I've only just this evening got access to it, so it could be hidden where I haven't looked for it). OTOH, I stand by my original comment - I believe that 32-bit values are pragmatically adequate, and I would use long
anyway, which would be 64-bit on 8-bit machines. I suppose that roughly the worst thing that could happen is that an 'appropriately privileged' process read a value that was too large, and sent a signal to the wrong process because of a mismatch of types. I'm not convinced I'd be worried about that.
...oooh!...p400 under <sys/types.h>
The implementation shall support one or more programming environments in which the widths of blksize_t, pid_t, size_t, ssize_t, and suseconds_t are no greater than the width of type long.
Related videos on Youtube
Teddy
Updated on April 17, 2022Comments
-
Teddy about 2 years
Some background: If I wanted to use for, for instance,
scanf()
to convert a string into a standard integer type, likeuint16_t
, I’d useSCNu16
from<inttypes.h>
, like this:#include <stdio.h> #include <inttypes.h> uint16_t x; char *xs = "17"; sscanf(xs, "%" SCNu16, &x);
But a more uncommon integer type like
pid_t
does not have any such thing; only the normal integer types are supported by<inttypes.h>
. To convert the other way, to portablyprintf()
apid_t
, I can cast it tointmax_t
and usePRIdMAX
, like this:#include <stdio.h> #include <inttypes.h> #include <sys/types.h> pid_t x = 17; printf("%" PRIdMAX, (intmax_t)x);
However, there does not seem to be a way to portably
scanf()
into apid_t
. So this is my question: How to do this portably?#include <stdio.h> #include <sys/types.h> pid_t x; char *xs = 17; sscanf(xs, "%u", &x); /* Not portable! pid_t might not be int! /*
I thought of
scanf()
ing to anintmax_t
and then checking that the value is withinpid_t
’s limits before casting topid_t
, but there does not seem to be a way to get the maximum or minimum values forpid_t
. -
Teddy over 15 yearsI heard that the next POSIX standard would only require it to fit into an intmax_t, so using long is out. What I actually did was depend on the GNU C library's docs saying pid_t would always be int, and commenting it. The code uses other glibc-specific stuff anyway, so this is not a real problem.
-
Teddy over 15 yearssizeof does not actually concern itself with the possible values of a type, only the storage requirements in bytes. So your code does not actually guarantee what you think it does. Also, the spec says "signed integer type", not "signed int". Big difference.