Hack de estructura C en el trabajo

7 minutos de lectura

avatar de usuario
Ankur Agarval

¿Es así como se puede usar la memoria “extra” asignada al usar el truco de estructura C?

Preguntas:

Tengo una implementación de pirateo de estructura C a continuación. Mi pregunta es cómo puedo usar la memoria “extra” que he asignado con el truco. ¿Puede alguien darme un ejemplo sobre el uso de esa memoria adicional?

#include<stdio.h>
#include<stdlib.h>

int main()
{

    struct mystruct {

        int len;
        char chararray[1];

    };

    struct mystruct *ptr = malloc(sizeof(struct mystruct) + 10 - 1);
    ptr->len=10;


    ptr->chararray[0] = 'a';
    ptr->chararray[1] = 'b';
    ptr->chararray[2] = 'c';
    ptr->chararray[3] = 'd';
    ptr->chararray[4] = 'e';
    ptr->chararray[5] = 'f';
    ptr->chararray[6] = 'g';
    ptr->chararray[7] = 'h';
    ptr->chararray[8] = 'i';
    ptr->chararray[9] = 'j';


}

  • ¿Puedes darnos un poco más de contexto? ¿Qué es el truco de la estructura c? ¿Cuál es tu pregunta real?

    – Roberto Harvey

    14 mayo 2013 a las 22:00


  • @RobertHarvey Creo que extendiendo la matriz a 9 elementos desde 1 elemento.

    –Lews Therin

    14 mayo 2013 a las 22:01


  • @RobertHarvey Fingir un miembro de matriz flexible declarando una matriz de longitud 1 al final. Antes de que se agregaran miembros de matriz flexible en C99, eso era los forma de tener estructuras con matrices de longitud variable (y determinada por el tiempo de ejecución). (Comportamiento indefinido, sin embargo). Pero sospecho que la tuya fue una pregunta retórica.

    –Daniel Fischer

    14 mayo 2013 a las 22:03


  • @RobertHarvey da miedo cuando lo ves por primera vez, pero en la práctica funciona bastante bien.

    – Keith Nicolás

    14 mayo 2013 a las 22:09

  • @RobertHarvey Fue tan ampliamente utilizado que ningún compilador podía permitirse romperlo. Pero siempre fue un comportamiento explícitamente indefinido de acuerdo con el estándar. [well, obviously not before the first standard]. Ahora tenemos miembros de matriz flexibles estandarizados, por lo que el truco de la estructura debería seguir el dodo.

    –Daniel Fischer

    14 mayo 2013 a las 22:10


Sí, esa es (y era) la forma estándar en C para crear y procesar un tamaño variable struct.

Ese ejemplo es un poco detallado. La mayoría de los programadores lo manejarían más hábilmente:

struct mystruct {
        int len;
        char chararray[1];  // some compilers would allow [0] here
    };
    char *msg = "abcdefghi";
    int n = strlen (msg);

    struct mystruct *ptr = malloc(sizeof(struct mystruct) + n + 1);

    ptr->len = n;
    strcpy (ptr->chararray, msg);
}

  • Eso era. La forma estándar ahora es usar un miembro de matriz flexible.

    –Daniel Fischer

    14 mayo 2013 a las 22:05

  • @LewsTherin ver stackoverflow.com/questions/246977/…

    – Alok Singhal

    14 mayo 2013 a las 22:11

  • @LewsTherin struct foo { int length; other_type other_member; double flexible_array_member[]; };.

    –Daniel Fischer

    14 mayo 2013 a las 22:12

  • @wallyk ¿Y strcpy funciona aquí porque asignamos memoria adicional en malloc que chararray puede señalar de manera segura sin el peligro de exceder los límites de una matriz? Derecha ?

    –Ankur Agarwal

    14 mayo 2013 a las 22:12

  • @abc: Eso es correcto. Sin embargo, esto también suele ser una fuente potencial de errores cuando alguien medio consciente cambia parte de él sin reconocer la dependencia de la otra parte.

    – Wallyk

    14 mayo 2013 a las 22:14

avatar de usuario
miguel rebabas

Desde que leí este artículo (http://blogs.msdn.com/b/oldnewthing/archive/2004/08/26/220873.aspx), me ha gustado usar el struct hack así:

  #include<stdio.h>
  #include<stdlib.h>

  int main()
  {

      struct mystruct {

          int len;
          char chararray[1];
      };

      int number_of_elements = 10;

      struct mystruct *ptr = malloc(offsetof(struct mystruct, chararray[number_of_elements]));
      ptr->len = number_of_elements;

      for (i = 0; i < number_of_elements; ++i) {
        ptr->chararray[i] = 'a' + i;
      }

  }

Encuentro que no tener que recordar si 1 necesita ser restado (o sumado o lo que sea) es bueno. Esto también tiene la ventaja de trabajar en situaciones donde 0 se usa en la definición de matriz, que no todos los compiladores admiten, pero algunos sí. Si la asignación se basa en offsetof() no necesita preocuparse por ese posible detalle que hace que sus matemáticas sean incorrectas.

También funciona sin cambios si la estructura es un miembro de matriz flexible C99.

  • Sin embargo, es difícil pasar por alto la afirmación de que C99 permite matrices de tamaño 0 en esta publicación de blog (C99 tiene la palabra crucial “no vacía” en 6.2.5:20). También está estandarizado que el tamaño de una matriz de tamaño n es n veces el tamaño del elemento, y el estándar supone implícitamente que el tamaño de un objeto nunca es cero en un par de lugares (aunque GCC se arriesgó a permitir cero matrices de tamaño como una extensión. No entiendo cómo aceptaron ese riesgo, pero tal vez si solo es al final de una estructura, está bien)

    – Pascal Cuoq

    14 mayo 2013 a las 22:36

  • Creo que la mención en el artículo acerca de que las matrices de longitud cero no son legales hasta C99 es una combinación un tanto imprecisa de dejar en claro que no es legal en C90 y que C99 permite tipos de matrices incompletas al final de una estructura (que no es demasiado de estirar desde matrices de tamaño cero incluso si no es precisamente eso).

    – Michael Burr

    14 mayo 2013 a las 22:51


avatar de usuario
Jesús Ramos

Desaconsejaría eso debido a posibles problemas de alineación, en su lugar considere esto:

struct my_struct
{
    char *arr_space;
    unsigned int len;
}

struct my_struct *ptr = malloc(sizeof(struct my_struct) + 10);
ptr->arr_space = ptr + 1;
ptr->len = 10;

Esto le dará ubicación y seguridad 🙂 y evitará problemas de alineación extraños.

Por problemas de alineación me refiero a posibles retrasos de acceso para acceder a la memoria no alineada.

En el ejemplo original, si agrega un byte o un miembro no alineado con palabras (byte, char, short), el compilador puede extender el tamaño de la estructura, pero en lo que respecta a su puntero, está leyendo la memoria directamente después del final de la estructura (no alineado). Esto significa que si tiene una matriz de un tipo alineado como int cada acceso generará un impacto en el rendimiento de las CPU que reciben impactos al leer memoria no alineada.

struct
{
    byte_size data;
    char *var_len;
    some_align added by compiler;
}

En el caso original, estará leyendo del some_align región que es solo de relleno, pero en mi caso, leerá desde la memoria adicional alineada después (lo que desperdicia algo de espacio, pero eso generalmente está bien).

Otro beneficio de hacer esto es que es posible obtener más localidad de las asignaciones asignando todo el espacio para miembros de longitud variable de un struct en una asignación en lugar de asignarlos por separado (evita los gastos generales de múltiples llamadas de asignación y le brinda una localidad de caché en lugar de rebotar en toda la memoria).

  • char las matrices no tienen ningún requisito de alineación; se pueden alinear en cualquier límite.

    – Adam Rosenfield

    14 mayo 2013 a las 22:03

  • @AhmedMasud: ¿Cómo puede int len; estar desalineado cuando es antes de la sección de tamaño variable?

    – Roddy

    14 mayo 2013 a las 22:10


  • @Jesus: lo siento, no: el último elemento de la estructura es una matriz de tamaño 1. Ya está alineado correctamente. Si asigna espacio para elementos adicionales, estos elementos también se alinearán correctamente.

    – Pablo R.

    14 mayo 2013 a las 22:13

  • @JesusRamos – pero eso es todavía no es un problema de alineación. Ese es solo un problema diferente.

    – Roddy

    14 mayo 2013 a las 22:17

  • @JesusRamos: Un miembro de matriz flexible puede solamente ser el último miembro de una estructura; del mismo modo para el “truco de estructura” más antiguo.

    –Keith Thompson

    14 mayo 2013 a las 22:52

avatar de usuario
clifford

Es ‘correcto’, pero necesitaría una buena razón para hacerlo en lugar de una solución más razonable. Más comúnmente, tal vez usaría esta técnica para “superponer” una matriz existente para imponerle algún tipo de estructura de encabezado.

Tenga en cuenta que GCC por extensión permite una miembro de matriz de longitud cero exactamente para este propósito, mientras que ISO C99 “legitima” la práctica al permitir un miembro con corchetes vacíos (solo como el último miembro).

Tenga en cuenta que hay algunos problemas semánticos: el tamaño de la estructura, por supuesto, no tendrá en cuenta el tamaño “flexible” del miembro final, y pasar la estructura “por valor” solo pasará el encabezado y el primer elemento (o ningún elemento usando el GCC extensión o miembro de matriz flexible C99). Del mismo modo, la asignación de estructura directa no copiará todos los datos.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad