0

Hola gente soy nuevo y espero que estén bien. Estoy haciendo un programa en C con la intención de ingresar una cadena de caracteres y que me imprima la cantidad de caracteres que ingrese y es básicamente esto:

int main(void)
{
    int i;
    char c;
    char* ptr;
    ptr=&c;

    printf("Ingrese una secuencia de caracteres: ");
    scanf("%s",ptr);

    for(i=0;*(ptr+i)!='\0';i++)
    {

    }

    printf("\nEl conteo es de %d\n",i);

    return 0;
}

Ósea, estoy experimentando con punteros. Se que se debería declarar char c[50] por ejemplo pero quería ver que pasaba. No sé que tan mal este el programa ni cuál sea el error, pero cuando arranco el programa me sale 0, 2, 6 o segmentatation fault alguien me ayuda a entender? Muchas gracias.

Christian
  • 9,428
  • 6
  • 13
  • 34
  • en primer lugar `scanf` leerá la entrada hasta que haya un espacio. Luego hay que recordar que en C no existen los string, sino los arrays de caracteres. – Christian Jul 31 '21 at 16:02
  • Hola @Christian, espero que estés bien. Ajá. Entonces no vasta con tener una dirección de memoria, sino que para lo que quiero hacer tengo que si o si declarar un arreglo? – El_Gordo_Freeman Jul 31 '21 at 16:22
  • `char c` es un único caracter, por ejemplo `a`, si tu tienes `char cadena[50]` esto ya es un array donde vas a poder almacenar hasta 49 caracteres. – Christian Jul 31 '21 at 16:28
  • también lo probé con `char c[]={'c'};`, hace lo que quiero me pero al final me sale "stack samshing detected", que sucedió ahí? me arranco el programa igual y el compilador no detecto error aparente. – El_Gordo_Freeman Jul 31 '21 at 16:48
  • no haz declarado e tamaño del array, sería `char c[1] = {'c'}`, también pudes hacer `char c[] = "c"` – Christian Jul 31 '21 at 16:52

3 Answers3

2

Se que se debería declarar char c[50] por ejemplo pero quería ver que pasaba.

No sé que tan mal este el programa ni cuál sea el error

No sientes que ambas oraciones se contradicen?

Porque hace eso?

Simulemos tu programa, y vamos a ver el stack y lo que pasa instruccion a instruccion. En mi caso siempre me decia que el largo era 2, asi que eso es lo que vamos a reproducir aqui:

De tus variables:

    int i;
    char c;
    char* ptr;

Tendremos un stack que se vera algo como1:

Direccion | Valores
0x1       | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | // char* ptr;
0x9       | 0x0 | // char c;
0xA       | 0x0 | 0x0 | 0x0 | 0x0 | // int i;

Luego va esta linea:

    ptr=&c;

Asi que le damos a ptr la direccion de c.

Direccion | Valores
0x1       | 0x9 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | // char* ptr;
0x9       | 0x0 | // char c;
0x10      | 0x0 | 0x0 | 0x0 | 0x0 | // int i;

Y luego lees:

    scanf("%s",ptr);

Supongamos que el usuario ingresa 'Hola'. Como crees que se vera el stack? Intenta hacerlo tu antes de continuar.

Lee primero la H:

Direccion | Valores
0x1       | 0x9 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | // char* ptr;
0x9       | 0x48 (H) | // char c;
0x10      | 0x0 | 0x0 | 0x0 | 0x0 | // int i;

Y si te das cuenta ya te terminaste la memoria que asignaste para la palabra, pero scanf no sabe eso, asi que no se detendra, seguira escribiendo en las siguientes direcciones de memoria. Tu stack puede parar algo similar a esto:

Direccion | Valores
0x1       | 0x9 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | // char* ptr;
0x9       | 0x48 (H) | // char c;
0xA       | 0x6f (o) | 0x6c (l) | 0x61 (a) | 0x0 (\0) | // int i;

Ya aqui ya estamos muy mal, has sobreescrito i.

Ahora vamos a tu for, primero que nada, a i le damos el valor de 0.

Direccion | Valores
0x1       | 0x9 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | // char* ptr;
0x9       | 0x48 (H) | // char c;
0xA       | 0x0 | 0x0 | 0x0 | 0x0 | // int i;

Lees en (ptr+i) (en la direccion 0x9), que seria la H, y como no es igual a \0 incrementamos i en 1.

Direccion | Valores
0x1       | 0x9 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | // char* ptr;
0x9       | 0x48 (H) | // char c;
0xA       | 0x1 | 0x0 | 0x0 | 0x0 | // int i;

Ahora cuando lees en (ptr+i) (en la direccion 0xA), y como no es igual a \0 incrementamos i en 1. Ahora el stack es:

Direccion | Valores
0x1       | 0x9 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | 0x0 | // char* ptr;
0x9       | 0x48 (H) | // char c;
0xA       | 0x2 | 0x0 | 0x0 | 0x0 | // int i;

Finalmente lee en 0xB, y como esa direccion tiene el valor de 0x0 (el \0), ya termina el loop, dandole a i el valor final de 2.

Comportamiendo indefinido

El leer o escribir en memoria que tu no has asignado es comportamiendo indefinido, eso significa que no se puede asegurar que es lo que sucedera en cada caso.

El error en este caso es tratar de escribir multiples caracteres en un buffer que solo puede recibir uno, tienes un buffer overflow.


1: Dejare cada variable en una linea por claridad. Nota que lo mas probable no es que esten inicializadas a 0, sino que tengan valores basura.

Pablochaches
  • 2,505
  • 1
  • 5
  • 21
  • Gracias amigo, me ayudo mucho tu explicación a entender que pasaba en la memoria, justo eso quería saber. Sí, tiene razón, se contradicen, me expresé mal. Si la dirección de memoria al que apunta `ptr` esta muy "lejos" de donde se almacena `i`, entonces el programa seguiría sobrescribiéndose y dando un valor que en teoría es el deseado pero en la practica esta mal verdad? Lo digo porque puse `char c[]={'c'};` a ver que onda y no detecto error el compi. Sale un valor, aunque la `ñ' lo cuenta como 2 y al final sale "stack samshing detected". – El_Gordo_Freeman Aug 01 '21 at 04:40
  • @El_Gordo_Freeman Si, tecnicamente ***podria*** funcionar, pero al ser comportamiento indefinido no se puede asegurar nada. – Pablochaches Aug 01 '21 at 15:05
  • *ptr no deberia ocupar 4 bytes? Pregunto, no afirmo – Hernán Garcia Aug 10 '21 at 14:01
  • Prueba con `std::cout << sizeof(char*) << '\n`. @HernánGarcia. Al final del dia dependera de tu arquitectura, en una maquina x64 tiene que ser de 8. – Pablochaches Aug 11 '21 at 16:51
  • @Pablochaches. pues a mi me da 4 bytes. tengo windows x64. Soy muy novato en C (apenas pasé unas 5 horas sentado aprendiendo) pero sera debido a que el compilador MinGW que uso es 32 bits? Curioso, pero despues pones un int y lo muestras como de 4 bytes, no deberia ser tambien de 8 en x64? a mi me dan tanto int como char* 4 bytes con sizeof – Hernán Garcia Aug 11 '21 at 21:49
  • Si, como MinGW es para 32 el puntero es de 4 bytes. *"no debería ser también de 8 en x64?"* No. El puntero tiene que ser de 8 para poder guardar todas las posibles direcciones de memoria en x64, pero bastan con 4 para todas las direcciones de x86, esa distinción no tiene nada que ver con el tamaño de `int`. – Pablochaches Aug 12 '21 at 00:10
  • Entonces int es 4 bytes tanto rn x64 como en x86 cierto? El q si varia es el puntero – Hernán Garcia Aug 13 '21 at 15:05
  • Supongo que si. @HernánGarcia – Pablochaches Aug 13 '21 at 16:30
1

los punteros en C, guardan la dirección de memoria dónde se encuentran los datos. En este caso, tu puntero apunta a un solo carácter, sin embargo, scanf tiene el %s que le indica es una cadena de caracteres Aquí ya tienes una inconsistencia. Pero en tu for haces la condición ptr+i aquí ya te saliste del límite al que apunta ptr. Para saber la cantidad de caracteres utiliza strlen


int main(void)
{
    int i;
    char c[100];
    char* ptr;
    ptr=&c;

    printf("Ingrese una secuencia de caracteres: ");
    scanf("%s",ptr);

   

    printf("\nEl conteo es de %d\n",strlen(c));

    return 0;
}


Bueno ya sabes lo que pasa.

Juan Carlos Guibovich
  • 2,735
  • 1
  • 5
  • 14
0

No sé que tan mal este el programa ni cuál sea el error, pero cuando arranco el programa me sale 0, 2, 6 o segmentatation fault alguien me ayuda a entender?

Te pongo un ejemplo, ¿que hace este programa?

int main(void)
{
    char c;
    char* ptr;
    ptr = &c;

    printf("Ingrese un caracter: ");
    scanf("%c", ptr);

    return 0;
}

El programa simplemente espera que el usuario ingrese únicamente un caracter (por algo usamos el especificador %c), luego scanf se encargará de almacenar ese caracter en la dirección (es decir, de la variable c) que apunte ptr.

Compara el ejemplo anterior con tu código actual... te darás cuenta que en la variable c solo podemos almacenar un valor. No deberíamos decirle lo contrario a scanf.

Si tu haces esto:

printf("Ingrese un caracter: ");
scanf("%s", ptr);

Le estás indicando a scanf que lea un conjunto de caracteres, esto ocasiona un comportamiento indefinido (significa que el comportamiento del programa puede ser cualquiera... puede que funcione o puede que no funcione).

Por ejemplo, aquí hay dos posibles comportamientos:

  • scanf podría escribir en una dirección de memoria que esté usando el proceso actual (el programa ejecutándose), esto cambiaría por completo el comportamiento del programa.

  • scanf podría intentar escribir en una dirección de memoria que no fue asignada al proceso actual (a esto denominamos segmentatation fault), esto provoca que el sistema operativo mate al proceso actual, por lo que el programa abortaría.

Observación

Por favor, nunca uses la función scanf de esa forma... podría ocasionar un desbordamiento de búfer. Siempre, siempre debes de especificar el tamaño del búfer en el primer parámetro de scanf. Para mas información, leer este hilo.

MrDave1999
  • 7,491
  • 1
  • 7
  • 22