Implementación de printf no termina con null las cadenas

Iniciado por huchoko, 6 Abril 2019, 15:24 PM

0 Miembros y 1 Visitante están viendo este tema.

huchoko

Buenas, he hecho una implementación de printf, sin usar vsnprintf (por que no puedo).
Funciona bien, pero si trato de formatear un número hexadecimal y aveces cadenas, se descontrola todo, por lo que pienso que se me está colando un terminador nulo '\0' .
Ejemplo:
kputs("r8: %x    r9: %x    r10: %x", r->r8, r->r9, r->r10);
Salida:
r8: b8000└
Sólo me imprime r8  y de ahí el valor en hexadecimal, pero los demás los ignora y no imprime r9 ni r0.
Nótese el carácter "└" el cual no es esperado.

int kputs(const char *fmt, ...) {
/* Hacer nada si la cadena esta vacia. */
if (!fmt)
return 0;
char buffer[1024] = {0};
va_list args;

size_t cout;
unsigned int* charc;
va_start(args, fmt);

for (cout = 0; cout < strlen(fmt); ++cout) {
switch (fmt[cout]) {
case '%':
switch (fmt[cout + 1]) {
case 'c':
charc = va_arg(args, unsigned int);
putch(charc);
++cout;
break;
case 's':
charc = (int*) (va_arg(args, unsigned int));
strcpy(buffer, (const char*) charc);
print_string(buffer);
++cout;
break;
case 'i':
charc = va_arg(args, int);
itoa(charc, buffer, 10);
print_string(buffer);
++cout;
break;
case 'X':
case 'x':
charc = va_arg(args, int);
itoa((unsigned)charc, buffer, 16);
print_string(buffer);
++cout;
case '%':
putch("%");
++cout;

default:
va_end(args);
return 1;
}
break;
default:
putch(fmt[cout]);
break;
}
}
va_end(args);
return cout;
}

Las funciones itoa no creo que sean el problema, por que mi implementación no tiene problemas para imprimir enteros. (excepto que se desborda con un número muy grande, eso se arregla fácil)

CalgaryCorpus

Tienes que variar el tipo del segundo parametro de va_arg.

Tu solo usas unsigned int (o int) y deberias usar el tipo que calce con el % que se pasa.

Mira el ejemplo al final de aqui: https://linux.die.net/man/3/va_arg
Aqui mi perfil en LinkedIn, invitame un cafe aqui

huchoko

Cita de: CalgaryCorpus en  7 Abril 2019, 01:13 AM
Tienes que variar el tipo del segundo parametro de va_arg.

Tu solo usas unsigned int (o int) y deberias usar el tipo que calce con el % que se pasa.

Mira el ejemplo al final de aqui: https://linux.die.net/man/3/va_arg

Gracias, pero aún así no me funciona, mismo problema.  :-(

CalgaryCorpus

Aqui mi perfil en LinkedIn, invitame un cafe aqui

huchoko

Mismo problema, hacer un cast de int a char cuando trato de imprimir hexadecimal no imprime nada, y el mismo problema...

int kputs(const char *fmt, ...) {
/* Empty string sanity test */
if (!fmt)
return 0;
char buffer[1024] = {0};
va_list args;

size_t cout;
unsigned int* charc;
va_start(args, fmt);

for (cout = 0; cout < strlen(fmt); ++cout) {
switch (fmt[cout]) {
case '%':
switch (fmt[cout + 1]) {
case 'c':
charc = (char)va_arg(args, int);
putch(charc);
++cout;
break;
case 's':
charc = va_arg(args, char *);
strcpy(buffer, (const char*) charc);
print_string(buffer);
++cout;
break;
case 'i':
charc = va_arg(args, int);
itoa(charc, buffer, 10);
print_string(buffer);
++cout;
break;
case 'X':
case 'x':
charc = (char)va_arg(args, int);
itoa(charc, buffer, 16);
print_string(buffer);
++cout;
case '%':
putch("%");
++cout;

default:
va_end(args);
return 1;
}
break;
default:
putch(fmt[cout]);
break;
}
}
va_end(args);
return cout;
}

CalgaryCorpus

sugiero que recibas lo que va_arg te retorna en una variable del tipo que pides.

Ejemplo en linea 28 y siguientes:

cambia por

  int miVariableInt;  // al inicio de la funcion

// y luego en el switch, para el caso .'i'
  miVariableInt = va_arg(args, int);
  itoa(miVariableInt, buffer, 10);
  print_string(buffer);


Y si esto se comporta bien, hacer los mismos con los otros tipos.
Aqui mi perfil en LinkedIn, invitame un cafe aqui

huchoko

#6
Ahora si funciona para los enteros, pero aún está el problema con los hexadecimales.
Osea, si le paso el hexadecimal 0xF2, imprime 0xF2, pero no tiene el terminador nulo, lo cual hace que se imprima un caracter aleatorio después de imprimir el hexadecimal. (Sólo pasa cuando se trata de imprimir hexadecimal, los demás están ok)

RayR

Ese error se debe a que te falta un break para el case 'x' (y ya que estamos, para el case '%').

Hay más problemas, pero el principal es lo que ya te dijeron, que debes usar variables de distintos tipos según el % que recibas. Tal como lo tienes es una suerte que funcione.

Dado que char c es un puntero, cuando se ejecuta esto:

charc = va_arg(args, int);


Lo que en realidad estás haciendo es que charc apunte a la dirección de memoria cuyo número le enviaste a kputs. Por ejemplo, si le envias 22, charc estará apuntando a la dirección 22. Cuando llamas a itoa, en realidad le estás pasando una dirección de memoria, pero como esta función espera un simple entero, lo interpreta como tal, y por eso no falla. Pero si en algún punto se intentara desreferenciar a charc, tu programa fallaría estrepitosamente  ;D, seguramente generando una violación de acceso. charc debería ser un simple int, o unsigned, y para el case 's' debes usar un char *.

huchoko

Gracias, funciona perfectamente! Gracias a ambos!
Cita de: RayR en  8 Abril 2019, 01:20 AM
Ese error se debe a que te falta un break para el case 'x' (y ya que estamos, para el case '%').
Ah, ¡esos condenados break! siempre me pasa, y por más que reviso el código siempre se me cola uno  ;-)
Saludos  :)