自己根据《C陷阱与缺陷》中的一个例子写了这样一个问题问自己大致如下:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
char c;
for(i = 0; i < 5; i++){
*(int*)(&c) = ______; //any expressions are ok
printf("%d\t", i);
}
return 0;
}
要求输出0 1 2 3 4,才疏学浅的我仅仅在CodeBlocks的debug下运行成功就认为题目没什么问题了。这个问题本意就是对char型变量的赋值从而修改int型的内存值,因为我读死书认为i和c先后在栈上分配空间,那么c地址与i地址相邻,故一切顺利成章。我沾沾自喜地找到了很多答案,i<<8是其中一个。
偶然的机会Coding狂人(编程达人)看到了我的这个问题,一语道破答案是任意合理C语句表达式。开始感觉自己分析的没错,但当我在release下和vc6.0下运行时,结果任意值都能完成输出。经过Coding狂人的指导我明白了编译器的字节对齐,明白了i会在寄存器中自加,c值会被编译器忽略掉,这些是我读死书中不曾学习到的,或者太不认真。最后经过自己的学习将题目修改如下:
#include <stdio.h>
#pragma pack (1)
typedef struct _num
{
char c;
int i;
} num;
int main(int argc, char *argv[])
{
num k = {0,0};
int i;
for(i = 0; i < sizeof(k); i++){
*(int*)&k.c = i*0x100; //i << 8
printf("%d\t", k.i);
}
return 0;
}
这个问题的答案就被局限了,
用预编译指令指定对齐了宽度,结构体中char型在低字节且只占一个字节,int型地址相对于结构体地址在第二个字节且占四个字节,因此需要把赋值的第二个字节限定为i,高字节限定为0即可。学习之路漫长,自己需要学习的还要很多,给自己加油!
另:在《C语言进阶》书中学到了Linux内核中list.h的一个宏记录如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
该宏返回TYPE结构体的MEMBER字段的偏移值,巧妙的用0地址实现了这一效果,其他值也可不过最后需要剪掉该值,不方便。