怎么制作网站数据包导入小程序,建设公司和建筑公司哪个好,郑州竞价托管代运营,长沙广告公司排名参考链接#xff1a;Structure pointer pointing to different structure instance 注#xff1a;可以查看此篇的问题和唯一的回复#xff0c;那是相对正确的#xff0c;不要看comment#xff0c;有很多错误。 我是拒绝分析这种问题的#xff0c;因为似乎没有人会这么乱用… 参考链接Structure pointer pointing to different structure instance 注可以查看此篇的问题和唯一的回复那是相对正确的不要看comment有很多错误。 我是拒绝分析这种问题的因为似乎没有人会这么乱用但是……在华保健老师的编译原理示例代码和Linux0.11内核中就遇到了这么神奇的代码那就不得不研究一下了毕竟是大神写的代码我不知道应该是我渣。
1 测试代码
#include stdio.h
#include stdlib.hstruct A {char a;int b;
};struct B {int c;int d;
};struct C {int e;char f;
};int main() {struct A a { a, 100 };struct B b { 101, 300 };struct C c { 200,c };// 根据字节对齐都占据8字节printf(A: size %d %c %d\n, sizeof(a), a.a, a.b);printf(B: size %d %d %d\n, sizeof(b), b.c, b.d);printf(C: size %d %d %c\n, sizeof(c), c.e, c.f);struct A *ap b; // A结构体指针指向结构体Bprintf(%d %d\n,ap-a, ap-b);printf(%c %d\n, ap-a, ap-b);char *chp b;chp[1] b; // 这块区域其实是字节对齐导致的空闲空间printf(%d %d\n, ap-a, ap-b);printf(%c %d\n, ap-a, ap-b);/* 如何访问这块内存取决于ap指针能访问多大地方取决于内存区域本身 */ap-a c; // ap-a c就是相当于 char a c;ap-a 1000; // ap-a 1000 就是相当于 char a 1000; 1000过大会被截断高位ap-b 3000; // ap-b int b ...struct C *cp b; // C结构体指针指向结构体Bprintf(%d %d\n, cp-e, cp-f);printf(%d %c\n, cp-e, cp-f);cp-e 3000;cp-f e;cp-f 1000;// 整形指针指向结构体Aint *bp a;bp[0] 1000;bp[1] 2000;printf(A: %c %d\n, a.a, a.b);printf(A: %d %d\n, a.a, a.b);bp[2] 2000; // 可以修改内存但是堆栈溢出// 因为该空间没有被分配局部变量是保存在堆栈中的return 0;
}2 结构体占据空间问题 字节对齐
struct A {char a;int b;
};struct B {int c;int d;
};struct C {int e;char f;
};...
struct A a { a, 100 };
struct B b { 101, 300 };
struct C c { 200,c };// 根据字节对齐都占据8字节
printf(A: size %d %c %d\n, sizeof(a), a.a, a.b);
printf(B: size %d %d %d\n, sizeof(b), b.c, b.d);
printf(C: size %d %d %c\n, sizeof(c), c.e, c.f);
...运行以上程序我们可以直到三个结构体分别创建了一个变量并且每个结构体占据的空间大小都是8字节。 至于为什么都是8字节这是内存对齐问题不展开说明了我们看看这几个结构体被分配的空间情况吧。 每个结构体都占8字节的内存空间红色部分表示实际占用的空间蓝色部分表示空闲空间
注意这就意味着凡是被分配的8字节空间是可以任意访问的而空间外面是不允许访问的。
让结构体A的指针ap指向结构体B的变量b
现在我们建立一个结构体A的指针让其指向b。
struct A *ap b; // A结构体指针指向结构体B
printf(%d %d\n,ap-a, ap-b);
printf(%c %d\n, ap-a, ap-b);我们看看内存的情况再分析一下打印的结果。 上面是内存的分布情况现在
访问ap-a打印出来的是101e访问ap-b打印出来的是300
所以ap指针实际访问的应该是下面重点标出的部分
而这部分是不是很熟悉
所以ap指针尽管指向了结构体B但是实际还是按照结构体A的结构访问内存的。
2.1 使用char指针指向结构体B
刚才我们发现使用结构体A的指针可以直接访问结构体B那么如果是基本数据类型呢我们试一下。
char *chp b;
chp[1] b; // 这块区域其实是字节对齐导致的空闲空间
printf(%d %d\n, ap-a, ap-b);
printf(%c %d\n, ap-a, ap-b);我们看到内存分布如上图现在执行chp[1] bb的ASCII码是62
之后就变成了 哦这是令人惊讶的char类型的指针指向了一块内存区域然后使用下标修改了内存的值
还记得动态数组申请吗和内个是一样的原理
int *a (int *)malloc(sizeof(int) * 10);
a[0] 1; // 使用下标访问
a[1] 2;
...
free(a);告诉我们两件事
指针默认指向最开始的元素索引是0使用下标索引可以依次访问后面的元素每次向后移动的内存数取决于指针的数据类型
所以上面的事情不难理解。
然后我们继续执行程序
printf(%d %d\n, ap-a, ap-b);
printf(%c %d\n, ap-a, ap-b);尽管之前的空闲空间改变了但是结果依然不变也就是说我们之前的说法是正确的。 再进一步验证
/* 如何访问这块内存取决于ap指针能访问多大地方取决于内存区域本身 */
ap-a c; // ap-a c就是相当于 char a c;
ap-a 1000; // ap-a 1000 就是相当于 char a 1000; 1000过大会被截断高位
ap-b 3000; // ap-b int b ...结果显而易见对于ap-a 1000尽管1000已经超过了1字节大小但是最终只修改了第一个字节这就好比char a 1000一样a 0xe8 是的1000 0x3e8但是只有一个字节所以最高位的3被舍弃了。
2.2 用结构体C指针cp指向结构体B
struct C *cp b; // C结构体指针指向结构体B
printf(%d %d\n, cp-e, cp-f);
printf(%d %c\n, cp-e, cp-f);cp-e 3000;
cp-f e;
cp-f 1000;我们再试一试
最终结果显而易见。 2.3 用int指针指向结构体A
// 整形指针指向结构体A
int *bp a;
bp[0] 1000;
bp[1] 2000;
printf(A: %c %d\n, a.a, a.b);
printf(A: %d %d\n, a.a, a.b);
bp[2] 2000; // 可以修改内存但是堆栈溢出// 因为该空间没有被分配局部变量是保存在堆栈中的其实这个事情我们之前干过了之前用char现在用int再干一下。 这个事情进一步说明了什么呢
a提供了有限的8字节内存空间bp指针能够修改哪里取决于它指向的地址一次修改多大空间取决于它数据类型的大小指针不能修改未被分配的空间最后bp[2]访问了外界空间因此产生了
因为局部变量都是被分配在栈中的现在这个局部变量访问越界了产生了错误栈被破坏。
栈破坏这里情况非常复杂先粗浅理解为使用了未分配的空间导致了错误吧。
Linux0.11 内核中使用上述方法实现了GDT和IDT。
3 小结精华在这里
分析了这么多最终小结一下吧。
我们的眼中只有两件事
已分配的内存空间某数据类型的指针
现在我们就让指针指向内存空间的起始地址然后就可以操作这个内存空间了。
再增加一些限制
内存空间就这么大不能访问外面指针每次访问的地址是通过下标访问的一次只能移动数据类型大小的整数倍 这个时候你眼中的C语言分配一块内存再创建一个指针打遍天下无敌手
当然了除了特殊情况一般没人这么干你会疯掉看你代码的人也会疯掉
4 补充直接深入底层看汇编代码
之前我们的分析是基于C语言层级的比较抽象实际上编译完成之后的汇编语言一看就明白了。
你可以看到ap-a直接访问的是byte而ap-b访问的是dword一个是字节一个是双字大小自然清晰。
这也是编译器的功能把C语言提供的方便人类使用的大量抽象给翻译成方便机器使用的少量指令的复杂排列组合。
5 什么叫打遍天下无敌手呢
其实就是瞎玩儿吧……但是的确可以这么干的我们试一试。
int main() {char aaa[4] { 1,2,3,4 };char aaa2[4] { 1,2,3,4 };int *bbb aaa;printf(\n\n%x\n\n\n, bbb[0]);return 0;
}会打印什么呢显而易见的内存是01 02 03 04然后一个int *指针访问了它打印04030201。 我们可以使用bbb[0]或者*b都行因为b指向起始地址。
那能不能通过bbb[1]访问aaa2的内存呢
不行 因为aaa1和aaa2是两个数组变量他们在内存中的位置不是连续的是随机的如果你想达到内种效果那就是前面提到的结构体了把这两个放进一个结构体里面就是连续分配内存了就能使用bbb[1]了。 最后记住只有两件事
一块已分配的内存一个指针