我试图通过减法找到两个指针的差异,但一个是int *
,另一个是char *
。结果,正如我所料,它给了我一个错误,因为指针类型不兼容。
int main() {
char * ca="test";
int *ia=malloc(12);
*ia=45;
printf("add char * =%p, add int = %p \n", ca, ia);
printf("add ca-va * =%p\n", ca-ia);
return(0);
}
test3.c:22:35:错误:二进制操作数无效 - (有'char *' 和'int *')
但是,当我将int*
输入size_t
时,我成功地能够减去地址。有人可以解释size_t
在这里做了什么吗?
int main() {
char * ca="test";
int *ia=malloc(12);
*ia=45;
printf("add char * =%p, add int = %p \n", ca, ia);
printf("add ca-va * =%p\n", (ca-(size_t)ia));
return(0);
}
答案 0 :(得分:3)
这里有2个问题:
两个指针值之间的差异以指针指向的数据类型为单位计算。如果您有两种不同的数据类型,则无法使用此功能。
指针算术只允许在同一个数据对象中。您可能只减去指向同一个数组或一个动态分配内存块的指针。 在您的代码中不是这种情况。
减去与这些标准不匹配的指针无论如何都没有多大意义。
编译是抱怨的权利。
答案 1 :(得分:1)
这只是指针算法。
对于某些指针ptr
和整数offset
,ptr - offset
表示 offset
之前的地址ptr
元素。请注意,这是元素(无论指针指向什么),而不是字节。你也可以在这里使用添加。 ptr[i]
是*(ptr + i)
的缩写。
对于相同类型的两个指针(例如char*
),ptr1 - ptr2
表示 2个指针之间的元素数。例如如果ptr1 - ptr2 == 5
,则ptr1 + 5 == ptr2
。
对于不同类型的两个指针(例如char*
和int*
)ptr1 - ptr2
没有任何意义。
在您的第一段代码中发生错误是因为您尝试减去不同类型的指针。第二段代码有效,因为您的演员表使其使用ptr - offset
版本。但这肯定不是你真正想要的,因为指针被转换为偏移量,结果是一个指针。
你可能想要的是Paul Hankin在评论中提到的内容:
intptr_t pc = (intptr_t)ca;
intptr_t pa = (intptr_t)ia;
printf("add ca-va = %" PRIdPTR "\n", pc - pa);
这会将指针转换为能够保存地址的整数类型,然后进行减法。您需要#include <inttypes.h>
才能获得PRIdPTR
(inttypes.h
内部包含提供stdint.h
的{{1}}。
答案 2 :(得分:1)
size_t
是整数类型。当指针转换为整数类型时,结果是实现定义的(如果它可以适合目标类型;否则行为不是由C标准定义的。)
根据C标准中的非规范性说明,“用于将指针转换为整数或整数到指针的映射函数旨在与执行环境的寻址结构一致。”在简单的机器上在存储器地址方案中,将指针转换为整数的结果通常是存储器地址。本答案的其余部分将假设我们有这样的C实现。
因此,如果ca
指向地址9678处的char
数组,并且ia
指向4444处的某个已分配内存,则将ia
转换为{{0}} {1}}将是4444.然后,当从size_t
中减去4444时,我们不是减去两个指针而是从指针中减去一个整数。通常,C标准没有定义它的行为,因为只允许在一个数组的边界内向指针添加和减去整数,在这个例子中,4444远远超出ca
。但是,编译器可能做的只是将整数转换为指向元素的大小,然后从地址中减去结果。由于ca
指向ca
,并且char
的大小为一个字节,因此将4444转换为4444 char
元素的大小仅为4444字节。然后9678-4444是5234,因此结果是一个指向地址5234的指针。
当您需要将指针转换为整数时,有一个更好的类型,char
,在uintptr_t
标头中定义。 (注释已指出<stdint.h>
,但除非有特定原因要使用签名版本,否则应使用未签名版本。)然后,如果将两个指针都转换为intptr_t
,则与{{1}一样你将避免第一个指针可能指向某个大小不是一个字节的类型的问题。然后,具有平坦内存地址空间的机器上的结果通常是两个地址之间的差异。
由于此处涉及实现定义和未定义的行为,因此这不是您可以依赖的,并且您不应该在普通代码中以这种方式操作指针。