Optimización inesperada de strlen al alias de matriz 2-d

28

Aquí está mi código:

#include <string.h>
#include <stdio.h>

typedef char BUF[8];

typedef struct
{
    BUF b[23];
} S;

S s;

int main()
{
    int n;

    memcpy(&s, "1234567812345678", 17);

    n = strlen((char *)&s.b) / sizeof(BUF);
    printf("%d\n", n);

    n = strlen((char *)&s) / sizeof(BUF);
    printf("%d\n", n);
}

Usando gcc 8.3.0 u 8.2.1 con cualquier nivel de optimización, excepto -O0, esto sale 0 2cuando esperaba2 2 . El compilador decidió que strlenestá limitado b[0]y, por lo tanto, nunca puede igualar o superar el valor dividido por

¿Es esto un error en mi código o un error en el compilador?

Esto no se explica claramente en el estándar, pero pensé que la interpretación principal de la procedencia del puntero era que para cualquier objeto X, el código (char *)&Xdebería generar un puntero que pueda iterar sobre el todo X: este concepto debería mantenerse incluso si Xsucede que tiene sub-matrices como estructura interna.

(Pregunta adicional, ¿hay una bandera gcc para desactivar esta optimización específica?)

MM
fuente
44
Ref: My gcc 7.4.0 informa 2 2bajo varias opciones.
chux - Restablece a Monica el
2
@Ale las garantías estándar que están en la misma dirección (la estructura no puede tener relleno inicial)
MM
3
@ DavidRankin-ReinstateMonica "que da como resultado que los límites de char (*) [8] se limiten a b [0]. Pero eso es todo lo que entiendo" Creo que eso lo clava. desde s.bse limita a b[0]que se limita a 8 caracteres, y por lo tanto dos opciones: (1) fuera de límite de acceso en caso de que haya 8 caracteres que no son nulos, lo cual es UB, (2) hay un carácter nulo, en cuyo la len es menor que 8, por lo tanto, dividir por 8 da cero. Entonces
compilar el
3
Dado que & s == & s.b, no hay forma de que el resultado pueda diferir. Como mostró @ user2162550, no se llama a strlen () y el compilador adivina cuál podría ser su resultado, incluso en el caso de godbolt.org/z/dMcrdy donde el compilador no puede saberlo. Es un error del compilador .
Ale

Respuestas:

-1

Hay algunos problemas que puedo ver y pueden verse afectados por la forma en que el compilador decide diseñar la memoria.

    n = strlen((char *)&s.b) / sizeof(BUF);
    printf("%d\n", n);

En el código anterior s.bhay una matriz de 23 entradas de una matriz de 8 caracteres. Cuando hace referencia a solo s.bestá obteniendo la dirección de la primera entrada en la matriz de 23 bytes (y el primer byte en la matriz de 8 caracteres). Cuando el código dice &s.b, esto está pidiendo la dirección de la dirección de la matriz. Debajo de las cubiertas, es muy probable que el compilador genere algo de almacenamiento local, almacene la dirección de la matriz allí y proporcione la dirección del almacenamiento local astrlen .

Tienes 2 posibles soluciones. Son:

    n = strlen((char *)s.b) / sizeof(BUF);
    printf("%d\n", n);

o

    n = strlen((char *)&s.b[0]) / sizeof(BUF);
    printf("%d\n", n);

También traté de ejecutar su programa y demostrar el problema, pero tanto el sonido de claxon como la versión de gcc que tengo con las -Oopciones siguieron funcionando como esperaba. Por lo que vale, estoy ejecutando clang versión 9.0.0-2 y gcc versión 9.2.1 en x86_64-pc-linux-gnu).

JonBelanger
fuente
-2

Hay errores en el código.

 memcpy(&s, "1234567812345678", 17);

por ejemplo, es arriesgado, aunque s comience por b debería ser:

 memcpy(&s.b, "1234567812345678", 17);

El segundo strlen () también tiene errores

n = strlen((char *)&s) / sizeof(BUF);

por ejemplo, debería ser:

n = strlen((char *)&s.b) / sizeof(BUF);

La cadena sb, si se copia correctamente, debe tener 17 letras. No estoy seguro de cómo se almacenan las estructuras en la memoria, si están alineadas. ¿Has comprobado que sb realmente contiene los 17 caracteres copiados?

Entonces un strlen (sb) debería mostrar 17

El printf solo muestra números enteros, ya que% d es entero, y la variable n se declara como un número entero. sizeof (BUF), debe ser 8

Entonces un 17 dividido por 8 (17/8) debería imprimir 2 ya que n se declara como entero. Como memcpy se usó para copiar datos a sy no a sb, supongo que esto tiene que ver con las alineaciones de memoria; suponiendo que es una computadora de 64 bits, puede haber 8 caracteres en una dirección de memoria.

Por ejemplo, supongamos que alguien ha llamado un malloc (1), que el siguiente "espacio libre" no está alineado ...

La segunda llamada strlen muestra el número correcto, ya que la copia de cadena se realizó en la estructura s en lugar de en sb

user413990
fuente