Como posso saber se um C inteiro variável é assinado?
-
05-07-2019 - |
Pergunta
Como um exercício, eu gostaria de escrever uma macro que me diz se uma variável inteira é assinado. Isto é o que eu tenho até agora e eu obter os resultados que esperam se eu tentar isso em uma variável de char com gcc -fsigned-char ou -funsigned-char.
#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0)
Este é portátil? Existe uma maneira de fazer isso sem destruir o valor da variável?
Solução
#define ISVARSIGNED(V) ((V)<0 || (-V)<0 || (V-1)<0)
não muda o valor de V. O terceiro punhos de teste o caso em que V == 0.
No meu compilador (gcc / cygwin) Isso funciona para int
e long
, mas não para char
ou short
.
#define ISVARSIGNED(V) ((V)-1<0 || -(V)-1<0)
também faz o trabalho em dois ensaios.
Outras dicas
Se você estiver usando GCC você pode usar a palavra-chave typeof
para não substituir o valor:
#define ISVARSIGNED(V) ({ typeof (V) _V = -1; _V < 0 ? 1 : 0 })
Isso cria uma variável temporária, _V
, que tem o mesmo tipo que V
.
Quanto à portabilidade, eu não sei. Ele vai trabalhar na máquina elogio a dois de (tudo A.K.A. seu código nunca será executado em toda a probabilidade), e eu acredito que ele vai trabalhar em um de máquinas elogio e sinal-e-ordem de grandeza. Como uma nota lateral, se você usar typeof
, você pode querer -1
elenco para typeof (V)
para torná-lo mais seguro (ou seja, menos propensos a avisos de gatilho).
#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0))
sem destruir o valor da variável. Mas não funciona para 0 valores.
E:
#define ISVARSIGNED(V) (((V)-(V)-1) < 0)
Uma abordagem diferente para todos os "torná-lo negativo" respostas:
#define ISVARSIGNED(V) (~(V^V)<0)
Dessa forma, não há necessidade de ter casos especiais para diferentes valores de V, uma vez ? V ? Z, V ^ V = 0.
Esta solução simples não tem efeitos colaterais, incluindo o benefício de apenas referindo-se v uma vez (o que é importante em um macro). Nós usamos a extensão gcc "typeof" para obter o tipo de v, e em seguida, lançar -1 a este tipo:
#define IS_SIGNED_TYPE(v) ((typeof(v))-1 <= 0)
É <= em vez de apenas
Por que na terra você precisa que ele seja uma macro? Os modelos são ótimos para isso:
template <typename T>
bool is_signed(T) {
static_assert(std::numeric_limits<T>::is_specialized, "Specialize std::numeric_limits<T>");
return std::numeric_limits<T>::is_signed;
}
O que vai funcionar out-of-the-box para todos os tipos integrais fundamentais. Ele também irá falhar em tempo de compilação em ponteiros, que a versão usando apenas subtração e comparação provavelmente não.
Editar : Oops, a questão exige C. Ainda assim, os modelos são a maneira agradável: P
A característica distintiva de assinado math / não assinado é que quando você direita deslocar um número assinado, o bit mais significativo é copiado. Quando você muda um número sem sinal, os novos bits são 0.
#define HIGH_BIT(n) ((n) & (1 << sizeof(n) * CHAR_BITS - 1))
#define IS_SIGNED(n) (HIGH_BIT(n) ? HIGH_BIT(n >> 1) != 0 : HIGH_BIT(~n >> 1) != 0
Então, basicamente, esta macro utiliza uma expressão condicional para determinar se o bit alto de um número é definido. Se não for, os conjuntos de macro-lo por bit a bit negando o número. Nós não podemos fazer uma negação aritmética, porque -0 == 0. Em seguida, deslocamento para a direita por 1 bit e testar se extensão de sinal ocorreu.
Isso pressupõe 2 complemento da aritmética, mas que normalmente é uma suposição segura.