03
Апр
2016

Как в C/C++ узнать, является ли тип знаковым или беззнаковым?

Сталкиваясь с типами данных, подобными time_t, size_t и другими, очевидно "численными" (считая, что указатель это тоже число) типами, иногда становится просто интересно, а это signed или unsigned тип?

Как, не роясь во всех include-файлах, где находятся их определения, часто спрятанные в дебрях условных макроподстановок, выяснить это?

По крайней мере для x86_64 GNU/Linux и компилятора gcc/g++ можно использовать простой прием, заключающийся в вычитании единицы из нуля и проверки, меньше ли нуля результат вычитания. Если меньше, то мы имеем signed тип, а иначе он unsigned.

Такой прием можно оформить парой макросов:

#define SIGNED_TYPE(typename) ({volatile typename v = 0; volatile typename v1 = v - 1; v > v1;})

определяет знаковость типа по имени типа, а макрос

#define SIGNED_VAR(var) ({volatile typeof(var) v = 0, v1 = v - 1; v > v1;})

по переменной данного типа.

И если с первым макро все хорошо, то второй работает только при компиляции с флагом -std=... , определяющим GNU расширения C/C++ (в т.ч. без флага -std=..., т.е. "по умолчанию" для gcc/g++), а вот для -std=c89, -std=c11 и т.д. не компилируется, поскольку typeof() это GNU расширение.

Такая модификация макроса для определения "знаковости" переменной

#ifndef __STRICT_ANSI__
// GNU extensions
#define SIGNED_VAR(var) ({volatile typeof(var) v = 0, v1 = v - 1; v > v1;})
#else 
#define SIGNED_VAR(var) ({var = 0; var > var - 1;})
#endif

конечно ужасна, во-первых, макрос меняет значение тестируемой переменной, а во-вторых (и это главное), неправильно определяет "знаковость" указателя (правильно -- unsigned) при компиляции в __STRICT_ANSI__ с оптимизацией выше -O1.

Маленькая программа для иллюстрации.

// compile gcc/g++
#include <stdio.h>
#include <stdlib.h>
#include <time.h>


int
main (int ac, char *av[])
{
  time_t t = 0, t1 = t - 1;
  size_t s = 0, s1 = s - 1;
  char *p = 0, *p1 = p - 1;
  int i = 0, i1 = i - 1,
    *pp = 0, *pp1 = pp - 1;
  double d = 0, d1 = d - 1;

  printf("time_t: %ssigned\n", t1 < t ? "" : "un");
  printf("size_t: %ssigned\n", s1 < s ? "" : "un");
  printf("int:    %ssigned\n", i1 < i ? "" : "un");
  printf("double: %ssigned\n", d1 < d ? "" : "un");
  printf("char *: %ssigned\n", p1 < p ? "" : "un");
  printf("  demo for pointers\n  %p > %p %s\n", 
         pp, pp1, pp > pp1 ? "true" : "false");
  puts("\n  check macro");

#define SIGNED_TYPE(typename) ({volatile typename v = 0; volatile typename v1 = v - 1; v > v1;})

  printf("SIGNED_TYPE(time_t): %ssigned\n", SIGNED_TYPE(time_t) ? "" : "un");
  printf("SIGNED_TYPE(size_t): %ssigned\n", SIGNED_TYPE(size_t) ? "" : "un");
  printf("SIGNED_TYPE(char *): %ssigned\n", SIGNED_TYPE(char *) ? "" : "un");
  printf("SIGNED_TYPE(int):    %ssigned\n", SIGNED_TYPE(int) ? "" : "un");
  printf("SIGNED_TYPE(double): %ssigned\n", SIGNED_TYPE(double) ? "" : "un");
  printf("SIGNED_TYPE(long long):     %ssigned\n", 
         SIGNED_TYPE(long long) ? "" : "un");
  printf("SIGNED_TYPE(unsigned char): %ssigned\n", 
         SIGNED_TYPE(unsigned char) ? "" : "un");

#ifndef __STRICT_ANSI__
// GNU extensions
#define SIGNED_VAR(var) ({volatile typeof(var) v = 0, v1 = v - 1; v > v1;})
#else 
// UGLY, because change var, WRONG for -std=c++... -std=c... with -Ox
#define SIGNED_VAR(var) ({var = 0; var > var - 1;})
#endif

  printf("SIGNED_VAR(time_t t): %ssigned\n", SIGNED_VAR(t) ? "" : "un");
  printf("SIGNED_VAR(size_t s): %ssigned\n", SIGNED_VAR(s) ? "" : "un");
  printf("SIGNED_VAR(int i):    %ssigned\n", SIGNED_VAR(i) ? "" : "un");
  printf("SIGNED_VAR(double d): %ssigned\n", SIGNED_VAR(d) ? "" : "un");
  printf("SIGNED_VAR(char *p):  %ssigned\n", SIGNED_VAR(p) ? "" : "un");

  return puts("End") == EOF;
}

Какими еще способами можно ответить на данный вопрос и работает ли описанный метод в системах, отличных от x86_64 GNU/Linux gcc/g++ ?

P.S.
не стоит рассматривать эту задачку, как к имеющую смысл при практическом программировании, важную для использования API с такими типами, нет, к ней можно относиться только с т.з. удовлетворения естественного любопытства.

Источник: https://ru.stackoverflow.com/questions/509298/%D0%9A%D0%B0%D0%BA-%D0%B2-c-c-%D1%83%D0%B7%D0%BD%D0%B0%D1%82%D1%8C-%D1%8F%D0%B2%D0%BB%D1%8F%D0%B5%D1%82%D1%81%D1%8F-%D0%BB%D0%B8-%D1%82%D0%B8%D0%BF-%D0%B7%D0%BD%D0%B0%D0%BA%D0%BE%D0%B2%D1%8B%D0%BC-%D0%B8%D0%BB%D0%B8-%D0%B1%D0%B5%D0%B7%D0%B7%D0%BD%D0%B0%D0%BA%D0%BE%D0%B2%D1%8B%D0%BC

Тебе может это понравится...

Добавить комментарий