结构体中指针成员的动态分配

示例1:

typedef struct _a{
    int type;
    char dat[1];
}A;

int main(void)
{
    char* ptr = "hello_world";    
    A *a = malloc(sizeof(A) + strlen(ptr) + 1);    
    memcpy(a->dat, ptr, strlen(ptr) + 1);
    printf("a->dat = %s\n", a->dat);
    free(a);

    return 0;
}

运行结果正常。
结构体A占据的内存空间是8字节(32Bit操作系统),int型变量和char型变量各占据4字节,strlen(ptr)等于11字节,所以malloc分配的空间是20字节,分配出来的空间是地址连续的堆空间。执行memcpy()时候,ptr字符串内容会覆盖结构体中dat数组变量的起始空间,而后续的空间又有(strlen(ptr) + 1)大小,所以运行正常。

示例2:
如果把结果体A中的数组变量dat改成指针变量*dat呢,

typedef struct _a{
    int type;
    char *dat;
}A;

int main(void)
{
    char* ptr = "hello_world";

    A *a = malloc(sizeof(A) + strlen(ptr) + 1);

    memcpy(a->dat, ptr, strlen(ptr) + 1);

    printf("a->dat = %s\n", a->dat);

    free(a);

    return 0;
}

显然,运行后发生Segmentation fault。这是因为malloc是在堆内存分配空间的,堆上的数据默认是0,也就是说指针变量默认是NULL。
在malloc之后打印结构体A的首地址以及type、dat的地址:

printf("a = %p, &a->type = %p, a->dat = %p\n", a, &a->type, a->dat);

运行:
这里写图片描述
memcpy()函数访问的目标地址是0地址处,所以自然段错误。
如何修改,显然需要将a->dat指针指向一个合适的地方去。指向a->type之后的地址?

int mian()
{
    char* ptr = "hello_world";

    A *a = malloc(sizeof(A) + strlen(ptr) + 1);

    printf("a = %p, &a->type = %p, a->dat = %p\n", a, &a->type, a->dat);

    a->dat = (char* )(&(a->type) + 1); //加1加的是步长,等价于加偏移4字节地址

    printf("a->dat = %p\n", a->dat);

    memcpy(a->dat, ptr, strlen(ptr) + 1);

    printf("a->dat = %s\n", a->dat);

    free(a);
}

运行结果还是段错误:
这里写图片描述
memcpy()操作的目的地址是0x8b4d00c,而结构体变量a的起始地址,也就是a结构体首个变量type的地址0x8b4d008,其中偏移4字节,跟前面示例1对比,看似并没什么问题,但是注意,示例1中a->type之后是一个数组变量,而在本例中,a->type之后是一个指针变量,它原指向NULL,经这么修改,它指向的是自己的地址了。指针变量也是变量,它的存在本身也需要地址存放,每个指针都需要占据空间给自己用,memcpy()函数的是dat指针给自己生存用的地址,因此出现段错误。应修改为:

a->dat = (char* )(a + 1);

也就是说a->dat指向A之外的内存大小为strlen(ptr) + 1空间的起始地址,这样就可以正常运行。

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值