二级指针 *(unsigned char**)(buf+0) = (unsigned char*)(buf+1)

RTT里面的代码

 1 rt_err_t rt_mp_init(struct rt_mempool *mp,
 2                     const char        *name,
 3                     void              *start,
 4                     rt_size_t          size,
 5                     rt_size_t          block_size)
 6 {
 7     rt_uint8_t *block_ptr;       /*这里<============*/
 8     register rt_base_t offset;
 9     /* parameter check */
10     RT_ASSERT(mp != RT_NULL);
11     /* initialize object */
12     rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name);
13     /* initialize memory pool */
14     mp->start_address = start;
15     mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
16     /* align the block size */
17     block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
18     mp->block_size = block_size;
19     /* align to align size byte */
20     mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *));
21     mp->block_free_count  = mp->block_total_count;
22     /* initialize suspended thread list */
23     rt_list_init(&(mp->suspend_thread));
24     mp->suspend_thread_count = 0;
25     /* initialize free block list */       /*这里<============*/
26     block_ptr = (rt_uint8_t *)mp->start_address;
27     for (offset = 0; offset < mp->block_total_count; offset ++)
28     {
29         *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) =
30             (rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)));
31     }
32     *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) =
33         RT_NULL;
34     mp->block_list = block_ptr;
35     return RT_EOK;
36 }
37 


29.30行 简化成如下:

unsigned char *buf;

*(unsigned char**)(buf+0) = (unsigned char*)(buf+1)

目的是,把 buf+1这个地址值,存到 buf+0 指向的空间。

1. 首先 buf+1 的步长是 1 * sizeof(unsigned char)

这是指针步长的定义。步长是根据指向的数据类型的长度而定的。

2. 直接写

*(buf+0) = (unsigned char*)(buf+1)

在VS2015里面会报错。

因为左边此时是 uchar 类型的,右边是 uchar*

虽然意思是对的。就是把 buf+1 这个地址值,存到 buf+0 指向的空间。

*(unsigned char**)(buf+0) = (unsigned char*)(buf+1)

无论加不加红色的部分,我们保存的目标地址,都是buf指针指向的空间,而不是去修改buf指针的值!

即不管你是几级指针,对指针取内容作为左边的表达式,就是去修改指针指向的空间,而不是指针的值。

所以在这里,这个二级指针,变得只是一个骗过编译器的把戏...(并不是说二级指针本身就是没用哈,二级指针的几个使用的模型,即用途可以网上搜搜)

那么上边的红色部分,仅仅是为了骗过编译器?

3. 不能写成

buf+0 = (unsigned char*)(buf+1)

这样只是修改了 buf+0 即 buf 的内容,修改的是 buf 指针的值。

当循环进行到

buf+1 = (unsigned char*)(buf+2)

时,就会产生越界访问,因为 buf+1 是未定义的内存区域。

我们可以对 buf 的值随意更改,

但是不能对 buf+1 的值进行更改,因为未定义这块内存,无权访问。

buf+1 = buf+2

这种写法,在VS2015中,报错:表达式必须是可以更改的左值。

关于第2点,

骗过编译器?

定义一个二级指针uint** p的话,*p的类型是uint*吗?

验证如下:

unsigned int arr[10] = {0,1,2,3,4,5,6,7,8,9};
unsigned int *buf;
unsigned int **p;

buf = arr;
p = &buf;

printf("%d\n", sizeof(char*));      //8     一个指针占的字节数
printf("%#lx\n", arr);              //0x40
printf("%#lx\n", &arr[0]);          //0x40
printf("%#lx\n", &arr[1]);          //0x44
printf("%#lx\n", &buf);             //0x38
printf("%#lx\n", p);                //0x38  p是buf的地址
printf("%#lx\n", buf);              //0x40
printf("%#lx\n", *p);               //0x40  *p是buf的值,不是buf的地址!buf的值,是a[0]的地址。
printf("%#lx\n", (*p)+1);           //0x44  (*p)+1是a[1]的地址

如果改成
unsigned char **p;
printf("%#lx\n", (*p)+1);           //0x41 不是0x44

在 p=&buf 时,
有的编译器会报warning,
VS2015直接报错。

也就是说,二级指针 char **p 对于 *p 确实会被认为是 char* 类型的。

注意,p = &buf 不能写成 *p = buf

除非二级指针p 已经初始化了。见后边例子的写法。

否则p只是一个空指针。

还有这个类似的程序代码:

https://github.com/ZhUyU1997/SOS/blob/master/mm/mem.c

int kmem_cache_line_object(void *head, unsigned int size, int order) {
    void **pl;
    char *p;
    pl = (void **)head;
    p = (char *)head + size;
    int i, s = PAGE_SIZE * (1 << order);

    for (i = 0; s > size; i++, s -= size) {
        *pl = (void *)p; //保存下个内存块首地址于当前内存块
        pl = (void **)p;
        p = p + size;
    }
    if (s == size)
        i++;
    return i;
}

不管是几级指针,第一个*前边的数据类型,就是告诉编译器,怎么从最终地址读数据。

因此可以推导出,char**取一次内容之后,变成char*

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值