C size_t и ssize_t отрицательное значение

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??

Ответ 1

В первом случае вы назначаете неподписанный тип - a. Во втором случае вы используете неверный спецификатор формата. Второй спецификатор должен быть %zd вместо %zu.

Ответ 2

Прежде всего, вы должны проверить фактический размер двух типов. Что-то вроде следующего фрагмента должно сделать:

#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).

Ответ 3

... почему я получил неправильный результат также с 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

Ответ 4

Переполнение coz size_t является НЕОГРАНИЧЕННЫМ когда вы пытаетесь установить size_t как (-val) вы получаете переполнение и получаете SIZE_T_MAX - val

например: size_t val = -20; //val == 18446744073709551615 - 20;