我想创建一个获取void指针的函数。该指针指向任意用户数据。现在这不相关。描述此用户数据的描述符更为重要。在此数据下 。我想得到它的地址,有两种不同的解决方案:
struct data_desc {
size_t size;
data_type_t type;
/* And so on... */
};
/* Operate on the descriptor */
void operate_on_data(void *ptr)
{
struct data_desc *desc;
/* Now I want to get the desc /*
/* This is the first approach, simply fails */
desc = ((struct data_desc *)ptr) - sizeof(struct data_desc);
/* This is the second, it works...*/
desc = (struct data_desc *)ptr;
desc--;
/* Do something with desc */
}
如您所见,我使用两种不同的方法来获取描述符的地址。我认为第一个更自然,但它不起作用。我使用了更多的括号来避免优先问题。
我知道这些方法不安全。 第一个不起作用,为什么?这种行为背后的原因是什么?
提前致谢!
答案 0 :(得分:2)
问题是指针算术。当你拿一个指针并减去1时,你实际上是在减去(1*sizeof(struct))
。第一个带有“-sizeof”的等式,你实际上是从指针中减去(sizeof(struct) * sizeof(struct))
个字节。有意义吗?
答案 1 :(得分:1)
当您将data_desc
定义为struct data_desc
的指针时,编译器知道在将指针递增或递减1时添加或减去sizeof(struct data_desc)
。换句话说,它将“一”翻译成“一个结构”。
编写这样的代码很常见:
struct data_desc *desc = (struct data_desc *) ptr;
然后你可以像这样操纵它:
size_t sz0 = desc->size; // size of 0th element
size_t sz1 = desc[1].size; // size of 1st element
size_t sz2 = (desc + 2)->size; // size of 2nd element (slightly awkward)
desc++; // Increment to next structure
返回原始代码:可以编写desc = ((struct data_desc *)ptr) - 1;
,但大多数程序员更愿意初始化desc
然后直接使用它。
答案 2 :(得分:1)
desc = ((struct data_desc *)ptr) - sizeof(struct data_desc);
您将ptr
强制转换为struct data_desc *
。现在,编译器对此指针执行的所有算法都是以它指向的类型的大小为块。
因此,如果您减去sizeof(struct data_desc)
(假设struct data_desc
的大小为8个字节),ptr
将指向一个可以在其自身之间插入8 struct data_desc
的位置和ptr
。
假设下面的每个loc
都可以容纳一个struct data_desc
-------------------------------------------------------------
| | | | | | | | | | |
| loc | loc | loc | loc | loc | loc | loc | loc | loc | ptr |
| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | |
-------------------------------------------------------------
^ ^
| |
desc ptr
location location
after
subtraction