简体   繁体   English

用指针访问结构成员

[英]Access structure members with a pointer

In C, I am having a structure like this 在C语言中,我有一个像这样的结构

typedef struct
{
 char *msg1;
 char *msg2;
 .
 .
 char *msgN;
}some_struct;

some_struct struct1;
some_struct *pstruct1 = &struct1;

I want to keep a pointer or a varible which when incremented or decremented, gives the next/last member variable of this structure. 我想保留一个指针或变量,当递增或递减时,它会给出此结构的下一个/最后一个成员变量。 I do not want to use array of char * since it is already designed like this. 我不想使用char *数组,因为它已经像这样设计。

I tried using the union and structure combination, but I don't know how to write code for that. 我尝试使用联合和结构组合,但是我不知道如何编写代码。

Thought iterator may help but this is C. Any suggestions ? 思想迭代器可能有所帮助,但这是C。有什么建议吗?

You can't do that, safely. 您不能安全地这样做。 You can take a chance that the adjacent character pointers are really adjacent (with no padding) as if they were in an array, but you can't be sure so that's pretty much straight into the undefined behavior minefield. 您可以把握一下相邻字符指针实际上是相邻的(不带填充)的感觉,就好像它们在数组中一样,但是您无法确定,因此这很可能直接进入了未定义的行为雷区。

You can abstract it to an index, and do something like: 您可以将其抽象为索引,然后执行以下操作:

char * get_pointer(some_struct *p, int index)
{
  if(index == 0)
    return p->msg1;
  if(index == 1)
    return p->msg2;
  /* and so on */
  return NULL;
}

Then you get to work with an index which you can increment/decrement freely, and just call get_pointer() to map it to a message pointer when needed. 然后,您就可以使用可以自由增加/减少的索引,并且只需在需要时调用get_pointer()将其映射到消息指针即可。

You can do this using strict C, but you need to take certain precautions to ensure compliance with the standard. 您可以使用严格的C来执行此操作,但是需要采取某些预防措施以确保符合标准。 I will explain these below, but the precautions you need to take are: 我将在下面解释这些,但是您需要采取以下预防措施:

(0) Ensure there is no padding by including this declaration: (0)通过包含以下声明,确保没有填充:

extern int CompileTimeAssert[
    sizeof(some_struct) == NumberOfMembers * sizeof(char *) ? 1 : -1];

(1) Initialize the pointer from the address of the structure, not the address of a member: (1)从结构的地址而不是成员的地址初始化指针:

char **p = (char **) (char *) &struct1;

(I suspect the above is not necessary, but I would have to insert more reasoning from the C standard.) (我怀疑上面没有必要,但是我将不得不从C标准中插入更多的理由。)

(2) Increment the pointer in the following way, instead of using ++ or adding one: (2)以下列方式递增指针,而不是使用++或添加一个:

p = (char **) ((char *) p + sizeof(char *));

Here are explanations. 这里是解释。

The declaration in (0) acts as a compile-time assertion. (0)中的声明充当编译时声明。 If there is no padding in the struct, then the size of the struct equals the number of members multiplied by the size of a member. 如果该结构中没有填充,则该结构的大小等于成员数乘以成员大小。 Then the ternary operator evaluates to 1, the declaration is valid, and the compiler proceeds. 然后,三元运算符求值为1,说明有效,然后编译器继续执行。 If there is padding, the sizes are not equal, the ternary operator evaluates to -1, and the declaration is invalid because an array cannot have a negative size. 如果存在填充,则大小不相等,三元运算符求值为-1,并且声明无效,因为数组不能具有负大小。 Then the compiler reports an error and terminates. 然后,编译器报告错误并终止。

Thus, a program containing this declaration will compile only if the struct does not have padding. 因此,仅当该结构没有填充时,包含此声明的程序才会编译。 Additionally, the declaration will not consume any space (it only declares an array that is never defined), and it may be repeated with other expressions (that evaluate to an array size of 1 if their condition is true), so different assertions may be tested with the same array name. 此外,该声明将不占用任何空间(它仅声明一个从未定义的数组),并且可以与其他表达式重复(如果条件为真,则表达式的数组大小为1),因此可以使用不同的断言使用相同的阵列名称进行测试。

Items (1) and (2) deal with the problem that pointer arithmetic is normally guaranteed to work only within arrays (including a notional sentinel element at the end) (per C 2011 6.5.6 8). 项目(1)和(2)处理的问题是,通常保证指针算术仅在数组(包括末尾的概念性哨兵元素)中起作用(根据C 2011 6.5.6 8)。 However, the C standard makes special guarantees for character types, in C 2011 6.3.2.3 7. A pointer to the struct may be converted to a pointer to a character type, and it will yield a pointer to the lowest addressed byte of the struct. 但是,C标准在C 2011 6.3.2.3中对字符类型做出了特殊保证。7.指向该结构的指针可能会转换为指向字符类型的指针,它将产生一个指向该结构的最低寻址字节的指针。 Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object. 结果的连续递增(直到对象的大小)会产生指向对象剩余字节的指针。

In (1), we know from C 2011 6.3.2.3 7, that (char *) &struct1 is a pointer to the first byte of struct1 . 在(1)中,从C 2011 6.3.2.3 7中我们知道(char *) &struct1是指向(char *) &struct1的第一个字节的struct1 When converted to (char **) , it must be a pointer to the first member of struct1 (in particular thanks to C 2011 6.5.9 6, which guarantees that equal pointers point to the same object, even if they have different types). 转换为(char **) ,它必须是指向struct1的第一个成员的struct1 (尤其要感谢C 2011 6.5.9 6,它保证相等的指针指向相同的对象,即使它们的类型不同)也是如此。 。

Finally, (2) works around the fact that array arithmetic is not directly guaranteed to work on our pointer. 最后,(2)解决了不能直接保证数组算术对我们的指针起作用的事实。 That is, p++ would be incrementing a pointer that is not strictly in an array, so the arithmetic is not guaranteed by 6.5.6 8. So we convert it to a char * , for which increments are guaranteed to work by 6.3.2.3 7, we increment it four times, and we convert it back to char ** . 也就是说, p++将递增严格不在数组中的指针,因此6.5.6 8.不能保证该算术。因此我们将其转换为char * ,对于此值,保证可以按6.3.2.3的要求工作7 ,我们将其递增四倍,然后将其转换回char ** This must yield a pointer to the next member, since there is no padding. 由于没有填充,这必须产生一个指向下一个成员的指针。

One might claim that adding the size of char ** (say 4) is not the same as four increments of one char , but certainly the intent of the standard is to allow one to address the bytes of an object in a reasonable way. 有人可能会说,添加char **的大小(例如4)与一个char四个增量不同,但是可以肯定的是,该标准的目的是允许人们以一种合理的方式寻址对象的字节。 However, if you want to avoid even this criticism, you can change + sizeof(char *) to be +1+1+1+1 (on implementations where the size is 4) or +1+1+1+1+1+1+1+1 (where it is 8). 但是,即使您想避免这种批评,也可以将+ sizeof(char *)更改为+1+1+1+1 (在大小为4的实现中)或+1+1+1+1+1+1+1+1 (这里是8)。

Take the address of the first member and store it to char ** : 获取第一个成员的地址并将其存储到char **

char **first = &struct1.msg1;
char **last = &struct1.msg1 + sizeof(some_struct) / sizeof(char *) - 1;

char **ptr = first;  /* *ptr is struct.msg1 */
++ptr;               /* now *ptr is struct1.msg2 */

This assumes that the structure only contains char * members. 假定结构仅包含char *成员。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM