9. Verificação do sinal do char
Em algumas circunstâncias, o tipo char pode ser definido como signed ou unsigned. Uma diferença simples que pode acarreta em grandes mudanças na forma como funções com retorno char podem ser usadas.
Dessa forma, é necessário descobrir como detectar essa diferença, e como um bônus, também descobriremos outros tipos que poderão ser analisados com o método a ser apresentado
Testaremos de duas maneiras: a primeira especifica para o tipo char e a segunda sendo mais geral
9.1. Maneira Especifica
9.1.1. Macros utilizadas
# configure.ac
AC_INIT([epp], [7.38])
AC_PROG_CC
m4_include([m4/check.m4])
check_char_sign
AC_OUTPUT
# m4/check.m4
AC_DEFUN(check_char_sign, [
AC_MSG_CHECKING([if char is signed])
AC_RUN_IFELSE([AC_LANG_PROGRAM([],
[[
#include <stdio.h>
int main(int argc, char *argv[]) {
char bbb = 0;
--bbb;
if (bbb == -1) {
return 1;
} else {
return 0;
}
}
]]
)],
[AC_MSG_RESULT(signed)],
[AC_MSG_RESULT(unsigned)])
])
O primeiro exemplo dado se utiliza de um evento simples que acontece ao tentar diminuir 1 de uma variável unsigned com o valor 0: integer overflow.
O programa acima define uma macro que compila e executa um programa simples que tenta subtrair 1 da variável char definida inicialmente como 0. Após a subtração, caso a variável seja -1, o programa retorna 1 e o macro AC_RUN_IFELSE executa o primeiro ramo da condicional, caso contrário, a variável será 255 e o programa retornará 0, executando o segundo ramo da condicional
Essa maneira de verificar é aceitável, pois obtém o resultado esperado, porém há compiladores e flags que podem se recusar a executar um programa com um overflow ou underflow, por isso é necessário pensar em uma maneira diferente de resolver esse problema
Aqui está a saída desse programa:
$ autoconf
$ ./configure
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether the compiler supports GNU C... yes
checking whether gcc accepts -g... yes
checking for gcc option to enable C23 features... none needed
checking if char is signed... unsigned
configure: creating ./config.status
Como esperado, a saída obtida foi unsigned, correto para o
sistema x86 sendo utilizado para esta documentação.
9.2. Maneira Geral
9.2.1. Macro utilizada
# configure.ac
AC_INIT([epp], [20.08])
AC_PROG_CC
m4_include([m4/check.m4])
check_char_sign
AC_OUTPUT
# m4/check.m4
AC_DEFUN(check_char_sign, [
AC_MSG_CHECKING([if char is signed])
AC_RUN_IFELSE([AC_LANG_PROGRAM([],
[[
#include <limits.h>
int main(int argc, char *argv[]) {
if(CHAR_MIN < 0) {
return 1;
} else {
return 0;
}
]]
)],
[AC_MSG_RESULT(signed)],
[AC_MSG_RESULT(unsigned)])
])
O programa acima segue uma filosofia diferente, não há nenhum truque de aritmética com o tipo char, apenas uma condicional simples com um header padrão do C.
CHAR_MIN é definido dentro do header limits.h, junto de
muitos mínimos, máximos e alguns outros limites do sistema.
Caso CHAR_MIN seja menor que 0, é simples concluir que
char é signed, então retornamos 1 e a compilação
condicional do AC_RUN_IFELSE executa o primeiro ramo, caso
contrário concluímos que char é unsigned e retornamos 0 do
programa, executando o segundo ramo do macro.
Essa forma de testar evita alguns problemas com compiladores e flags que possam impedir o programa anterior de executar e acabar retornando o resultado errado.
Outros limites podem ser testados, como int e float, de
uma forma similar.
Por fim, aqui está a saída desse programa:
$ autoconf
$ ./configure
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether the compiler supports GNU C... yes
checking whether gcc accepts -g... yes
checking for gcc option to enable C23 features... none needed
checking if char is signed... unsigned
configure: creating ./config.status
Como esperado e de acordo com o resultado anterior.