我对C中的结构有疑问。所以当你创建一个结构时,你实际上是在定义一块内存的框架。因此,当您创建结构的实例时,您正在创建一个内存块,以便它能够容纳一定数量的元素。
但是,我对点运算符的作用感到有些困惑。如果我有struct Car
并且有一个名为GasMileage
的成员(int
成员),我可以通过执行类似的操作来获取GasMileage
的值,< / p>
int x = CarInstance.GasMileage;
然而,我对这个点运算符实际发生的事情感到困惑。点运算符是否只是作为基址的偏移量?它究竟是如何推断它是一个int?
我想我对幕后发生的事情很好奇。通过做其他事情可以引用GasMileage
吗?如
int *GasMileagePointer = (&carInstance + offsetInBytes(GasMileage));
int x = *GasMileage
这只是我快速弥补的事情。我一直在努力寻找一个好的解释,但似乎没有什么比将点运算符视为魔术更能解释它了。
答案 0 :(得分:5)
当您使用.
运算符时,编译器会根据其前面的字段(和填充)的大小将其转换为struct
内的偏移量。
例如:
struct Car {
char model[52];
int doors;
int GasMilage;
};
假设int
是4个字节且没有填充,model
的偏移量为0
,doors
的偏移量为52
,偏移量GasMilage
的数字是56。
因此,如果你知道成员的偏移量,你可以得到一个指向它的指针:
int *GasMileagePointer = (int*)((char *)&carInstance + offsetInBytes(GasMile));
强制转换为char *
是必要的,因此指针算法一次只能传输1个字节而不是1 sizeof(carInstance)
。然后需要将结果转换为正确的指针类型,在本例中为int *
答案 1 :(得分:2)
是的,点运算符只是从结构的底部应用一个偏移量,然后访问该地址的值。
int x = CarInstance.GasMileage;
相当于:
int x = *(int *)((char*)&CarInstance + offsetof(Car, GasMileage));
对于其他类型T
的成员,唯一的区别是广告(int *)
变为(T *)
。
答案 2 :(得分:1)
点运算符只需选择成员。
由于编译器具有关于成员的类型(以及因此 size )的信息(实际上是所有成员),因此它知道成员从一开始的偏移量结构,并可以生成适当的指令。它可以生成基本+偏移访问,但它也可以直接访问成员(或者甚至将其缓存在寄存器中)。编译器具有所有这些选项,因为它在编译时具有所有必要的信息。
如果没有,例如<strong>不完整类型,则会出现编译时错误。
答案 3 :(得分:0)
当它工作时,“。” “。”的行为运算符等效于获取结构的地址,通过成员的偏移量对其进行索引,并将其转换为成员类型的指针,并将其解除引用。但是,该标准规定,在某些情况下,不能保证不起作用。例如,给定:
struct s1 {int x,y; }
struct s2 {int x,y; }
void test1(struct s1 *p1, struct s2 *p2)
{
s1->x++;
s2->x^=1;
s1->x--;
s2->x^=1;
}
编译器可能会认为没有合法的方式p1-&gt; x和p2-&gt; x 可以识别相同的对象,因此它可以重新排序代码以便++ - 对s1-> x取消的操作,以及对s2-> x取消的^ = 1操作, 从而留下一个什么都不做的功能。
请注意,使用联合时行为不同,因为给定:
union u { struct s1 v1; struct s2 v2; };
void test2(union u *uv)
{
u->v1.x^=1;
u->v2.x++;
u->v1.x^=1;
u->v2.x--;
}
common-initial-subsequence规则表示因为u-> v1和u-> v2 从相同类型的字段开始,访问u-&gt; v1中的这样一个字段 相当于访问u-> v2中的相应字段。因此,一个 编译器不允许重新排序。另一方面,给定
void test1(struct s1 *p1, struct s2 *p2);
void test3(union u *uv)
{
test1(&(u.v1), &(u.v2));
}
u.v1和u.v2以匹配字段开头的事实并不能防范 编译器假设指针不会别名。
请注意,某些编译器提供了强制生成代码的选项
成员访问总是与上述指针等效
操作。对于gcc,选项为-fno-strict-alias
。如果需要代码
访问不同结构类型的公共初始成员,省略它
切换可能导致一个人的代码在奇怪,怪异和不可预测的情况下失败
方式。