Definición de declaración de cadena de C / C ++, longitud, tamaño de, explicación detallada de '\ 0'

Introducción

Recientemente, estoy trabajando en un pequeño proyecto, usando la microcomputadora de un solo chip stm32f103c8t6 para convertir el audio de la computadora en los efectos de iluminación en la barra de luces LED. En el proceso de depuración utilizando la función de impresión del puerto serie, encontré problemas relacionados con la longitud específica de la cadena y el final '\ 0'. Después de solicitar una gran cantidad de información y realizar pruebas lo más detalladas posible, resúmalas y regístrelas aquí.

Cuatro formas de declarar una cadena definida

Las cadenas siempre han sido un visitante frecuente de los programas C \ C ++. Hay cuatro declaraciones y definiciones de cadenas de uso común ( para evitar problemas, este artículo no distingue entre "declaración" y "definición" en detalle. Los siguientes usos " definición "para referirse colectivamente a todos los comportamientos relacionados. Si es necesario, consulte: La diferencia entre declaración y definición en lenguaje c ):

char *rick = "Wubba";
char morty[10] = "Whoa!";
char squanchy[] = "Squanch it.";
char snowball[] = {
    
    't', 'e', 's', 't', 'i', 's'};

Excepto por el segundo tipo, que necesita especificar la longitud reservada para la cadena, el formulario es bastante libre. Por supuesto, la libertad también significa ocultar los detalles. Para el lenguaje C de nivel relativamente bajo, esta es la razón por la que es fácil cometer errores.

Cómo juzgar la longitud de una cuerda

La definición de cadenas en el lenguaje c es fácil y simple, pero involucra muchos detalles, que son fáciles de confundir y fáciles de cometer errores. Aquí trata de discutir claramente cómo determinar la longitud de la cuerda.

Caso 1: rick string

char *rick = "Wubba";

Para el primer método de definición, la cadena termina con el carácter '\ 0' (el código ASCII es 0). Al definir rick, no agregamos específicamente '\ 0' al final, pero se agregará automáticamente al último dígito de la cadena rick (la cadena "Wubba" que señalamos explícitamente).

Si usamos % s para generar la cadena rick, obtendremos exactamente las cinco letras "Wubba" que esperamos.

int main() {
    
    
  char *rick = "Wubba";
  printf("%s", rick);
  
  getchar();
  return 0;
}

resultado de salida de rick string% s


Definimos la cadena rick con tanta libertad, pero el compilador aún puede interpretarla con precisión. Los detalles que se esconden detrás de esta poderosa habilidad son los culpables de infinidad de errores.

Como se mencionó anteriormente, el carácter '\ 0' se agrega automáticamente al final de la cadena rick que definimos. Al utilizar un bucle for, este fenómeno se puede observar claramente:

int main() {
    
    
  char *rick = "Wubba";

  for (int i = 0; i < 6; ++i)
    printf("%c", rick[i]);

  getchar();
  return 0;
}

para salida de bucle

Se puede encontrar que hay un espacio entre la palabra de salida y el cursor, que es el carácter '\ 0' que forzamos a generar ( esto no es realmente lo que escribí ).


También podemos probar más:

printf("%d", rick[5]); //int形式输出'\0'

Salida '\ 0' en forma int
El 0 en este círculo es la representación del código ASCII de '\ 0' .


Si dibuja un diagrama esquemático de la cadena rick (o "matriz de caracteres") en la memoria (o "dirección" ):
Cómo se ve la cadena Wubba en la dirección
el subíndice es 6 y los valores posteriores son desconocidos, y están determinados por el estado actual del la memoria de la computadora.


De hecho, también podemos agregar manualmente '\ 0' a nuestra cadena (no limitado al final). Cuando se trata como una cadena y se genera con% s, se cortará en '\ 0'.

  char *rick = "Wub\0ba";
  printf("%s", rick);

Salida de "Wub \ 0ba"


Si usa un bucle for para forzar la salida, puede ver que el resultado de salida de '\ 0' es un espacio. Los siguientes caracteres también se inicializan normalmente.

  char *rick = "Wub\0ba";
  for (int i = 0; i < 6; ++i)
    printf("%c", rick[i])

"Wub \ 0ba" para salida de bucle

Caso 2: Cuerda de Morty

El primer caso básicamente ha cubierto la lógica básica y los detalles de las cadenas en el lenguaje C. La explicación de otras situaciones se ha vuelto relativamente fácil.

Para el protagonista de esta parte, la matriz de caracteres de Morty, la mayor diferencia con otros métodos de definición es que este método declara explícitamente la longitud de la cadena, o el espacio de memoria reservado para la cadena.

  char morty[10] = "Whoa!";

Como ejemplo, la longitud declarada aquí es obviamente mayor que la longitud real de la cadena. Entonces, ¿cómo manejará nuestro lenguaje C esta situación?

Una simple prueba, todo se aclara gradualmente.

int main() {
    
    
  char morty[10] = "Whoa!";
  
  // morty数组存储内容(字符形式)
  printf("morty[]:");
  for (int i = 0; i < 15; ++i)
    printf("%4c", morty[i]);
  printf("\n");
  
  // morty数组存储内容(ASCII码形式)
  printf("ASCII:  ");
  for (int i = 0; i < 15; ++i)
    printf("%4d", morty[i]);
  printf("\n");

  // 对应下标
  printf("index:  ");
  for (int i = 0; i < 15; ++i)
    printf("%4d", i);

  getchar();
  return 0;
}

resultados de la prueba de morty array


Aquí usamos la función de formato de printf (como % 4d ) para escribir cada carácter de la matriz morty, el código ASCII correspondiente a cada carácter y el subíndice correspondiente del elemento en la matriz en una forma de alineación vertical, que es una suma El efecto es el mismo que el del dibujo ( pero es más que un poco más feo que la imagen anterior ).

La observación muestra que, además de la inicialización exitosa de la cadena "¡whoa!" Que enumeramos explícitamente, en lugares distintos del espacio de 10 caracteres reservado para la matriz de morty, el subíndice es 14, no está definido El valor de (check la tabla ASCII y encontrar que es un carácter '.'). Sin embargo, en el rango de posición del subíndice 0-9 que reservamos para Morty, los valores indefinidos (los cinco elementos del subíndice 5-9) son todos 0.

En este momento, podemos adivinar audazmente: los elementos reservados pero no definidos se inicializarán en 0 de forma predeterminada, es decir, '\ 0'. Usemos la práctica para revelar la verdad.

La práctica es el único criterio (para probar enlaces de prueba de verdad

Para explorar si la conjetura puede establecerse universalmente, un método intuitivo es intentar cambiar el estado de la memoria de la computadora para ver si hay contraejemplos (los elementos reservados pero no definidos aparecen como valores ASCII distintos de 0).
Basado en mi comprensión limitada de los principios de la informática, encontré las siguientes pruebas constructivas:

int main() {
    
    
  char squirrel[10] = "follow him"; // 干扰数组
  char morty[10] = "Whoa!";

  // morty数组存储内容(字符形式)
  printf("morty[]:");
  for (int i = 0; i < 15; ++i)
    printf("%4c", morty[i]);
  printf("\n");
  
  // morty数组存储内容(ASCII码形式)
  printf("ASCII:  ");
  for (int i = 0; i < 15; ++i)
    printf("%4d", morty[i]);
  printf("\n");

  // 对应下标
  printf("index:  ");
  for (int i = 0; i < 15; ++i)
    printf("%4d", i);

  getchar();
  return 0;
}

Los resultados son los siguientes:


salida de prueba de práctica de matriz de morty


¡OhLaLa! Parece que la inmersión en el mundo de la informática en los últimos años no solo ha provocado la expansión del conocimiento, sino que la intuición relacionada parece haberse cultivado. Se hicieron varios intentos más y los resultados fueron similares, todos de acuerdo con nuestra conjetura.


(El tamaño de la matriz de morty se cambia a 12, el contenido se cambia ligeramente y se define una cadena adicional después de la matriz de morty. El resultado es el siguiente)

int main() {
    
    
  char squirrel[10] = "follow him"; // 干扰数组
  char morty[12] = "Who123a!";
  char squirrel1[10] = "follow him"; // 干扰数组

  // morty数组存储内容(字符形式)
  printf("morty[]:");
  for (int i = 0; i < 15; ++i)
    printf("%4c", morty[i]);
  printf("\n");
  
  // morty数组存储内容(ASCII码形式)
  printf("ASCII:  ");
  for (int i = 0; i < 15; ++i)
    printf("%4d", morty[i]);
  printf("\n");

  // 对应下标
  printf("index:  ");
  for (int i = 0; i < 15; ++i)
    printf("%4d", i);

  getchar();
  return 0;
}

Más intentos en la práctica


Pero esto solo puede acercarnos a la verdad, pero aún somos incapaces de alcanzarla. Tengo que esperar a que encuentre un libro autorizado en lenguaje c ~~ o para pedirle al fundador del lenguaje c ~~ que lo sepa realmente. También espero que todos los lectores que tengan las condiciones puedan dar respuestas, está muy agradecido.

Situación 3: cuerda blandita

char squanchy[] = "Squanch it.";

(Una sección está escrita específicamente para el Caso 3 solo en aras de un formato atractivo).

De hecho, la situación de squanchy es exactamente la misma que la situación 1, porque el significado del nombre de matriz squanchy en lenguaje C es exactamente el puntero al primer elemento de la matriz en la memoria . Esto es exactamente lo mismo que la cuerda de Rick.


Después de diseñar la misma prueba que en el Caso 2, se obtienen los siguientes resultados

int main() {
    
    
  char squirrel[10] = "follow him"; // 干扰数组
  char squanchy[] = "Squanch it.";

  //squanchy数组存储内容(字符形式)
  printf("squanchy[]:");
  for (int i = 0; i < 15; ++i)
    printf("%4c", squanchy[i]);
  printf("\n");
  
  // squanchy数组存储内容(ASCII码形式)
  printf("ASCII:     ");
  for (int i = 0; i < 15; ++i)
    printf("%4d", squanchy[i]);
  printf("\n");

  // 对应下标
  printf("index:     ");
  for (int i = 0; i < 15; ++i)
    printf("%4d", i);

  getchar();
  return 0;
}

resultados de la prueba blandos

Como puede ver, no se especifica ningún espacio reservado y el compilador simplemente agrega un '\ 0' al final de la cadena para nosotros (en la posición donde el subíndice es 11). Pero esto suele ser suficiente para nosotros.

Situación 4: cuerda de bola de nieve

  char snowball[] = {
    
    't', 'e', 's', 't', 'i', 's'};
  printf("%s", snowball);

Esta forma de definición tiene grandes peligros ocultos. El compilador parece estar sesgado en contra de este enfoque. Si no agrega explícitamente '\ 0' al final de la cadena, nadie lo ayudará a hacer esto. Los posibles resultados de esto son:

Inserte la descripción de la imagen aquí
¿Qué pasa con los testículos? ¿Qué es este extra?


Aquí hay un hombre que obtuvo resultados más espectaculares:> Portal .

Uso de sizeof () en cadenas

Normalmente, puede obtener fácilmente la longitud de la matriz utilizando la función sizeof () . Y sizeof () es especialmente adecuado para cadenas de caracteres (matrices de caracteres) -char en lenguaje c generalmente ocupa 1 byte en la memoria , por lo que el resultado de sizeof (char) es 1. Por lo tanto, usar sizeof () en una cadena es una longitud de cadena intuitiva, mientras que otros tipos de matrices son un poco más engorrosas:

char rick[] = "Wubba\0";
int egg[] = {
    
    123,456,666};
printf("        Type size    Array size     Array length\n");
printf("rick:   %9d %11d %16d\n", sizeof(char), sizeof(rick), sizeof(rick)/sizeof(char));
printf("egg:    %9d %11d %16d",   sizeof(int),  sizeof(egg),  sizeof(egg)/sizeof(int));

tamaño del resultado de la prueba

Simplemente analiza

  1. Para el huevo de matriz int: use sizeof () para obtener 12. Esto es 4 veces la longitud real de la matriz de huevos, porque int ocupa 32 bits y 4 bytes aquí. Para obtener la longitud de la matriz, debe usar sizeof (egg) / sizeof (int). Los dos métodos de string rick obtienen el mismo resultado, y la razón es obvia.
  2. Para la matriz de caracteres rick: aunque agregamos manualmente un '\ 0' al final de la cadena, el compilador aún agregó un '\ 0' adicional para nosotros (puede imprimir la prueba a través del bucle for% d). Esto es fácil de entender, el compilador no se tomará el tiempo para identificar si el usuario ha agregado manualmente '\ 0', pero lo tratará de la misma manera.

¿Matriz de caracteres o puntero?

Durante el proceso de prueba, se obtuvieron nuevos avances inesperados.

Si se usa sizeof () para los cuatro métodos de definición de cadenas:

char *rick = "Wubba";
char rick1[] = "Wubba";
char rick2[] = {
    
    'W', 'u', 'b', 'b', 'a', '\0'};
char rick3[10] = "Wubba";

printf("%d ", sizeof(rick));
printf("%d ", sizeof(rick1));
printf("%d ", sizeof(rick2));
printf("%d ", sizeof(rick3));

Inserte la descripción de la imagen aquí
Los resultados obtenidos son sorprendentes, aunque los resultados de rick1, 2 y 3 son razonables ( tenga en cuenta que el '\ 0' agregado por el compilador también se cuenta ), ¡el resultado de la primera matriz rick es un poco ridículo número 4!

Confundido, traté de cambiar la longitud del contenido de la cadena, cambiar la longitud del nombre de la cadena, agregar la matriz de interferencia ... Pero no importa cuánto lo cambie, no pude quitarme el resultado.

Al buscar la información, se encuentra que este método de definición de cadena, a los ojos de sizeof (), es otro puntero de cosa similar pero diferente.

char *rick = "Wubba"; // 被我们理解为的字符数组
printf("rick: %d %d\n", sizeof(rick), rick);

char suffix = '?';
char *p = &suffix;   // 一个指针
printf("p:    %d %d", sizeof(p), p);

Inserte la descripción de la imagen aquí

Por lo tanto, es imposible usar sizeof () para obtener la longitud de la matriz normalmente si usa rick para definir una matriz usando un método de puntero obvio.


para resumir

Las cadenas son tan comunes que estamos familiarizadas con ellas, pero no las conocemos. La investigación nos acerca a la verdad y hace que los programas que creamos sean más hermosos.

A continuación se resumen los puntos clave del artículo:

  1. Excepto por los siguientes métodos, el compilador agregará automáticamente '\ 0' al final de la cadena.
	char snowball[] = {
    
    't', 'e', 's', 't', 'i', 's', '\0'}; // 千万别忘了加'\0'!
  1. Cuando use la salida% s de printf, la salida se detendrá antes del primer '\ 0'.
  2. El valor de la cadena fuera del rango definido puede ser exorbitante, dependiendo del estado actual de la memoria de la computadora. Hay “定义”dos situaciones mencionadas aquí :
    • Los 10 espacios reservados en morty [10] están todos definidos (el área reservada pero no inicializada se inicializará en '\ 0' por defecto)
    • En las otras tres formas, se refiere a la cadena escrita entre comillas cuando declaramos, más el '\ 0' agregado automáticamente por el compilador (la cadena como snowball [] no agregará automáticamente '\ 0')
  3. A diferencia de otros tipos de matrices, la longitud de las matrices de tipo de caracteres se puede obtener directamente mediante sizeof ().
  4. Si usa sizeof () en una cadena definida en forma de puntero (como * rick), lo que obtiene no es la longitud de la cadena, sino el tamaño del espacio de memoria ocupado por un puntero.

(Terminar)

Supongo que te gusta

Origin blog.csdn.net/weixin_39591031/article/details/109726381
Recomendado
Clasificación