Как переносить строку в необычный целочисленный тип?
Вопрос
Немного предыстории: если бы я хотел использовать, например, scanf()
для преобразования строки в стандартный целочисленный тип, например uint16_t
, я & # 8217; буду использовать SCNu16
из <inttypes.h>
вот так:
#include <stdio.h>
#include <inttypes.h>
uint16_t x;
char *xs = "17";
sscanf(xs, "%" SCNu16, &x);
Но более необычный целочисленный тип, такой как pid_t
, не имеет ничего подобного; printf()
поддерживает только нормальные целочисленные типы. Чтобы преобразовать другой способ в переносимое intmax_t
a PRIdMAX
, я могу привести его к <=> и использовать <=>, например:
#include <stdio.h>
#include <inttypes.h>
#include <sys/types.h>
pid_t x = 17;
printf("%" PRIdMAX, (intmax_t)x);
Тем не менее, похоже, что не существует способа переноса <=> в <=>. Так что это мой вопрос: как это сделать переносимо?
#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! /*
Я подумал о <=> переходе к <=>, а затем проверял, что значение находится в пределах <=> & # 8217; s ограничен до приведения к <=>, но, похоже, не существует способа чтобы получить максимальные или минимальные значения для <=>.
Решение
Существует одно надежное и переносимое решение, которое заключается в использовании strtoimax()
и проверке на переполнение.
То есть я анализирую intmax_t
, проверяю наличие ошибки в pid_t
, а затем также проверяю, подходит ли она " соответствует " в scanf()
путем его приведения и сравнения с исходным значением strtoll()
.
#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;
...
}
Невозможно использовать <=>, потому что (как я сказал в комментарии) <=> не будет обнаруживать переполнения . Но я ошибся, сказав, что ни одна из функций, связанных с <=>, не принимает <=>; <=> делает! Р>
Также не будет возможно использовать что-либо кроме <=>, если вы не знаете размер целочисленного типа (в данном случае <=>).
Другие советы
Это зависит от того, насколько портативным вы хотите быть. POSIX говорит, что pid_t
- это целочисленный тип со знаком, используемый для хранения идентификаторов процессов и идентификаторов групп процессов. На практике вы можете с уверенностью предположить, что long
достаточно велико. В противном случае ваш intmax_t
должен быть достаточно большим (поэтому он будет принимать любые действительные <inttypes.h>
); проблема в том, что этот тип может принимать значения, которые не являются допустимыми в <unistd.h>
. Вы застряли между камнем и наковальней.
Я бы использовал <sys/types.h>
и не очень беспокоился об этом, за исключением неясного комментария где-то, который 100-летний археолог найдет и наблюдает, и объясняет причину, по которой 256-битный процессор останавливается при передаче 512-битное значение как <=>.
POSIX 1003.1-2008 теперь доступен в Интернете (все 3872 страницы этого, в PDF и HTML). Вы должны зарегистрироваться (бесплатно). Я узнал об этом из книжного магазина Open Group .
Я вижу только то, что это должен быть целочисленный тип со знаком. Понятно, что все допустимые целочисленные значения со знаком вписываются в <=>. Я не могу найти в <=> или <=> никакой информации, указывающей на PID_T_MAX или PID_T_MIN или другие подобные значения (но я только сегодня вечером получил к ней доступ, поэтому она может быть скрыта там, где я ее не искал) , OTOH, я поддерживаю мой оригинальный комментарий - я считаю, что 32-битные значения прагматически адекватны, и я все равно буду использовать <=>, что будет 64-битным на 8-битных машинах. Я предполагаю, что примерно худшее, что может случиться, это то, что процесс с соответствующими привилегиями считывает слишком большое значение и посылает сигнал неправильному процессу из-за несоответствия типов. Я не уверен, что буду беспокоиться об этом.
... оооо! ... p400 в разделе <=>
Реализация должна поддерживать одну или несколько сред программирования, в которых ширина blksize_t, pid_t, size_t, ssize_t и suseconds_t не больше ширины типа long.
Если вы действительно обеспокоены, вы можете _assert(sizeof(pid_t) <= long)
или любой другой тип, который вы выберете для своей вещи '%'.
Как описано в этот ответ , в спецификации сказано signed int
. Если «int» меняется, ваш «% u» по определению меняется вместе с ним.