Я признаюсь, что идея, что я могу разместить struct
над локально определенным массивом таким образом, откровенно экзотична. Я все еще утверждаю, что C99 и все последующие стандарты позволяют это. Если факт, что это очень спорно, что его членами являются объекты сами по себе первый буллит в 6.7.5 это позволяет:
типа, совместимого с эффективным типом объекта
Я думаю, что Мэтт McNabb-х точка.
Рассматривая проблему другим способом, отметим, что абсолютно законно (в строго соответствующей среде) псевдоним участника sp->x
как объект в своем собственном праве.
В контексте кода в моем OP рассмотрим функцию с прототипом void doit(int* ip,s* sp);
следующий вызов должен вести себя логично:
doit(&(sp->x),sp);
NB: логика Программа может (конечно же) не может вести себя по желанию. Например, если doit
увеличивает sp->x
до достижения *ip
, тогда возникает проблема! Однако то, что не допускается в компиляторе-совместителе, заключается в том, что результат искажается артефактами из-за того, что оптимизатор игнорирует потенциал псевдонимов.
Я утверждаю, что C будет все слабее, если язык, необходимым мне код:
int temp=sp->x;
doit(&temp,sp);
sp->x=temp;
Imagine всех случаев, когда любой вызов любой функции должен быть охраняются для возможного доступа алиасов к любой части передаваемых структур. Такой язык, вероятно, был бы непригодным для использования.
Очевидно, что трудно оптимизирующий (т.е. несоответствующим) компилятор может сделать полный хэш doit()
, если он не признает, что ip
может быть псевдоним члена в середине sp
. Это не относится к этой дискуссии.
Чтобы указать, когда компилятор может (и не может) делать такие предположения, понимается как причина, по которой стандарт должен устанавливать очень точные параметры вокруг сглаживания. То есть дать оптимизатору некоторые условия для диспропорции. На языке низкого уровня, таком как «C», может быть разумным (даже желательно) сказать, что подходящий выровненный указатель на доступный действительный шаблон бита может использоваться для доступа к значению.
Совершенно установлено, что sp->x
в моем OP указывает на правильно выровненное местоположение, содержащее действительный номер unsigned int
.
Умные проблемы заключаются в том, согласен ли компилятор/оптимизатор, что это законный способ получить доступ к этому местоположению или игнорировать его как неопределенное поведение.
Как показывает пример doit()
, структура абсолютно разрушена и рассматривается как отдельные объекты, которые просто имеют особые отношения.
Этот вопрос, по-видимому, касается обстоятельств, когда набор членов, которые имеют такие особые отношения, может иметь структуру, «наложенную на них».
Я думаю, что большинство людей согласятся с тем, что программа в нижней части этого ответа выполняет действительную, полезную функциональность, которая, если она связана с некоторой библиотекой ввода-вывода, может «абстрагировать» значительную часть работы, необходимой для чтения и записи структур. Вы можете подумать, что есть лучший способ сделать это, но я не ожидаю, что многие люди подумают, что это не необоснованный подход.
Он работает именно таким образом - он строит член структуры членом, затем обращается к нему через эту структуру.
Я подозреваю, что некоторые люди, которые возражают против кода в OP, более расслаблены. Во-первых, он работает с памятью, выделенной из бесплатного хранилища, как «непечатанная» универсальная система хранения. Во-вторых, он строит целую структуру. В OP я указываю правила (по крайней мере, по-видимому, разрешают), что вы можете выровнять биты структуры и до тех пор, пока вы только отмените ссылки на эти биты, все в порядке.
Я несколько разделяю это отношение. Я думаю, что ОП немного извращен и язык растягивается в плохо написанном уголке стандарта. Не то, чтобы надеть рубашку.
Тем не менее, я абсолютно думаю, что было бы ошибкой запрещать приведенные ниже методы, поскольку они исключают логически очень эффективный метод, который распознает структуры, которые могут быть созданы из объектов, столь же разбитых на них.
Однако я скажу, что что-то вроде этого - единственное, что я мог придумать, где такой подход кажется целесообразным. Но, с другой стороны, если вы не можете разделить данные AND/OR, соедините их, тогда вы быстро начнете нарушать понятие в структурах C, это POD - возможно дополненная сумма их частей, не более того, не что иное.
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
typedef enum {
is_int, is_double //NB:TODO: support more types but this is a toy.
} type_of;
//This function allocates and 'builds' an array based on a provided set of types, offsets and sizes.
//It's a stand-in for some function that (say) reads structures from a file and builds them according to a provided
//recipe.
int buildarray(void**array,const type_of* types,const size_t* offsets,size_t mems,size_t sz,size_t count){
const size_t asize=count*sz;
char*const data=malloc(asize==0?1:asize);
if(data==NULL){
return 1;//Allocation failure.
}
int input=1;//Dummy...
const char*end=data+asize;//One past end. Make const for safety!
for(char*curr=data;curr<end;curr+=sz){
for(size_t i=0;i<mems;++i){
char*mem=curr+offsets[i];
switch(types[i]){
case is_int:
*((int*)mem)=input++;//Dummy...Populate from file...
break;
case is_double:
*((double*)mem)=((double)input)+((double)input)/10.0;//Dummy...Populate from file...
++input;
break;
default:
free(data);//Better than returning an incomplete array. Should not leak even on error conditions.
return 2;//Invalid type!
}
}
}
if(array!=NULL){
*array=data;
}else{
free(data);//Just for fun apparently...
}
return 0;
}
typedef struct {
int a;
int b;
double c;
} S;
int main(void) {
const type_of types[]={is_int,is_int,is_double};
const size_t offsets[]={offsetof(S,a),offsetof(S,b),offsetof(S,c)};
S* array=NULL;
const size_t size=4;
int err=buildarray((void **)&array,types,offsets,3,sizeof(S),size);
if(err!=0){
return EXIT_FAILURE;
}
for(size_t i=0;i<size;++i){
printf("%zu: %d %d %f\n",i,array[i].a,array[i].b,array[i].c);
}
free(array);
return EXIT_SUCCESS;
}
Я думаю, что это интересное напряжение. C предназначен для того, чтобы быть низкоуровневым языком высокого уровня и дать программисту почти прямой доступ к машинным операциям и памяти. Это означает, что программист может выполнять произвольные требования аппаратных устройств и писать высокоэффективный код. Однако, если программисту дается абсолютный контроль, такой как моя точка зрения о приближении «если он подходит», к псевдониму, то оптимизатор получает свою игру испорченной. Настолько странно, что стоит немного вернуть производительность, чтобы вернуть дивиденд от оптимизатора.
Раздел 6.5 стандарта C99 пытается (и не полностью преуспевает), чтобы установить эту границу.
Нарушение правил строгой сглаживания может привести к тому, что компиляторы с высокой степенью оптимизации будут генерировать код, который не сделает то, что вы намереваетесь. Я видел, что чтение/запись переупорядочено (через указатели, которые, по его мнению, не могли * указать на один и тот же объект, но сделали), вызывая, по-видимому, присваивание «пропущенных». –
Мне это хорошо известно. Этот параграф явно пытается объявить, что должно быть соблюдено и не должно соблюдаться сглаживание строго совместимых компиляторов. Что происходит, когда вы просите своего компилятора игнорировать некоторые из более осторожных правил и отправляться в город, выходит за рамки этого вопроса. – Persixty
Не переходя через ссылочный документ еще раз, если я правильно помню, это действительно означало обращение к экземплярам, в которых массив был членом самой структуры, а не просто приписывал тип указателю типа struct. Поэтому, если вы хотите передать массив по значению, поместите его как элемент данных в структуру и передайте структуру по ссылке – fayyazkl