What I've done sometimes in the past is used a larger data type, and then when I convert to my smaller type, immediately convert back to the larger type and check that the value didn't change.
For example, say you used an int64_t instead, then you might have something like:
int64_t my_pid64;
/* ... parse string value into my_pid64 ... */
pid_t my_pid = (pid_t) my_pid64;
if ((int64_t) my_pid != my_pid64) /* check that value was not out of range of pid_t */
{
/* ... handle error ... */
}
There's no great option for the larger data type to use. "long" used to be the largest primitive integer data type but that's not true on some common compilers/architectures anymore--even for Linux (see comments below). Meanwhile, the intmax_t type has poor library support. The result is that int64_t is sometimes more useful in practice.
But, basically your options for a larger data type are probably long, int64_t and intmax_t.