Могу ли я рассматривать структуру как массив?

У меня есть структура для хранения 4D вектора

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

И я использую библиотеку, которая имеет некоторые функции, которые работают с векторами, но принимают в качестве аргументов указатели с плавающей точкой.

Законно ли называть что-то вроде doSomethingWithVectors( (float *) &myVector)?

8 ответов

Решение

Это могло бы работать, но это не переносимо, компилятор может выровнять вещи так, чтобы один float не обязательно сразу следовал за другим.

Вы можете написать код, который попытается обработать его как массив, но язык не дает никаких гарантий относительно функциональности этого кода. Поведение не определено.

В языке C взятие области хранения, занятой значением одного типа, и ее повторное толкование как другого типа почти всегда недопустимо. Есть несколько исключений из этого правила (именно поэтому я сказал "почти"), например, вы можете интерпретировать любой объект как массив символов, но в целом это явно недопустимо.

Более того, возможные опасности не являются чисто теоретическими, и речь идет не только о возможных различиях выравнивания между массивами и структурами. Современные компиляторы могут (и делают) полагаться на вышеупомянутое правило языка для выполнения оптимизации псевдонимов (для примера читайте о строгой семантике псевдонимов в GCC). Короче говоря, компилятору разрешено переводить код в предположении, что память занята struct никогда не может перекрывать память, занятую массивом float, Это часто приводит к неожиданным результатам, когда люди начинают использовать трюки, как в вашем посте.

Вау. Есть много ответов о том, что это сработает. Это не гарантируется стандартом C. В ответ на комментарий с просьбой привести пример из реальной жизни, есть отличная запись Криса Торека из comp.lang.c, Последняя цитата: урок здесь тот же, что и всегда: "Если вы лжете компилятору, он отомстит".

Обязательно прочитайте те, кто считает, что нормально относиться к struct однородных членов в виде массива.

Давайте просто выбросим все аргументы о "Правильном пути", чтобы что-то сделать из окна на минуту.

Это работает, чтобы рассматривать эту структуру как массив? Да. Будет ли это работать во всех случаях, на всех компиляторах и платформах? Нет.

Плавания обычно бывают 32-разрядными, и даже на моей 64-разрядной машине они выровнены по границам 32-разрядных слов.

#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);
}

Если вам нужен массив, почему бы вам не разместить массив внутри вашей структуры и не использовать его? Я полагаю, вы могли бы добавить указатели, если вы все еще хотите получить к нему доступ таким же образом, используя vector4f.x vector4f.y vector4f.z

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

Конечно, это сделает вашу структуру больше и сложнее инициализации.

Нет, это не законно. И почти каждый раз, когда вы обнаруживаете, что используете приведение, вы должны подозревать, что с вашим кодом что-то не так. Я подозреваю, что в ближайшее время кто-то предложит использовать профсоюз, но это также не будет законным.

Конечно, законно или нет, подход, который вы предлагаете в своем вопросе, или использование профсоюза, вероятно, "сработает", но это не то, что вы просили.

Если бы у меня была такая ситуация, я бы так и сделал: псевдонимы переменных-членов C++?

В вашем случае это будет выглядеть так:

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;

Тем не менее, это не решает проблему обработки структуры как массива, если это именно то, что вы хотели знать.

Да, это будет работать на любом компиляторе, который следует стандарту C99. Он также, вероятно, будет работать с компиляторами, использующими более ранние, менее понятные стандарты.

Вот почему:

6.2.5.20 определяет массивы как "последовательно расположенные" элементы, в то время как структуры являются "последовательно распределенными" членами, и, пока все члены структуры имеют одинаковый тип, это действительно одно и то же.

6.5.2.3 гарантирует, что если типы используются в объединении, видимом в настоящее время, вы можете прозрачно использовать элементы, расположенные в том же порядке.

6.5 проясняет, что "эффективный тип" для любого доступа к элементу массива или полю структуры - это "float", поэтому любые два доступа могут иметь псевдоним. Доступ к структуре в целом на самом деле является доступом к каждому члену, поэтому применяются те же правила наложения имен.

Требование 'union' является наиболее странным, но поскольку типы, объявленные там, где объединение является видимым, должны быть совместимы с типами, определенными одинаково в другом модуле компиляции без объединения, те же правила в конечном итоге применяются даже там, где объединение не видно.

редактировать

Ключевым моментом здесь является различие между вещами, которые не определены, и вещами, которые определены реализацией. Для неопределенных вещей компилятор может что угодно и может делать разные вещи при перекомпиляции одного и того же исходного файла. Для вещей, определяемых реализацией, вы можете не знать точно, что он делает, но он должен делать то же самое последовательно во всех единицах компиляции. Таким образом, даже там, где спецификация точно не объясняет, как что-то должно быть сделано, взаимодействие между разными вещами и тот факт, что вещи должны быть согласованными между единицами компиляции с декларациями совместимых типов, сильно ограничивают возможности компилятора.

Другие вопросы по тегам