C语言 如何比较C指针?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17584416/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to compare C pointers?
提问by jiych.guru
Recently, I wrote some code to compare pointers like this:
最近,我写了一些代码来比较这样的指针:
if(p1+len < p2)
however, some staff said that I should write like this:
但是,有些工作人员说我应该这样写:
if(p2-p1 > len)
to be safe.
Here,p1and p2are char *pointers,lenis an integer.
I have no idea about that.Is that right?
为了安全。这里,p1和p2是char *指针,len是一个整数。我不知道,是吗?
EDIT1: of course,p1and p2pointer to the same memory object at begging.
EDIT1:当然,p1和p2在乞讨时指向同一个内存对象。
EDIT2:just one min ago,I found the bogoof this question in my code(about 3K lines),because lenis so big that p1+lencan't store in 4 bytes of pointer,so p1+len < p2is true.But it shouldn't in fact,so I think we should compare pointers like this in some situation:
EDIT2:就在一分钟前,我在我的代码(大约 3K 行)中发现了这个问题的bogo,因为len它太大了,p1+len不能存储在 4 个字节的指针中,所以p1+len < p2是真的。但它应该实际上不是,所以我认为我们应该在某些情况下比较这样的指针:
if(p2 < p1 || (uint32_t)p2-p1 > (uint32_t)len)
回答by ruakh
In general, you can only safely compare pointers if they're both pointing to parts of the same memory object (or one position past the end of the object). When p1, p1 + len, and p2all conform to this rule, both of your if-tests are equivalent, so you needn't worry. On the other hand, if only p1and p2are known to conform to this rule, and p1 + lenmight be too far past the end, only if(p2-p1 > len)is safe. (But I can't imagine that's the case for you. I assume that p1points to the beginning of some memory-block, and p1 + lenpoints to the position after the end of it, right?)
一般而言,您只能安全地比较指针,如果它们都指向同一内存对象的一部分(或对象末尾的一个位置)。当p1、p1 + len和p2都符合此规则时,您的两个if-test 是等效的,因此您不必担心。另一方面,如果仅p1和p2已知符合此规则,并且p1 + len可能已经过时太远,则仅if(p2-p1 > len)是安全的。(但我无法想象你是这种情况。我假设它p1指向某个内存块的开头,并p1 + len指向它结尾之后的位置,对吧?)
What they may have been thinking of is integer arithmetic: if it's possible that i1 + i2will overflow, but you know that i3 - i1will not, then i1 + i2 < i3could either wrap around (if they're unsigned integers) or trigger undefined behavior (if they're signed integers) or both (if your system happens to perform wraparound for signed-integer overflow), whereas i3 - i1 > i2will not have that problem.
他们可能一直在想的是整数算术:如果可能i1 + i2会溢出,但您知道不会溢出,i3 - i1那么i1 + i2 < i3可能会环绕(如果它们是无符号整数)或触发未定义的行为(如果它们是有符号整数)或两者(如果您的系统碰巧对有符号整数溢出执行环绕),而i3 - i1 > i2不会出现该问题。
Edited to add:In a comment, you write "lenis a value from buff, so it may be anything". In that case, they are quite right, and p2 - p1 > lenis safer, since p1 + lenmay not be valid.
编辑添加:在评论中,您写“len是来自 buff 的值,所以它可能是任何东西”。在那种情况下,它们是完全正确的,并且p2 - p1 > len更安全,因为它们p1 + len可能无效。
回答by Dietrich Epp
"Undefined behavior" applies here. You cannot compare two pointers unless they both point to the same object or to the first element after the end of that object. Here is an example:
“未定义的行为”适用于此。您不能比较两个指针,除非它们都指向同一个对象或该对象末尾之后的第一个元素。下面是一个例子:
void func(int len)
{
char array[10];
char *p = &array[0], *q = &array[10];
if (p + len <= q)
puts("OK");
}
You might think about the function like this:
你可能会像这样考虑这个函数:
// if (p + len <= q)
// if (array + 0 + len <= array + 10)
// if (0 + len <= 10)
// if (len <= 10)
void func(int len)
{
if (len <= 10)
puts("OK");
}
However, the compiler knows that ptr <= qis true for all validvalues of ptr, so it might optimize the function to this:
然而,编译器知道ptr <= q是为所有真正有效的价值ptr,所以它可能优化功能如下:
void func(int len)
{
puts("OK");
}
Much faster! But not what you intended.
快多了!但不是你想要的。
Yes, there are compilers that exist in the wild that do this.
是的,有些编译器可以做到这一点。
Conclusion
结论
This is the only safe version: subtract the pointers and compare the result, don't compare the pointers.
这是唯一安全的版本:减去指针并比较结果,不要比较指针。
if (p - q <= 10)
回答by Jonathan Leffler
Technically, p1and p2must be pointers into the same array. If they are not in the same array, the behaviour is undefined.
从技术上讲,p1并且p2必须是指针到同一阵列。如果它们不在同一个数组中,则行为未定义。
For the addition version, the type of lencan be any integer type.
对于加法版本,的类型len可以是任何整数类型。
For the difference version, the result of the subtraction is ptrdiff_t, but any integer type will be converted appropriately.
对于差异版本,减法的结果是ptrdiff_t,但任何整数类型都会适当转换。
Within those constraints, you can write the code either way; neither is more correct. In part, it depends on what problem you're solving. If the question is 'are these two elements of the array more than lenelements apart', then subtraction is appropriate. If the question is 'is p2the same element as p1[len](aka p1 + len)', then the addition is appropriate.
在这些约束内,您可以以任何一种方式编写代码;两者都不正确。在某种程度上,这取决于您要解决的问题。如果问题是“数组的这两个元素是否比len元素相距更多”,那么减法是合适的。如果问题是“p2与p1[len](又名p1 + len)是相同的元素”,那么添加是合适的。
In practice, on many machines with a uniform address space, you can get away with subtracting pointers to disparate arrays, but you might get some funny effects. For example, if the pointers are pointers to some structure type, but not parts of the same array, then the difference between the pointers treated as byte addresses may not be a multiple of the structure size. This may lead to peculiar problems. If they're pointers into the same array, there won't be a problem like that — that's why the restriction is in place.
实际上,在许多具有统一地址空间的机器上,您可以避免减去指向不同数组的指针,但您可能会得到一些有趣的效果。例如,如果指针是指向某种结构类型的指针,但不是同一数组的一部分,那么作为字节地址处理的指针之间的差异可能不是结构大小的倍数。这可能会导致特殊的问题。如果它们是指向同一个数组的指针,就不会有这样的问题——这就是限制存在的原因。
回答by Chris Dodd
The existing answers show why if (p2-p1 > len)is better than if (p1+len < p2), but there's still a gotcha with it -- if p2happens to point BEFORE p1in the buffer and lenis an unsigned type (such as size_t), then p2-p1will be negative, but will be converted to a large unsigned value for comparison with the unsigned len, so the result will probably be true, which may not be what you want.
现有的答案显示了为什么if (p2-p1 > len)比 好if (p1+len < p2),但它仍然有一个问题——如果p2碰巧指向p1缓冲区中的BEFORE并且len是一个无符号类型(例如size_t),那么p2-p1将为负数,但会被转换为一个大的无符号值用于与无符号 len 进行比较,因此结果可能是正确的,这可能不是您想要的。
So you might actually need something like if (p1 <= p2 && p2 - p1 > len)for full safety.
因此,您实际上可能需要类似的东西if (p1 <= p2 && p2 - p1 > len)才能完全安全。
回答by Patrick
As Dietrich already said, comparing unrelated pointers is dangerous, and could be considered as undefined behavior.
正如 Dietrich 已经说过的,比较不相关的指针是危险的,可以被视为未定义的行为。
Given that two pointers are within the range 0 to 2GB (on a 32-bit Windows system), subtracting the 2 pointers will give you a value between -2^31 and +2^31. This is exactly the domain of a signed 32-bit integer. So in this case it does seem to make sense to subtract two pointers because the result will always be within the domain you would expect.
鉴于两个指针在 0 到 2GB 的范围内(在 32 位 Windows 系统上),减去这两个指针将得到一个介于 -2^31 和 +2^31 之间的值。这正是有符号 32 位整数的域。所以在这种情况下,减去两个指针似乎是有意义的,因为结果将始终在您期望的域内。
However, if the LargeAddressAware flag is enabled in your executable (this is Windows-specific, don't know about Unix), then your application will have an address space of 3GB (when run in 32-bit Windows with the /3G flag) or even 4GB (when run on a 64-bit Windows system). If you then start to subtract two pointers, the result could be outside the domain of a 32-bit integer, and your comparison will fail.
但是,如果在您的可执行文件中启用了 LargeAddressAware 标志(这是 Windows 特定的,不知道 Unix),那么您的应用程序将拥有 3GB 的地址空间(当在带有 /3G 标志的 32 位 Windows 中运行时)甚至 4GB(在 64 位 Windows 系统上运行时)。如果然后开始减去两个指针,结果可能超出 32 位整数的域,并且比较将失败。
I think this is one of the reasons why the address space was originally divided in 2 equal parts of 2GB, and the LargeAddressAware flag is still optional. However, my impression is that current software (your own software and the DLL's you're using) seem to be quite safe (nobody subtracts pointers anymore, isn't it?) and my own application has the LargeAddressAware flag turned on by default.
我想这就是地址空间最初被分成2等份的2GB的原因之一,而LargeAddressAware标志仍然是可选的。但是,我的印象是当前的软件(您自己的软件和您正在使用的 DLL)似乎非常安全(没有人再减去指针,不是吗?)并且我自己的应用程序默认打开了 LargeAddressAware 标志。
回答by cmaster - reinstate monica
Neither variant is safe if an attacker controls your inputs
如果攻击者控制您的输入,这两种变体都不安全
The expression p1 + len < p2compiles down to something like p1 + sizeof(*p1)*len < p2, and the scaling with the size of the pointed-to type can overflow your pointer:
表达式p1 + len < p2编译成类似p1 + sizeof(*p1)*len < p2,并且使用指向类型的大小进行缩放可能会溢出您的指针:
int *p1 = (int*)0xc0ffeec0ffee0000;
int *p2 = (int*)0xc0ffeec0ffee0400;
int len = 0x4000000000000000;
if(p1 + len < p2) {
printf("pwnd!\n");
}
When lenis multiplied by the size of int, it overflows to 0so the condition is evaluated as if(p1 + 0 < p2). This is obviously true, and the following code is executed with a much too high length value.
当len乘以 的大小时int,它溢出到0因此条件被评估为if(p1 + 0 < p2)。这显然是正确的,并且以下代码执行时的长度值太大了。
Ok, so what about p2-p1 < len. Same thing, overflow kills you:
好的,那么p2-p1 < len. 同样的事情,溢出会杀死你:
char *p1 = (char*)0xa123456789012345;
char *p2 = (char*)0x0123456789012345;
int len = 1;
if(p2-p1 < len) {
printf("pwnd!\n");
}
In this case, the difference between the pointer is evaluated as p2-p1 = 0xa000000000000000, which is interpreted as a negativesignedvalue. As such, it compares smaller then len, and the following code is executed with a much too low lenvalue (or much too large pointer difference).
在这种情况下,指针之间的差异被评估为p2-p1 = 0xa000000000000000,这被解释为负符号值。因此,它比较小 then len,并且以太低的len值(或太大的指针差异)执行以下代码。
The only approach that I know is safe in the presence of attacker-controlled values, is to use unsigned arithmetic:
我知道在存在攻击者控制的值时安全的唯一方法是使用无符号算术:
if(p1 < p2 &&
((uintptr_t)p2 - (uintptr_t)p1)/sizeof(*p1) < (uintptr_t)len
) {
printf("safe\n");
}
The p1 < p2guarantees that p2 - p1cannot yield a genuinely negative value. The second clause performs the actions of p2 - p1 < lenwhile forcing use of unsigned arithmetic in a non-UB way. I.e. (uintptr_t)p2 - (uintptr_t)p1gives exactly the count of bytes between the bigger p2and the smaller p1, no matter the values involved.
不能产生真正负值的p1 < p2保证p2 - p1。第二个子句执行p2 - p1 < len以非 UB 方式强制使用无符号算术的操作。即,无论涉及的值如何,都(uintptr_t)p2 - (uintptr_t)p1准确地给出了较大p2和较小之间的字节数p1。
Of course, you don't want to see such comparisons in your code unless you know that you need to defend against determined attackers. Unfortunately, it's the only way to be safe, and if you rely on either form given in the question, you open yourself up to attacks.
当然,除非您知道需要防御坚定的攻击者,否则您不希望在代码中看到此类比较。不幸的是,这是确保安全的唯一方法,如果您依赖问题中给出的任何一种形式,您就会面临攻击。

