Ответ 1
В первом случае вы назначаете неподписанный тип - a
. Во втором случае вы используете неверный спецификатор формата. Второй спецификатор должен быть %zd
вместо %zu
.
size_t
объявляется как unsigned int
, поэтому он не может представлять отрицательное значение.
Таким образом, существует ssize_t
, который является подписанным типом size_t
справа?
Вот моя проблема:
#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;
}
почему я получил:
18446744073709551591
18446744073709551586
как результат?
Я знаю, что с size_t
это может быть возможно, потому что это неподписанный тип, но почему я получил неверный результат и с ssize_t
??
В первом случае вы назначаете неподписанный тип - a
. Во втором случае вы используете неверный спецификатор формата. Второй спецификатор должен быть %zd
вместо %zu
.
Прежде всего, вы должны проверить фактический размер двух типов. Что-то вроде следующего фрагмента должно сделать:
#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;
}
Я получаю (64-битный Linux, GCC v7.2) "8 байт" в обоих случаях, что совпадает с long int и long long int, максимальное целое значение собственного процессора.
Когда размеры одинаковы (и они всегда должны быть), size_t
может иметь "в 2 раза больше абсолютных значений", чем ssize_t
который, в свою очередь, может иметь знаковые (положительные или отрицательные) значения.
Если бы они были другими, то больший был бы... больше и мог бы таким образом приспособиться к большим значениям.
Но, в конце концов, ssize_t
и size_t
- это два разных типа, используемых для "разговора" о размерах, длине, количестве памяти и так далее.
Первый просто сбрасывает 1 бит на значение, чтобы получить знак, необходимый для обозначения некоторой ошибки.
Наконец, два типа не являются взаимозаменяемыми, по крайней мере, не всегда. Когда размер может превышать 2 ^ 63 байта/элемента, разница становится очевидной. size_t
не переполнится, а ssize_t
будет.
При "нормальных" обстоятельствах вы можете разыграть одного из них. Для случаев, которые я упоминал ранее, вы никогда не должны смешивать их.
В качестве ссылки оба strlen()
и malloc()
используют size_t
, а read()
и readv()
используют ssize_t.
Таким образом, ssize_t
не является подписанной версией size_t
как они имеют непересекающиеся области.
Затем, на ваши вопросы, два числа, которые вы видите, отличаются на 5 единиц, это именно то, что вы ожидаете. То, что вы видите, является значением этих двух переменных, когда рассматривается как unsigned long
. Попробуйте вместо этого напечатать их со signed long
(%ld
).
... почему я получил неправильный результат также с
ssize_t
??
использование
ssize_t b = -30;
printf("%jd\n", (intmax_t) b);
Используйте соответствующий спецификатор, который для отрицательного ssize_t
не является %zu
.
ssize_t b = -30;
printf("%zu\n", b); // problem.
ssize_t
не имеет указанного спецификатора печати C. C даже не указывает ssize_t
.
Различные расширения C указывают ssize_t
а в случае Linux - спецификатор печати. Руководство программиста Linux имеет:
z: следующее целочисленное преобразование соответствует аргументу
size_t
илиssize_t
",
printf("%zd\n", b); // OK for that Linux
POSIX B.2.12 Типы данных имеет
size_t
Предполагается, что это аналог соsize_t
. Формулировка такова, что реализация может либо выбрать использование более длинного типа, либо просто использовать подписанную версию типа, которая лежит в основеsize_t
.
Поскольку ssize_t
(необычно) может быть шире, чем size_t
, использование "%zd"
может вызвать неопределенное поведение (UB). Достаточно просто привести к самому широкому стандартному типу со intmax_t
начиная с C99, как intmax_t
и print.
printf("%jd\n", (intmax_t) b); // OK for general use
Переполнение coz size_t является НЕОГРАНИЧЕННЫМ когда вы пытаетесь установить size_t как (-val) вы получаете переполнение и получаете SIZE_T_MAX - val
например: size_t val = -20; //val == 18446744073709551615 - 20;