¿Puedo tratar una estructura como una matriz?

9 minutos de lectura

Tengo una estructura para sostener un vector 4D.

struct {
    float x;
    float y;
    float z;
    float w;
} vector4f

Y estoy usando una biblioteca que tiene algunas funciones que operan en vectores pero toman punteros flotantes como argumentos.

¿Es legal llamar algo como doSomethingWithVectors( (float *) &myVector)?

  • Esto siempre es LEGAL, ya que siempre se pueden usar conversiones: la verdadera pregunta es si el comportamiento resultante está bien definido, definido por la implementación o indefinido.

    – Chris Dodd

    28 de diciembre de 2009 a las 1:14

Podría funcionar pero no es portátil, el compilador es libre de alinear las cosas para que un flotador no necesariamente siga inmediatamente al otro.

  • -1 funcionará y es portátil. El empaque de estructura y el empaque de matriz siguen las mismas reglas.

    – John Knoeller

    27 de diciembre de 2009 a las 23:15

  • @John Knoeller: Incorrecto. En primer lugar, el lenguaje C no tiene “reglas de empaquetado” que serían las mismas para matrices y estructuras. De hecho, las reglas son explícitamente diferentes: las estructuras pueden agregar relleno, las matrices no. En segundo lugar, este código se rompería por más razones que solo la posible diferencia de empaque. La violación estricta de aliasing, por ejemplo, es un problema aún más importante que el empaquetado.

    – Ant

    27 de diciembre de 2009 a las 23:25

  • @Aidan, las matrices no pueden tener relleno entre los miembros. Si tengo T a[N];luego a y a+1 están sizeof(T) bytes separados.

    – Alok Singhal

    28 de diciembre de 2009 a las 0:08

  • Para aquellos que (como yo) no sabían sobre el “aliasing estricto” en C, en realidad hay una pregunta sobre eso que lo explica muy bien: stackoverflow.com/questions/98650/…

    – sleske

    28 de diciembre de 2009 a las 0:39

  • John: todos los compiladores deben seguir la regla del lenguaje de &a[n] + 1 == &a[n + 1]. No existe tal regla para los miembros de la estructura. Si desea usar #pragmas específicos del compilador o similar para controlar el diseño de la estructura, está bien; pero no veo nada de eso en el código mencionado. Finalmente, las especificaciones del compilador son obviamente no portátil.

    Roger Paté

    28 de diciembre de 2009 a las 1:25

Puede escribir código que intente tratarlo como una matriz, pero el lenguaje no garantiza la funcionalidad de ese código. El comportamiento es indefinido.

En lenguaje C, tomar una región de almacenamiento ocupada por un valor de un tipo y reinterpretarlo como otro tipo casi siempre es ilegal. Hay algunas excepciones a esa regla (por eso dije “casi”), como que puedes reinterpretar cualquier objeto como una matriz de caracteres, pero en general es explícitamente ilegal.

Además, los posibles peligros no son puramente teóricos, y no se trata solo de las posibles diferencias de alineación entre matrices y estructuras. Los compiladores modernos pueden confiar (y lo hacen) en la regla del lenguaje antes mencionada para realizar optimizaciones de alias (lea sobre alias estricto semántica en GCC, por ejemplo). En resumen, el compilador puede traducir código bajo el supuesto de que la memoria ocupada por un struct nunca puede superponerse a la memoria ocupada por una matriz de float. Esto a menudo conduce a resultados inesperados cuando las personas comienzan a usar trucos como en tu publicación.

  • Bien, también lo del aliasing.

    – Alok Singhal

    28 de diciembre de 2009 a las 0:31

  • Buena explicación. Pero es irrelevante para la pregunta. myfunc(&myVector.x) es un puntero a un flotante, y el código del otro lado PUEDE tratarlo como una matriz de flotantes. No hay violación de alias.

    – John Knoeller

    28 de diciembre de 2009 a las 1:09

  • @John: Buen punto. Sin embargo, el código de reinterpretación en la pregunta original se ve diferente.

    – Ant

    28 de diciembre de 2009 a las 1:53

  • John: Eso también es irrelevante. El código del otro lado puede tratar eso como una matriz de flotantes, solo si esa matriz tiene una longitud de 1. Esto no debería sorprender y es cierto para cualquier objeto: void f(int* p); int n; f(&n); o struct { /*...*/ int n; /*...*/ } s; f(&s.n)f puede tratar a p como apuntando al primer elemento en un int[1].

    Roger Paté

    28 de diciembre de 2009 a las 2:20

Vaya Hay muchas respuestas que dicen que funcionará. No está garantizado por el estándar C. En respuesta a un comentario que solicita un ejemplo del mundo real, existe este excelente publicación por chris torek desde comp.lang.c. La cita final es: La lección aquí es la misma de siempre: “Si le mientes al compilador, se vengará”.

Definitivamente una lectura obligada para aquellos que piensan que está bien tratar un struct de miembros homogéneos como una matriz.

  • Los escritores de compiladores son puristas. Pero termina el final, ellos no vengarse de construcciones como esta, si lo hicieran nadie usaría sus compiladores.

    – John Knoeller

    28 de diciembre de 2009 a las 0:57

  • John, no es que los escritores de compiladores estén ahí para vengarse. Pero, si tienes expectativas poco razonables, las cosas voluntad ir mal. Hay formas mucho más limpias y seguras de hacer lo que quiere hacer.

    – Alok Singhal

    28 de diciembre de 2009 a las 17:25

Dejemos todos los argumentos sobre Right Way™ para hacer algo por la ventana por un minuto.

¿Funciona tratar esa estructura como una matriz? Si. ¿Funcionará en todos los casos, en todos los compiladores y plataformas? No.

Los flotantes tienden a ser de 32 bits, e incluso en mi máquina de 64 bits, se alinean en límites de palabras de 32 bits.

#include <stdio.h>

struct {
        float x;
        float y;
        float z;
        float w;
} vector4f;

float array4f[4];

int main(int argc, char **argv) {
        printf("Struct: %lu\n", sizeof(vector4f)); // 16
        printf(" Array: %lu\n", sizeof(array4f));  // 16

        return (0);
}

No, no es legal. Y casi cada vez que te encuentres usando un molde, debes sospechar que hay algo profundamente mal con tu código. Sospecho que en breve alguien sugerirá usar una unión, pero eso tampoco será legal.

Por supuesto, legal o no, el enfoque que sugiere en su pregunta o el uso de un sindicato probablemente “funcionará”, pero eso no es lo que preguntó.

  • ¿Podría explicar qué quiere decir con “legal”? Para mí, una parte del código C es legal si se compila sin errores. Esto debería compilarse sin errores ni advertencias y probablemente incluso hará lo que se supone que debe hacer, asumiendo la configuración adecuada de empaquetamiento de estructura/alineación.

    – Niki

    27 de diciembre de 2009 a las 23:00

  • No legal ya que en la norma no da ninguna garantía sobre su comportamiento. Puede compilarse en gcc con la configuración adecuada, pero eso no lo convierte en un código C válido.

    – Juan

    27 de diciembre de 2009 a las 23:04

  • El código C es legal si se compila y ejecuta correctamente con cualquier compilador compatible con el estándar. La suposición de alineación/empaquetado de estructuras violaría esa regla, ya que a los compiladores se les permite específicamente variar el empaque de estructuras según lo deseen.

    – Chris Arguin

    27 de diciembre de 2009 a las 23:04

  • El programa @nikie AC es legal si se adhiere al Estándar C. Puedo (y tengo) escribir algo que llamo un compilador C que puede compilar cualquier basura vieja.

    luego

    27 de diciembre de 2009 a las 23:15

  • nikie: Hay problemas para los que el estándar C no requiere ningún diagnóstico. Expresión común en ciertos círculos (estudiantes): “¡Dios mío, FUNCIONA! … Quiero decir que compila, ¿qué es un error de segmento?”

    Roger Paté

    28 de diciembre de 2009 a las 1:28

¿Puedo tratar una estructura como una matriz
Derek Litz

Si desea una matriz, ¿por qué no coloca una matriz dentro de su estructura y la usa? Supongo que podría agregar punteros si aún desea acceder a él de la misma manera usando vector4f.x vector4f.y vector4f.z

struct {
    float vect[4];
    float* x;
    float* y;
    float* z;
    float* w;
} vector4f

Por supuesto, eso haría que su estructura fuera más grande y más complicada de inicializar.

  • ¿Podría explicar qué quiere decir con “legal”? Para mí, una parte del código C es legal si se compila sin errores. Esto debería compilarse sin errores ni advertencias y probablemente incluso hará lo que se supone que debe hacer, asumiendo la configuración adecuada de empaquetamiento de estructura/alineación.

    – Niki

    27 de diciembre de 2009 a las 23:00

  • No legal ya que en la norma no da ninguna garantía sobre su comportamiento. Puede compilarse en gcc con la configuración adecuada, pero eso no lo convierte en un código C válido.

    – Juan

    27 de diciembre de 2009 a las 23:04

  • El código C es legal si se compila y ejecuta correctamente con cualquier compilador compatible con el estándar. La suposición de alineación/empaquetado de estructuras violaría esa regla, ya que a los compiladores se les permite específicamente variar el empaque de estructuras según lo deseen.

    – Chris Arguin

    27 de diciembre de 2009 a las 23:04

  • El programa @nikie AC es legal si se adhiere al Estándar C. Puedo (y tengo) escribir algo que llamo un compilador C que puede compilar cualquier basura vieja.

    luego

    27 de diciembre de 2009 a las 23:15

  • nikie: Hay problemas para los que el estándar C no requiere ningún diagnóstico. Expresión común en ciertos círculos (estudiantes): “¡Dios mío, FUNCIONA! … Quiero decir que compila, ¿qué es un error de segmento?”

    Roger Paté

    28 de diciembre de 2009 a las 1:28

1647708550 674 ¿Puedo tratar una estructura como una matriz
Comunidad

Si tuviera esta situación, esto es lo que haría: ¿Alias ​​de variables miembro de C++?

En tu caso quedaría así:

struct {
        float& x() { return values[0]; }
        float& y() { return values[1]; }
        float& z() { return values[2]; }
        float& w() { return values[3]; }

        float  operator [] (unsigned i) const { return this->values_[i]; }
        float& operator [] (unsigned i)       { return this->values_[i]; }
        operator float*() const { return this->values_; }

private:
        float[4] values_;
} vector4f;

Sin embargo, esto no aborda la cuestión de tratar una estructura como una matriz si eso es específicamente lo que desea saber.

  • La pregunta es sobre C, no C++.

    –Emerick Rogul

    28 de diciembre de 2009 a las 0:31

¿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