c++ – How do you know in C / C ++ if a type is signed or unsigned?

Question:

When faced with data types like time_t , size_t and others, obviously "numeric" (assuming that a pointer is also a number) types, it sometimes becomes just interesting, is it a signed or unsigned type?

How, without rummaging through all include files, where their definitions are located, often hidden in the jungle of conditional macro substitutions, to find out?

At least for x86_64 GNU / Linux and the gcc / g ++ compiler, you can use a simple trick of subtracting one from zero and checking if the result of the subtraction is less than zero. If less, then we have a signed type, otherwise it is unsigned .

This can be done with a couple of macros:

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

determines the semblance of a type by the type name, and the macro

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

by a variable of this type.

And if everything is fine with the first macro, then the second only works when compiled with the -std=... flag, which defines GNU C / C ++ extensions (including without the -std=... flag, that is, "by default "for gcc / g ++), but for -std=c89 , -std=c11 , etc. does not compile because typeof() is a GNU extension.

Such a modification of the macro to determine the "sign" of the variable

#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

Of course, it is terrible, firstly, the macro changes the value of the tested variable, and secondly (and this is the main thing), it incorrectly determines the "sign" of the pointer (correctly – unsigned ) when compiled in __STRICT_ANSI__ with optimization higher than -O1 .

A small program to illustrate.

// 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;
}

What other ways can you answer this question and does the described method work on systems other than x86_64 GNU / Linux gcc / g ++?

PS
you should not consider this task as meaningful in practical programming, important for using API with such types, no, it can only be treated with the tz. satisfying natural curiosity.

Answer:

C ++ has std::is_signed<T> , std::is_unsigned<T> .

In C, you can write a macro that will convert literal 0 and expression -1 to the desired type:

#define IS_SIGNED_TYPE(type) ((type)0 > (type)-1)

For variables, one could use var^var to get the 0 of the desired type. But this only works for integers. We also need to get (type)-1 which is impossible if we only use arithmetic operations, for example, the type var^var - 1 is an int .
Therefore, you need to use such a compiler extension, for example __typeof__ .

#define IS_SIGNED_VAR(var) (IS_SIGNED_TYPE(__typeof__(var))) 
Scroll to Top