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*