У меня есть структура для содержания 4D вектор
struct {
float x;
float y;
float z;
float w;
} vector4f
И я пользуюсь библиотекой, которая имеет некоторые функции, которые воздействуют на векторы, но берут указатели плавающие в качестве их аргументов.
Действительно ли законно назвать что-то как doSomethingWithVectors( (float *) &myVector)
?
Может быть, это и работает, но не переносится, компилятор может выровнять вещи так, чтобы один плавающий не обязательно сразу следовал за другим.
.Если бы у меня была такая ситуация, я бы так и сделал: Псевдонимы переменных членов 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 guarentees, что если типы используются в видимом в настоящее время объединении, то можно прозрачно использовать вещи, расположенные в одном и том же порядке.
6.5 ясно показывает, что "эффективный тип" для любого доступа к элементу массива или полю структуры является "float", так что любые два доступа могут иметь псевдонимы. Доступ к структуре в целом на самом деле является доступом к каждому члену массива по очереди, поэтому применяются одни и те же правила псевдонимов.
Требование "union" является наиболее странным, но поскольку типы, объявленные, когда объединение видимо, должны быть совместимы с типами, определенными одинаково в другой компиляционной единице без объединения, те же самые правила в конечном итоге применяются даже там, где объединение не видимо.
Редактирование
Ключевым моментом здесь является различие между неопределенными вещами и вещами, которые определены в реализации. Для неопределенных вещей компилятор может делать что угодно, и может делать разные вещи при перекомпиляции одного и того же файла исходного текста. Для вещей, определенных реализацией, вы можете не знать точно, что он делает, но он должен делать одно и то же последовательно во всех модулях компиляции. Так что даже там, где спецификация точно не описывает, как что-то должно быть сделано, взаимодействие между различными вещами и тот факт, что вещи должны быть согласованы между модулями компиляции с помощью скомпилируемых объявлений типа, сильно ограничивает то, что компилятор может здесь сделать.
.Вы можете написать код, который попытается рассматривать его как массив, но язык не дает никаких гарантий насчет функциональности этого кода. Поведение неопределенное.
В языке Си взятие области хранения, занимаемой значением одного типа, и ее переинтерпретация как другого типа почти всегда противозаконна. Из этого правила есть несколько исключений (поэтому я сказал "почти"), например, можно переинтерпретировать любой объект как char-массив, но в целом это явно противозаконно.
Более того, возможные опасности не чисто теоретические, и речь идет не только о возможных различиях в выравнивании массивов и структур. Современные компиляторы могут полагаться (и полагаются) на вышеупомянутое правило языка для выполнения оптимизации алиасинга (читайте, например, о строгой семантике алиасинга в GCC). Короче говоря, компилятору разрешено транслировать код в предположении, что память, занятая структурой
, никогда не сможет перекрыть память, занятую массивом float
. Это часто приводит к неожиданным результатам, когда люди начинают использовать трюки, как в Вашем сообщении.
Нет, это не законно. И почти каждый раз, когда вы оказываетесь в гипсе, вы должны заподозрить, что с вашим кодом что-то глубоко не так. Я подозреваю, что кто-то вскоре предложит использовать профсоюз, но это тоже не будет законно.
Конечно, законно или нет, или подход, который вы предлагаете в вашем вопросе, или использование профсоюза, скорее всего, "сработает" - но это не то, о чем вы спрашивали.
. Вау. Есть много ответов, в которых говорится, что это сработает. Это не гарантируется стандартом C. В ответ на комментарий, запрашивающий реальный пример, есть этот отличный пост Криса Торека из comp.lang.c
. Окончательная цитата: Урок здесь тот же, что и всегда: "Если вы лжете компилятору, он получит свою месть"
Определенно необходимо прочитать для тех, кто думает, что это нормально - рассматривать структуру
однородных членов как массив.
Давайте просто выкинем все аргументы о правильном пути™, чтобы сделать что-нибудь в окне на минуту.
Работает ли это, чтобы трактовать эту структуру как массив? Да. Будет ли это работать во всех случаях, на всех компиляторах и платформах? Нет.
Floats обычно 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
Конечно, это сделало бы Вашу структуру больше и сложнее при инициализации
.