Túnel do Tempo: a função itoa()

Em uma discussão no fórum de Programação em C e C++ do Viva o Linux, seu proponente perguntava acerca da função itoa(), desejoso de compreender seu funcionamento. Julguei interessante transportá-la, com algumas melhorias, para este espaço, até porque aqui posso fazer algo que não posso fazer naquele fórum, que é dar um exemplo explícito da implementação com código fonte em C.

[ Hits: 16.218 ]

Por: Paulo em 14/06/2017 | Blog: http://unixntools.blogspot.com.br/


"my_itoa()", uma implementação segura de conversão de número em string



O algoritmo descrito anteriormente pode ser implementado em C, com algumas adaptações. Abaixo se mostrará uma implementação, que eu chamei de my_itoa().

Um das adaptações é justamente para acomodar o fato de que strings no C têm tamanhos finitos, que devem ser conhecidos a fim de evitar extrapolar seu tamanho. Essa característica não estava presente na implementação de itoa() do Turbo C.

Outra adaptação é o uso de estruturas de repetição da linguagem C, em lugar do desvio incondicional (goto) do passo 7 do algoritmo.

Ainda outra adaptação se deve ao fato de que os algarismos do numeral posicional são gerados da direita para a esquerda, ao passo que strings costumam ser formadas da direita para a esquerda. Uma possibilidade seria estimar primeiro a quantidade necessária de posições, compará-la com o tamanho da string de saída, e então voltar ao começo e realizar a conversão. Eu adotei outra estratégia: ir armazenando os dígitos numa string reversa, e depois copiar os dígitos já convertido de trás para diante na string de saída.

O conjunto de algarismos é formado pelos caracteres ASCII de '0' a '9' e pelas letras maiúsculas, de 'A' até 'Z', o que permite que a base do sistema de numeração possa ser escolhida entre 2 (menos do que isso não faz sentido, pois não permite o uso de notação posicional) e 36 (por causa da quantidade total de algarismos). Variando-se a quantidade de elementos do array estático symbols, os valores de bases possíveis pode mudar. Variando-se o conteúdo desses elementos, pode-se usar representações alternativas, com algarismos diferentes dos tradicionais.

#include <errno.h>
#include <limits.h>
#include <stddef.h>

char *my_itoa(int n, char *str, size_t str_size, unsigned short base){
	static const char symbols[36]={
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
		'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
	};
	unsigned abs_n;
	unsigned quot, rem;
	char rev_str[CHAR_BIT*sizeof(int)];
	int rev_str_len=0;

	if(base<2 || base>sizeof symbols){  // Base inválida?
		errno=EINVAL;
		return NULL;
	}

	if(str_size<2){  // String de saída pequena demais?
		errno=ERANGE;
		return NULL;
	}

	if(n<0){  // Trata número negativo (ver nota).
		abs_n=(unsigned)-n;
		*str++='-';
		str_size--;
	}
	else
		abs_n=n;

	do {
		quot=abs_n/base;
		rem=abs_n-quot*base;
		rev_str[rev_str_len++]=symbols[rem];  // Seleciona o algarismo correspondente ao resto.
		if(rev_str_len>str_size-1){  // String de saída pequena demais?
			errno=ERANGE;
			return NULL;
		}
		abs_n=quot;
	} while(quot>0);

	do
		*str++=rev_str[--rev_str_len];  // Copia dígitos da string reversa para a ordem natural na saída.
	while(rev_str_len>0);

	*str='\0';  // Coloca o byte nulo terminador da string de saída.

	return str;
}
[/code]

O modo geral de utilizar a função é o seguinte.

[code]
int number;
char str[10];
unsigned short base;
/* ... Define os valores de 'number' e 'base'... */
if(my_itoa(number, str, sizeof str, base)!=NULL){
  // Conversão bem-sucedida: str contém string com numeral em notação posicional na base 'base'.
}
else{
  // Erro de conversão.  'errno' contém o tipo do erro
}

NOTA: A implementação acima indica números negativos através de um sinal de menos ("-") como primeiro caráter da string de saída. Isso difere da implementação de itoa() do Turbo C, que utiliza o sinal de negativo apenas para conversões usando a base 10, e converte números nas demais bases do mesmo modo como seria a conversão da representação binária de um valor do tipo unsigned int. Para mim, isso até pode fazer sentido para bases que sejam potências de 2 (2, 4, 8, 16 e 32), mas não para as qualquer uma das demais. Por esse motivo, achei mais adequado trabalhar sempre com o sinal, até porque a função inversa strtol(), que é parte da biblioteca padrão do C, aceita números com sinal em qualquer base suportada. Se alguém quiser ver a representação nativa dos bits, independentemente de sinal, deve usar a versão da função que recebe um valor unsigned int.

Página anterior     Próxima página

Páginas do artigo
   1. Apresentação do problema
   2. A implementação da itoa() do antigo Turbo C (e seus problemas)
   3. Alguns conceitos para a implementação da função
   4. Algoritmo para formação do numeral a partir do valor do número
   5. "my_itoa()", uma implementação segura de conversão de número em string
   6. Variações de "my_itoa()"
   7. Conclusão
Outros artigos deste autor
Nenhum artigo encontrado.
Leitura recomendada

Utilizando a biblioteca NCURSES - Parte II

Ponteiros void na linguagem C

Criando programas com suporte a arquivos de configuração com a libConfuse

LivreNFE - O emissor Nfe open source para Linux

Detectando assalto na multidão com visão computacional

  
Comentários
[1] Comentário enviado por uNclear em 19/06/2017 - 01:55h

ótimo artigo, quando tiver tempo vou fazer alguns testes

[2] Comentário enviado por uilianries em 19/06/2017 - 11:23h

Muito bem detalhado. Parabéns pelo conteúdo, Paulo.

[3] Comentário enviado por Nick-us em 01/03/2019 - 20:08h

Valeu a pena ler e guardar! informação nunca é demais!


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts