指针比较在C中如何工作?比较不指向同一数组的指针是否可以? [英] How does pointer comparison work in C? Is it ok to compare pointers that don't point to the same array?

查看:80
本文介绍了指针比较在C中如何工作?比较不指向同一数组的指针是否可以?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在K& R(C编程语言第二版)第5章中,我阅读了以下内容:

首先,可以在某些情况下比较指针. 如果pq指向同一数组的成员,则==!=<>=等关系将正常工作.

这似乎暗示只能将指向同一数组的指针进行比较.

但是,当我尝试此代码时

    char t = 't';
    char *pt = &t;
    char x = 'x';
    char *px = &x;

    printf("%d\n", pt > px);

1被打印到屏幕上.

首先,我认为我会得到未定义或某种类型或错误的信息,因为ptpx没有指向相同的数组(至少在我看来).

pt > px也是,因为两个指针都指向存储在堆栈中的变量,并且堆栈变小,所以t的内存地址大于x的内存地址?这就是为什么pt > px是真的吗?

当引入malloc时,我会更加困惑.同样在8.7章的K& R中,写了以下内容:

仍然有一个假设,就是可以有意义地比较指向sbrk返回的不同块的指针.该标准不能保证这一点,该标准只允许在数组内进行指针比较.因此,此版本的malloc仅可在对通用指针进行比较有意义的机器之间移植.

将指向堆上分配的空间的指针与指向堆栈变量的指针进行比较,我没有问题.

例如,以下代码可以很好地工作,并且可以打印1:

    char t = 't';
    char *pt = &t;
    char *px = malloc(10);
    strcpy(px, pt);
    printf("%d\n", pt > px);

基于对编译器的实验,我被认为可以将任何指针与任何其他指针进行比较,无论它们分别指向何处.而且,我认为两个指针之间的指针算术都很好,无论它们分别指向何处,因为该算术只是使用指针存储的内存地址.

不过,我对在K& R中阅读的内容感到困惑.

我问的原因是因为我的教授.实际上使它成为一个考试问题.他给出了以下代码:

struct A {
    char *p0;
    char *p1;
};

int main(int argc, char **argv) {
    char a = 0;
    char *b = "W";
    char c[] = [ 'L', 'O', 'L', 0 ];

   struct A p[3];
    p[0].p0 = &a;
    p[1].p0 = b;
    p[2].p0 = c;

   for(int i = 0; i < 3; i++) {
        p[i].p1 = malloc(10);
        strcpy(p[i].p1, p[i].p0);
    }
}

这些评估结果是什么

  1. p[0].p0 < p[0].p1
  2. p[1].p0 < p[1].p1
  3. p[2].p0 < p[2].p1

答案是010.

(我的教授确实在考试中声明了有关问题是针对Ubuntu Linux 16.04、64位版本编程环境的免责声明)

(编辑者的注:如果SO允许使用更多标签,则最后一部分将保证,甚至可能是的问题.问题/类的重点是底层操作系统的实现细节,而不是可移植的C.)

解决方案

根据 C11标准,关系运算符<<=>>=只能用于指向相同数组或struct对象的元素的指针.这在6.5.8p5节中有详细说明:

比较两个指针时,结果取决于 指向对象地址空间中的相对位置. 如果两个指向对象类型的指针都指向同一个对象,或者 都指向同一数组的最后一个元素 对象,它们比较相等.如果指向的对象是 同一聚合对象的成员,指向结构的指针 稍后声明的成员的比较大于指向的指针 在结构中较早声明的成员,以及指向的指针 下标值较大的数组元素比较大于 指向具有较低下标值的同一数组元素的指针. 指向同一个联合对象的成员的所有指针进行比较 平等的.如果表达式P指向数组的元素 对象和表达式Q指向对象的最后一个元素 相同的数组对象,指针表达式Q + 1比较大于P. 在所有其他情况下,行为都是不确定的.

请注意,任何不满足此要求的比较都会调用未定义的行为,含义(在其中其他事情),您不能依赖于结果是否可重复.

在您的特殊情况下,对于两个局部变量的地址之间的比较以及局部地址和动态地址之间的比较,该操作似乎都是有效的",但是通过进行看似无关的更改,结果可能会更改您的代码,甚至使用不同的优化设置来编译相同的代码.对于未定义的行为,仅仅因为代码可能崩溃或生成错误并不意味着它.

例如,以8086实模式运行的x86处理器具有分段存储器模型,该模型使用16位段和16位偏移量来构建20位地址.因此,在这种情况下,地址不能完全转换为整数.

相等运算符==!=没有此限制.可以在指向兼容类型的任何两个指针或NULL指针之间使用它们.因此,在两个示例中都使用==!=会产生有效的C代码.

但是,即使使用==!=,您也可以获得一些意想不到的但仍然定义明确的结果.有关更多详细信息,请参见无关指针的相等性比较能否评估为true?.

关于您的教授提出的考试问题,它提出了许多错误的假设:

  • 存在一个平面内存模型,其中地址与整数值之间存在一对一的对应关系.
  • 转换后的指针值适合整数类型.
  • 该实现只在执行比较时简单地将指针视为整数,而不会利用未定义行为提供的自由度.
  • 使用堆栈并将局部变量存储在此处.
  • 该堆用于从中提取分配的内存.
  • 堆栈(因此是局部变量)出现在比堆(因此是分配的对象)更高的地址上.
  • 该字符串常量出现在比堆低的地址上.

如果您要在不满足这些假设的体系结构和/或编译器上运行此代码,则会得到截然不同的结果.

此外,这两个示例在调用strcpy时也表现出未定义的行为,因为正确的操作数(在某些情况下)指向单个字符而不是空终止的字符串,导致函数读取的值超出给定范围变量.

In K&R (The C Programming Language 2nd Edition) chapter 5 I read the following:

First, pointers may be compared under certain circumstances. If p and q point to members of the same array, then relations like ==, !=, <, >=, etc. work properly.

Which seems to imply that only pointers pointing to the same array can be compared.

However when I tried this code

    char t = 't';
    char *pt = &t;
    char x = 'x';
    char *px = &x;

    printf("%d\n", pt > px);

1 is printed to the screen.

First of all, I thought I would get undefined or some type or error, because pt and px aren't pointing to the same array (at least in my understanding).

Also is pt > px because both pointers are pointing to variables stored on the stack, and the stack grows down, so the memory address of t is greater than that of x? Which is why pt > px is true?

I get more confused when malloc is brought in. Also in K&R in chapter 8.7 the following is written:

There is still one assumption, however, that pointers to different blocks returned by sbrk can be meaningfully compared. This is not guaranteed by the standard which permits pointer comparisons only within an array. Thus this version of malloc is portable only among machines for which the general pointer comparison is meaningful.

I had no issue comparing pointers that pointed to space malloced on the heap to pointers that pointed to stack variables.

For example, the following code worked fine, with 1 being printed:

    char t = 't';
    char *pt = &t;
    char *px = malloc(10);
    strcpy(px, pt);
    printf("%d\n", pt > px);

Based on my experiments with my compiler, I'm being led to think that any pointer can be compared with any other pointer, regardless of where they individually point. Moreover, I think pointer arithmetic between two pointers is fine, no matter where they individually point because the arithmetic is just using the memory addresses the pointers store.

Still, I am confused by what I am reading in K&R.

The reason I'm asking is because my prof. actually made it an exam question. He gave the following code:

struct A {
    char *p0;
    char *p1;
};

int main(int argc, char **argv) {
    char a = 0;
    char *b = "W";
    char c[] = [ 'L', 'O', 'L', 0 ];

   struct A p[3];
    p[0].p0 = &a;
    p[1].p0 = b;
    p[2].p0 = c;

   for(int i = 0; i < 3; i++) {
        p[i].p1 = malloc(10);
        strcpy(p[i].p1, p[i].p0);
    }
}

What do these evaluate to:

  1. p[0].p0 < p[0].p1
  2. p[1].p0 < p[1].p1
  3. p[2].p0 < p[2].p1

The answer is 0, 1, and 0.

(My professor does include the disclaimer on the exam that the questions are for a Ubuntu Linux 16.04, 64-bit version programming environment)

(editor's note: if SO allowed more tags, that last part would warrant , , and maybe . If the point of the question / class was specifically low-level OS implementation details, rather than portable C.)

解决方案

According to the C11 standard, the relational operators <, <=, >, and >= may only be used on pointers to elements of the same array or struct object. This is spelled out in section 6.5.8p5:

When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. If two pointers to object types both point to the same object, or both point one past the last element of the same array object, they compare equal. If the objects pointed to are members of the same aggregate object,pointers to structure members declared later compare greater than pointers to members declared earlier in the structure, and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. All pointers to members of the same union object compare equal. If the expression P points to an element of an array object and the expression Q points to the last element of the same array object, the pointer expression Q+1 compares greater than P. In all other cases, the behavior is undefined.

Note that any comparisons that do not satisfy this requirement invoke undefined behavior, meaning (among other things) that you can't depend on the results to be repeatable.

In your particular case, for both the comparison between the addresses of two local variables and between the address of a local and a dynamic address, the operation appeared to "work", however the result could change by making a seemingly unrelated change to your code or even compiling the same code with different optimization settings. With undefined behavior, just because the code could crash or generate an error doesn't mean it will.

As an example, an x86 processor running in 8086 real mode has a segmented memory model using a 16-bit segment and a 16-bit offset to build a 20-bit address. So in this case an address doesn't convert exactly to an integer.

The equality operators == and != however do not have this restriction. They can be used between any two pointers to compatible types or NULL pointers. So using == or != in both of your examples would produce valid C code.

However, even with == and != you could get some unexpected yet still well-defined results. See Can an equality comparison of unrelated pointers evaluate to true? for more details on this.

Regarding the exam question given by your professor, it makes a number of flawed assumptions:

  • A flat memory model exists where there is a 1-to-1 correspondence between an address and an integer value.
  • That the converted pointer values fit inside an integer type.
  • That the implementation simply treats pointers as integers when performing comparisons without exploiting the freedom given by undefined behavior.
  • That a stack is used and that local variables are stored there.
  • That a heap is used to pull allocated memory from.
  • That the stack (and therefore local variables) appears at a higher address than the heap (and therefore allocated objects).
  • That string constants appear at a lower address then the heap.

If you were to run this code on an architecture and/or with a compiler that does not satisfy these assumptions then you could get very different results.

Also, both examples also exhibit undefined behavior when they call strcpy, since the right operand (in some cases) points to a single character and not a null terminated string, resulting in the function reading past the bounds of the given variable.

这篇关于指针比较在C中如何工作?比较不指向同一数组的指针是否可以?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆