Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 指针结构是否保证在没有填充位的情况下表示?_C_Arrays_Struct - Fatal编程技术网

C 指针结构是否保证在没有填充位的情况下表示?

C 指针结构是否保证在没有填充位的情况下表示?,c,arrays,struct,C,Arrays,Struct,我有一个链接列表,其中存储我的应用程序的设置组: typedef struct settings { struct settings* next; char* name; char* title; char* desc; char* bkfolder; char* srclist; char* arcall; char* incfold; } settings_row; settings_row* first_profile = { 0 }; #define S

我有一个链接列表,其中存储我的应用程序的设置组:

typedef struct settings {
  struct settings* next;
  char* name;
  char* title;
  char* desc;
  char* bkfolder;
  char* srclist;
  char* arcall;
  char* incfold;
} settings_row;
settings_row* first_profile = { 0 };

#define SETTINGS_PER_ROW 7
当我将值加载到此结构中时,我不希望必须命名所有元素。我更愿意将其视为一个命名数组——值从文件中按顺序加载,并以增量方式放入结构中。然后,当我需要使用这些值时,我会按名称访问它们

//putting values incrementally into the struct
void read_settings_file(settings_row* settings){
    char* field = settings + sizeof(void*);
    int i = 0;
    while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW);
}

//accessing components by name
void settings_info(settings_row* settings){
    printf("Settings 'profile': %s\n", settings.title);
    printf("Description: %s\n", settings.desc);
    printf("Folder to backup to: %s\n", settings.bkfolder);
}
//递增地将值放入结构中
作废读取设置文件(设置行*设置){
字符*字段=设置+sizeof(void*);
int i=0;
while(将值读入(字段[i])&&i++
但我想知道,既然这些都是指针(并且在这个结构中只有指针),编译器会给这些值中的任何一个添加填充吗?它们是否保证按此顺序排列,并且值之间没有任何差异?我的方法有时会起作用,但会断断续续地失败吗

编辑以澄清 我意识到编译器可以填充结构的任何值——但考虑到结构(指针结构)的性质,我认为这可能不是问题。由于32位处理器寻址数据的最有效方式是在32位块中,因此编译器就是这样在结构中填充值的(即,结构中的int,short,int将在short之后添加2个字节的填充,使其成为32位块,并将下一个int与下一个32位块对齐)。但是,由于32位处理器使用32位地址(我认为64位处理器使用64位地址),填充是否完全没有必要,因为结构的所有值(地址,从本质上讲是有效的)都在理想的32位块中


我希望一些内存表示/编译器行为大师能为编译器提供一些线索,说明编译器是否有理由填充这些值

你不能用你正在尝试的方式来填充这些值。允许编译器填充结构的任何和所有成员。我认为不允许对字段进行重新排序

大多数编译器都有一个属性,可以应用于结构以对其进行打包(即将其转换为无填充的紧密打包存储的集合),但缺点是这通常会影响性能。打包标志可能允许您按照自己的方式使用结构,但它可能无法跨各种平台移植


填充旨在使目标体系结构上的字段访问尽可能高效。除非你必须这样做,否则最好不要与之抗争(即,结构转到磁盘或通过网络)。

虽然不是重复的,但这可能回答了你的问题:


应用程序将整个结构写入一个文件并再次读取,这种情况并不少见。但是,有一天,该文件可能需要在另一个平台上读回,或者由另一个版本的编译器以不同的方式打包该结构。(虽然这可以通过理解原始包装格式的专门编写的代码来处理)。

从技术上讲,您只能依赖订单;编译器可以插入填充。如果不同的指针大小不同,或者指针大小不是自然的单词大小,则可能会插入填充

实际上,你可以侥幸逃脱。我不推荐它;这是一个坏的,肮脏的把戏


您可以通过另一个间接级别(这还不能解决什么问题?)来实现目标,或者通过使用一个初始化为指向结构的各个成员的临时数组来实现目标。

这并不能保证,但在大多数情况下都可以正常工作。它不会是断断续续的,它要么在具有特定构建的特定平台上工作,要么不工作。因为您使用的是所有指针,所以大多数编译器不会弄乱任何填充


此外,如果您希望更安全,可以将其设置为联合。

在许多情况下,指针是自然字长的,因此编译器不太可能填充每个成员,但这并不是一个好主意。如果要将其视为数组,则应使用数组

我在这里大声思考,所以可能有很多错误,但也许你可以尝试这种方法:

enum
{
    kName = 0,
    kTitle,
    kDesc,
    kBkFolder,
    kSrcList,
    kArcAll,
    kIncFold,
    kSettingsCount
};

typedef struct settings {
    struct settings* next;
    char *settingsdata[kSettingsCount];
} settings_row;
设置数据:

settings_row myRow;
myRow.settingsData[kName] = "Bob";
myRow.settingsData[kDescription] = "Hurrrrr";
...
void read_settings_file(settings_row* settings){
    char** field = settings->settingsData;
    int i = 0;
    while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW);
}
读取数据:

settings_row myRow;
myRow.settingsData[kName] = "Bob";
myRow.settingsData[kDescription] = "Hurrrrr";
...
void read_settings_file(settings_row* settings){
    char** field = settings->settingsData;
    int i = 0;
    while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW);
}
作废读取设置文件(设置行*设置){
字符**字段=设置->设置数据;
int i=0;
while(将值读入(字段[i])&&i++
在POSIX规则下,所有指针(函数指针和数据指针)都要求大小相同;仅在ISO C下,所有数据指针都可以转换为“
void*
”并返回而不丢失信息(但函数指针不需要转换为“
void*
”而不丢失信息,反之亦然)

因此,如果编写正确,代码将正常工作。不过,它写得不太正确!考虑:

void read_settings_file(settings_row* settings)
{
    char* field = settings + sizeof(void*);
    int i = 0;
    while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW)
        ;
}
添加前的演员阵容至关重要


C标准(ISO/IEC 9899-1999):

6.3.2.3指针

指向void的指针可以转换为指向任何不完整或不完整对象的指针,也可以转换为指向任何不完整或不完整对象的指针 类型。指向任何不完整或对象类型的指针可以转换为指向void的指针 然后再回来;结果应与原始指针进行比较

[……]

指向一种类型函数的指针可以转换为指向另一种类型函数的指针 再次输入并返回;结果应与原始指针进行比较。如果一个 指针用于调用类型与指向类型不兼容的函数, 该行为未定义


在我看来,这种方法产生的问题比解决的问题多。 六个月后,当你阅读这段代码时,你还会是一名工程师吗