Soy nuevo en programación y comencé a aprender hace unas semanas. Leí en un libro que la matriz de caracteres debería terminar con \0, pero cuando creo una matriz sin \0, funciona correctamente. Como es posible.?

#include<stdio.h>
#include<string.h>
int main()
{
    char a[] = {'a','p','p','l','e'};

    printf("%d\n",strlen(a));
    printf("%s\n",a);
    return 0;
}

La salida del código anterior es

5
apple

También leí que char es un subconjunto de tipos de datos enteros, pero cuando creo la matriz anterior con int datatype, no funciona correctamente.

#include<stdio.h>
#include<string.h>
int main()
{
    int a[] = {'a','p','p','l','e'};

    printf("%d\n",strlen(a));
    printf("%s\n",a);
    return 0;
}

La salida del código anterior es

1
a

¿Por qué considera solo el primer elemento de la matriz?

-1
Vencat 8 sep. 2018 a las 14:56

6 respuestas

La mejor respuesta

La primera mitad de tu pregunta es equivalente a esto:

Soy nuevo en la vida y comencé a aprender sobre el tráfico por carretera hace unas semanas. Leí en un libro que deberías esperar la luz verde antes de entrar en la intersección, pero cuando entro en la intersección sin esperar, funciona correctamente. ¿Como es posible?

En otras palabras, solo tienes suerte. Dio la casualidad de que, a pesar de que construyó una matriz de caracteres sin un terminador \0 apropiado, resultó que había un byte 0 en la memoria justo después de e en apple, por lo que Trabajó de todos modos. Pero no está garantizado que funcione, como tampoco está garantizado que puedas seguir cruzando la calle contra la luz y, finalmente, no ser golpeado.

Pasando a su segunda pregunta, cuando lee que "char es un subconjunto de tipo de datos entero", eso no significa en absoluto que en cualquier lugar donde normalmente usaría un char, también puede usar {{ X2}}.

Aquí hay algunos personajes en la memoria. Cada uno de ellos tiene un byte de tamaño:

char c1 = 'p', c1 = 'e', c3 = 'a', c4 = 'r';

    +---+                   +---+
c1: | p |               c2: | e |
    +---+                   +---+

    +---+                   +---+
c3: | a |               c4: | r |
    +---+                   +---+

Aquí hay algunas entradas en la memoria. En una máquina moderna, cada uno de ellos tiene probablemente cuatro bytes de tamaño:

int i1 = 'p', i1 = 'e', i3 = 'a', i4 = 'r';

    +---+---+---+---+       +---+---+---+---+
i1: | p             |   i2: | e             |
    +---+---+---+---+       +---+---+---+---+

    +---+---+---+---+       +---+---+---+---+
i3: | a             |   i4: | r             |
    +---+---+---+---+       +---+---+---+---+

Aquí hay una matriz de char, terminada en nulo correctamente:

char ca[] = { 'p', 'e', 'a', 'r', '\0' };

    +---+---+---+---+---+
ca: | p | e | a | r |\0 |
    +---+---+---+---+---+

Cuando printf imprime esta cadena, o strlen calcula su longitud, comienzan al principio y se mueven a lo largo de la cadena un byte a la vez, hasta que encuentran el \0.

Pero aquí hay una matriz de int:

int ia[] = { 'p', 'e', 'a', 'r', '\0' };

    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
ia: | p             | e             | a             | r             | \0            |
    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

Pero lo he dibujado un poco mal, porque en realidad, los tres bytes adicionales en cada int no están llenos de espacios vacíos, están llenos de cero bytes. (Es como si quisiéramos representar el número 1 con ceros a la izquierda, es decir, como 0001.) Entonces, la imagen más precisa se ve así;

    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
ia: | p  \0  \0  \0 | e  \0  \0  \0 | a  \0  \0  \0 | r  \0  \0  \0 | \0  \0  \0  \0|
    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

Entonces, cuando printf o strlen comienzan desde el principio y procesan la matriz de un byte a la vez buscando la \0 final, encuentran uno inmediatamente, justo después de la primera letra.

Un punto importante a considerar aquí es que printf y strlen están definidos para operar en matrices de char . Y debido a la forma en que funciona C, no tenían forma de saber que había engañado y pasado una matriz de int en su lugar. Literalmente tomaron esa misma memoria y la trataron como si fuera una matriz de char, y obtuvieron un resultado muy diferente de lo que esperaban.

Debido a que es fácil cometer errores como este, los buenos compiladores te avisarán si lo haces. Para su código, mi compilador me dio estas advertencias:

warning: incompatible pointer types passing 'int [5]' to parameter of type 'const char *'
warning: format specifies type 'char *' but the argument has type 'int *'

Esos mensajes se refieren al tipo char *, que es puntero a - char, porque cuando pasa una matriz a una función, lo que realmente se pasa es un puntero al primer elemento de la matriz. (Pero ese es un tema para otro día. Pero tiene mucho que ver con lo que dije sobre printf y strlen "literalmente tomando esa misma memoria y tratándola como si" fuera un conjunto de caracteres , en cambio.)

2
Steve Summit 8 sep. 2018 a las 14:15

Pasar un int[] a strlen() está mal. strlen() espera caracteres. Incluso si proporciona algo más (y apaga o ignora todas las advertencias de alerta del compilador), strlen() interpreta la dirección dada como char* (lo que realmente contiene).

Para ser estrictos, esto es comportamiento indefinido.

Investigando un poco, podemos explorar lo que probablemente sucede:

char a[] = {'a','p','p','l','e'};

Define una matriz de 5 caracteres. Volcado de memoria esto podría verse así:

0x61 0x70 0x70 0x6c 0x65 ???? ???? ????

int a[] = {'a','p','p','l','e'};, suponiendo 32 bits int, little endian, esto podría verse así:

0x61 0x00 0x00 0x00 0x70 0x00 0x00 0x00
0x70 0x00 0x00 0x00 0x6c 0x00 0x00 0x00
0x65 0x00 0x00 0x00 ???? ???? ???? ????

Reinterpretando a[] como char* (lo que strlen() haría), esto da como resultado una cadena de longitud uno.

Sin embargo, sigue siendo un comportamiento indefinido ...

1
Scheff 8 sep. 2018 a las 12:08

Leí en un libro que la matriz de caracteres debería terminar con \0 ...

Solo es necesario cuando desea interpretar la matriz de caracteres como una cadena. En lenguaje C, las cadenas son en realidad una matriz unidimensional de caracteres terminados por un carácter nulo \0.

En su primer ejemplo, la matriz char a es simplemente una matriz de caracteres. Tienes suerte de que strlen y printf hayan dado el resultado esperado. La función strlen devuelve el número de caracteres que preceden al carácter nulo final. En su caso, la memoria justo después de la matriz a debe ser 0. Por lo tanto, está obteniendo el resultado esperado de strlen. Por la misma razón, printf también funciona como se esperaba porque escribe cada byte hasta y sin incluir el primer terminador nulo.

En su segundo ejemplo, está pasando un puntero entero a strlen:

printf("%d\n",strlen(a));

El compilador debe enviar un mensaje de advertencia porque el tipo de parámetro strlen es const char * y lo está pasando int *.

Además, en printf está dando argumento como puntero entero. El especificador de formato %s espera un puntero char. El comportamiento es indefinido en este caso.

0
H.S. 8 sep. 2018 a las 12:18

En el compilador de 32 bits int toma 4 bytes y char toma 1 byte. Si pasa la matriz de enteros a strlen, escanea el primer byte del entero que es a en su caso, luego 3 bytes son 0, por lo tanto, strlen se detiene en el segundo byte y muestra length como 1.

0
Mayur 8 sep. 2018 a las 12:08

Depende del hardware y la implementación, el int puede tener más de 2 bytes de longitud.

En el sistema little endian, el primer byte será el código ASCII de 'a' y el segundo byte (y el consecutivo hasta sizeof (int)) cero. Por lo tanto, las funciones de cualquier cadena lo considerarán como una cadena de un solo carácter.

El sistema big endian tendrá el orden de bytes opuesto y si interpretamos este int arrar como una matriz de caracteres, el primer carácter será cero, lo que termina la cadena y su longitud será cero.

Su segundo ejemplo es incorrecto ya que no tiene un cero final y usarlo como una picadura invoca el UB.

La inicialización de su tabla de caracteres debe ser:

char a[] = {'a','p','p','l','e', 0};

O

char a[] = "apple";

Como la inicialización literal de cadena también agrega la terminación nul.

1
P__J__ 8 sep. 2018 a las 16:19

En char a[] = {'a','p','p','l','e'};, el compilador cuenta el número de valores que proporciona, que es cinco. Luego crea una matriz de cinco char y los inicializa con esos valores.

Luego, en printf("%d\n",strlen(a)); y en printf("%s\n",a);, el estándar C no define el comportamiento porque se requiere que tenga un elemento cero en la matriz para indicar dónde está el final. En la situación en la que intentó esto, puede haber sucedido que la memoria después de la matriz a contenía un cero, lo que resultó en que el programa imprimió "5" y "manzana". Sin embargo, esto no siempre sucederá.

Además, el resultado de strlen tiene el tipo size_t y debe imprimirse con %zu en lugar de %d.

En int a[] = {'a','p','p','l','e'};, el compilador crea una matriz de int. Cuando usa esto en printf("%s\n",a);, está pasando un puntero a int cuando printf espera un puntero a char. El comportamiento de esto no está definido por el estándar C. Un resultado común es que printf procesará los bytes en la matriz de int como si fueran una matriz de char, aunque esto no se puede confiar: el comportamiento real de las implementaciones de C puede variar.

Como int son más anchos que char, un int que contiene el valor a generalmente contiene un byte con el valor a y uno o más bytes con el valor cero . También puede contener bits de relleno. El orden de los bytes dentro de un int no está definido por el estándar C. Si el byte que contiene a es el primero en la memoria y los siguientes bytes son cero, printf puede imprimir "a". Sin embargo, si un byte que contiene cero es el primero, printf lo verá como el final de la cadena y no imprimirá nada.

Nuevamente, el comportamiento no está definido por el estándar C. Lo anterior solo explica cómo se imprimió lo que vio, no lo que puede esperar en otras situaciones.

2
Eric Postpischil 8 sep. 2018 a las 12:10