今天在微博上看到一段小程序,博主问会不会core,如果core的话,会是在哪一行。程序代码如下。
#include <stdio.h> struct str { int len; char s[0]; }; struct foo { struct str *a; }; int main() { struct foo f = {0}; if(f.a->s) { printf(f.a->s); } return 0; }
段错误 (核心已转储)
int printf(const char *format, ...);
为什么f.a->s这个值会是0x04呢? 首先看看 f.a 的值, f = {0} 就是给a赋值的,所以 f.a = 0x00。
再看看 str 这个结构体的定义,s 是一个数组,而数组名是数组的开头地址,那么就是说f.a->s是一个地址,而这个地址则刚好是s在str中的偏移量。
len这个变量是int,所以s的地址是在a的地址(0x00)的基础上加上4,所以f.a->s = 0x04。
通过gdb的调试。f.a->s是可以看到输出的,但是f.a->len确实非法的。
(gdb) p f.a->s $1 = 0x4 <Address 0x4 out of bounds> (gdb) p f.a->len Cannot access memory at address 0x0 (gdb)
如果把str的结构改成如下
struct str { int len; char *s; };
对 f.a->s 的访问也是不合法的。
所以,我感觉要点就是:
数组名是数组的开头地址。
=====================================================
这个跟offsetof宏有点相似。
#define offsetof(s, m) (size_t)&(((s *)0)->m)
1. 将0强制转换成该结构体的指针: (s *)0;
2. 取得该成员变量:((s *) 0)->m
3. 取地址并转换成size_t类型:(size_t) &(((s *) 0)->m)
写到这里突然又有一个问题没想明白了:为什么这个宏中对0x00的解引用((s *) 0)->m是合法的?而在f.a->len中,a的值也为0,那么为什么却又不合法呢?
坐等coolshell的解释。。