Hay numerosos problemas con este código. Vamos a arreglar las variables y funciones mal llamada e investigar los problemas:
En primer lugar, CharToInt()
nombre debería cambiarse por el buen StringToInt()
ya que opera en una cadena de ni un solo carácter.
La función CharToInt()
[sic.] No es segura.No verifica si el usuario ingresa accidentalmente un puntero NULL.
No valida la entrada, o más correctamente, omite la entrada no válida. Si el usuario ingresa en un dígito que no es, el resultado contendrá un valor falso. es decir, si ingresa en N
, el código *(s+i) & 15
producirá 14!
A continuación, el anodino temp
en CharToInt()
[sic.] Debería llamarse digit
ya que es lo que realmente es.
Además, este truco return result/10;
es sólo eso - una mala hackear a trabajar en torno a una aplicación con errores.
Igualmente MAX
tiene un nombre incorrecto, ya que puede entrar en conflicto con el uso estándar. es decir, #define MAX(X,y) ((x)>(y))?(x):(y)
La detallada *(s+i)
no es tan fácil de leer como *s
. No hay necesidad de utilizar y saturar el código con otro índice temporal i
.
gets()
Esto es malo, ya que puede desbordar el búfer de cadena de entrada. Por ejemplo, si el tamaño del búfer es 2 e ingresa 16 caracteres, se desbordará str
.
scanf()
Esto es igual de malo, ya que puede desbordar el búfer de cadena de entrada.
usted menciona "cuando se utiliza la función scanf(), el resultado es totalmente erróneo, porque en primer carácter aparentemente tiene un valor de -52 ASCII."
Eso es debido a un uso incorrecto de scanf(). No pude duplicar este error.
fgets()
Esto es seguro porque se puede garantizar que nunca desbordan el búfer cadena de entrada por los que pasa en el tamaño del búfer (que incluye espacio para el NULL.)
getline()
Algunas personas han sugerido el C POSIX standardgetline()
como reemplazo. Lamentablemente, esta no es una solución portátil práctica ya que Microsoft no implementa una versión C; solo el estándar C++ string template function como este SO #27755191 pregunta respuestas. El C++ getline()
de Microsoft estaba disponible al menos desde Visual Studio 6, pero dado que el OP pregunta estrictamente sobre C y no sobre C++, no es una opción.
Misc.
Por último, esta implementación tiene errores ya que no detecta el desbordamiento de enteros. ¡Si el usuario ingresa un número demasiado grande, el número puede volverse negativo! es decir,
se convertirá en -18815698
?! Vamos a arreglar eso también.
Esto es trivial de arreglar para un unsigned int
.Si el número parcial anterior es menor que el número parcial actual, entonces nos hemos desbordado y devolvemos el número parcial anterior.
Para un signed int
esto es un poco más de trabajo. En el ensamblaje, pudimos inspeccionar el indicador de acarreo, pero en C no hay una forma incorporada estándar para detectar el desbordamiento con las operaciones matemáticas firmadas. Afortunadamente, ya que estamos multiplicando por una constante, * 10
, podemos detectar fácilmente este si utilizamos una ecuación equivalente:
n = x*10 = x*8 + x*2
Si x * 8 desbordamientos entonces lógicamente x * 10 también lo hará. Para un desbordamiento int de 32 bits ocurrirá cuando x * 8 = 0x100000000 por lo tanto, todo lo que tenemos que hacer es detectar cuando x> = 0x20000000. Como no queremos suponer cuántos bits es int
, solo tenemos que comprobar si se han configurado los 3 mejores msb (bits más significativos).
Además, se necesita una segunda prueba de desbordamiento. Si el msb está configurado (bit de signo) después de la concatenación de dígitos, entonces también sabemos el número desbordado.
Código
Aquí es una versión segura fijo junto con el código que se puede jugar con el desbordamiento de detectar en las versiones inseguras. También he incluido tanto un signed
y unsigned
versiones a través de #define SIGNED 1
#include <stdio.h>
#include <ctype.h> // isdigit()
// 1 fgets
// 2 gets
// 3 scanf
#define INPUT 1
#define SIGNED 1
// re-implementation of atoi()
// Test Case: 2147483647 -- valid 32-bit
// Test Case: 2147483648 -- overflow 32-bit
int StringToInt(const char * s)
{
int result = 0, prev, msb = (sizeof(int)*8)-1, overflow;
if(!s)
return result;
while(*s)
{
if(isdigit(*s)) // Alt.: if ((*s >= '0') && (*s <= '9'))
{
prev = result;
overflow = result >> (msb-2); // test if top 3 MSBs will overflow on x*8
result *= 10;
result += *s++ & 0xF;// OPTIMIZATION: *s - '0'
if((result < prev) || overflow) // check if would overflow
return prev;
}
else
break; // you decide SKIP or BREAK on invalid digits
}
return result;
}
// Test case: 4294967295 -- valid 32-bit
// Test case: 4294967296 -- overflow 32-bit
unsigned int StringToUnsignedInt(const char * s)
{
unsigned int result = 0, prev;
if(!s)
return result;
while(*s)
{
if(isdigit(*s)) // Alt.: if (*s >= '0' && *s <= '9')
{
prev = result;
result *= 10;
result += *s++ & 0xF; // OPTIMIZATION: += (*s - '0')
if(result < prev) // check if would overflow
return prev;
}
else
break; // you decide SKIP or BREAK on invalid digits
}
return result;
}
int main()
{
int detect_buffer_overrun = 0;
#define BUFFER_SIZE 2 // set to small size to easily test overflow
char str[ BUFFER_SIZE+1 ]; // C idiom is to reserve space for the NULL terminator
printf(" Enter some numbers (no spaces): ");
#if INPUT == 1
fgets(str, sizeof(str), stdin);
#elif INPUT == 2
gets(str); // can overflows
#elif INPUT == 3
scanf("%s", str); // can also overflow
#endif
#if SIGNED
printf(" Entered number is: %d\n", StringToInt(str));
#else
printf(" Entered number is: %u\n", StringToUnsignedInt(str));
#endif
if(detect_buffer_overrun)
printf("Input buffer overflow!\n");
return 0;
}
Es posible WAN para reemplazar su función 'CharToInt()' con una llamada a 'atoi()' (que hacen la misma cosa). Además, el tipo de datos 'char' está implícitamente' signed', lo que puede explicar el "-52 valor ASCII" que estaba viendo. http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/ – sigint
Sí, podría usar atoi(), pero el objetivo de este programa era utilizar operadores bitwise. Además, muchas gracias por recordarme el valor firmado de char. El uso de caracteres sin signo resolvió el problema, aunque todavía no estoy seguro de cómo y por qué. – Marko
@sigint: en C, char se puede firmar char o unsigned char a discreción del compilador. – ninjalj