Почему ssize_t в Visual Studio 2010 определяется как unsigned?

У меня есть переносная программа, которая использует ssize_t в предположении, что это целое число со знаком. Концептуально он делает что-то вроде:

#include <stdint.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    size_t size_10 = 10;
    size_t size_20 = 20;
    ssize_t len_diff;
    len_diff = (ssize_t)size_10 - (ssize_t)size_20;
    if (len_diff < 0)
        printf("negative\n");
    else if (len_diff > 0)
        printf("positive\n");
    else
        printf("zero\n");
}

Можно ожидать, что программа напечатает "негатив", но вместо этого напечатает "положительный". Причина легко понять из определения ssize_t (в sourceannotations.h):

#ifndef _SSIZE_T_DEFINED
#ifdef  _WIN64
typedef unsigned __int64    ssize_t;
#else
typedef _W64 unsigned int   ssize_t;
#endif
#define _SSIZE_T_DEFINED
#endif

Итак, вычитание двух значений без знака приводит к значению без знака и, следовательно, к результату.

В старых версиях Windows SDK (например, V7.0A) ssize_t был правильно определен как:

//
// SIZE_T used for counts or ranges which need to span the range of
// of a pointer.  SSIZE_T is the signed variation.
//

typedef ULONG_PTR SIZE_T, *PSIZE_T;
typedef LONG_PTR SSIZE_T, *PSSIZE_T;

Может ли кто-нибудь объяснить это изменение? Должны ли мы прекратить использование ssize_t в Windows?

Обновление: Основываясь на всех ответах, это, по-видимому, ошибка в Visual Studio 2010, которая включает ssize_t, но неверно определенную. Это непроницаемая и неприятная ошибка.

Последнее обновление: Эта ошибка была исправлена ​​в VS2012 и VS2016. Также из обсуждения комментариев кажется, что этот способ вычисления len_diff проблематичен, когда сравниваемые значения имеют разные знаки при передаче на SSIZE_T

Ответ 1

ssize_t не стандарт C, это typedef от Posix. То, что вы нашли его в заголовке анализа кода для VS2010, вероятно, имеет какое-то отношение к происхождению, большинство инструментов анализа кода началось в Unix. Он снова удаляется в VS2012 и вверх.

То, что он присутствует в файле SDK BaseTsd.h во всех шапках, конечно, не является ошибкой, Windows поддерживает подсистему Posix. Эти typedefs изолируют операционную систему от деталей реализации компилятора, основную причину, по которой Windows удалось пережить изменения архитектуры, переместившись с 16 на 32 до 64 бит.

Таким образом, реальная проблема заключается в том, что вы пытаетесь скомпилировать программу Posix в Windows, но без использования заголовков Posix. Trivial для решения, просто добавьте свой собственный typedef перед #includes.

Ответ 2

Будет ли это хорошим решением?

#if defined(_MSC_VER)
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif

Ответ 3

Хотя стандарт POSIX определенно не соответствует тому, что ssize_t является целым числом без знака, код OP рискует также взломать системы, соответствующие стандарту POSIX.

POSIX определяет ssize_t, чтобы (по крайней мере) охватывать только -1 и ничего более негативного:

ssize_t

Используется для подсчета байтов или указания ошибки.

[...]

Тип ssize_t должен быть способен хранить значения как минимум в диапазоне [-1, {SSIZE_MAX}].