【问题标题】:When subtracting two pointers in C在C中减去两个指针时
【发布时间】:2017-02-20 10:36:09
【问题描述】:

为了完全理解这个概念,我正在玩指针,然后想减去两个指针,期望这两个地址之间的距离或其他东西,但显然我错了,所以这是我的代码。

int x = 5, y = 7;
int *p = &y;
int *q = &x;
printf("p is %d\nq is %d\np - q is %d", p, q, (p - q));

为什么程序输出p - q is 1?谢谢。

【问题讨论】:

  • 那么您期待什么以及为什么?
  • 如果 xy 彼此相邻存储,则它们将相隔一个 int 大小,因此在特定情况下差异为 1 ...。但是指针差异没有意义,除非你在谈论一个数组——即一个连续的元素块。

标签: c pointers


【解决方案1】:

公式used by pointer substraction为:

( p2 - p1 ) == ( addr( p2 ) - addr( p1 ) ) / sizeof( T )

Tboth p1p2 的类型。

int array[10];
int* p1 = array + 2;
int* p2 = array + 5;

int* a = p2 - p1; // == 3
int* b = p1 - p2; // == -3 

【讨论】:

    【解决方案2】:

    您的特殊情况是导致未定义行为的原因,因为 pq 指向不相关的对象。

    只有当pq 指向同一个数组/超过同一个数组的最后一个元素时,您才能理解p-q

    int array[10];
    int* p = &array[0];
    int* q = &array[5];
    
    ptrdiff_t diff1 = q - p;  // Valid. diff1 is 5
    ptrdiff_t diff2 = p - q;  // Valid. diff2 is -5
    

    q - p 在这种情况下为 5,因为它们指向数组中相隔 5 个元素的元素。

    换句话说,p+5 等于 q。如果从p 开始,遍历数组的5 个元素,你将指向q 指向的数组元素。


    顺便说一句,不要使用格式说明符%d 来打印指针。使用%p。将%td 用于ptrdiff_t

    printf(" p is %p\n q is %p\n p-q is :%td", p, q, p-q);`
    //            ^^        ^^
    

    请参阅http://en.cppreference.com/w/c/io/fprintf 了解不同类型的有效格式说明符。

    【讨论】:

    • 您应该使用类型ptrdiff_t 进行指针减法。至于打印指针值,参数不应该是(void*)p吗?
    • @WeatherVane 通常,当函数具有指针类型的参数时,可能会发生隐式转换,具体取决于编译器设置; C89/C90 标准(引入了void*)禁止隐式指针转换,但例如如果您指定 -Wno-incompatible-pointer-types,GCC 将允许它。
    • @WeatherVane 对于像printf() 这样的可变参数函数,不能在编译时进行这样的检查; printf() 的实现只是假设对应于%p 参数的参数是void* 类型。值得庆幸的是,在实践中,存储在指针p *x 和任何对应的另一种类型指针q *y = (q*)x 中的二进制数据是相同的,尽管我不知道这种行为是否在任何C 标准中定义。我知道的任何标准中定义的唯一相关行为是 y == x 必须评估为“true”。
    • @WeatherVane,我似乎也错过了标准中的以下句子:“指向其他类型的指针不需要具有相同的表示或对齐要求。”
    【解决方案3】:

    数组中两个指针的减法将给出两个元素之间的距离。

    让第一个元素的地址,即 1000,那么第二个元素的地址 a+1 将是 1004。因此 p1 = 1000p2 =1004

    p2-p1 = (1004- 1000) /size of int = (1004-1000)/4 =4/4 =1
    

    【讨论】:

      【解决方案4】:

      2 个指针的减法给出了 2 个变量之间的距离。 例如。

      //设a的地址为1000,则a+1的地址为1004

      int a[]={1,2,3};
      int *p1=a;
      int *p2=a+1;
      printf("%u",p2-p1);
      

      结果将是 1 而不是 4。 同样在你的情况下,x 和 y 的位置是连续的,这就是为什么 ans.是 1。

      【讨论】:

        【解决方案5】:

        指针算法就是这样工作的。它不会为您提供两个地址之间的差异。相反,它将显示两个变量之间的差异,就好像它们存储在数组中一样。因此,无论您的变量(相同类型)是 4 字节、8 字节还是 1 字节,如果存储在相邻的内存位置,它们的指针减法将始终导致 1 或 -1。

        【讨论】:

          【解决方案6】:

          这是未定义的行为。按标准(N1570):

          6.5.6 加法运算符
          ....
          9 当两个指针相减时,都应该指向同一个数组对象的元素, 或数组对象的最后一个元素;结果是 两个数组元素的下标

          请注意,如果允许,结果是 下标 差异。因此,如果指针指向相同类型的两个连续元素,则减法得到1,而不管类型的大小。 (这可能是您在具体案例中得到1 的原因。)

          【讨论】:

          • 实际上,如果 xy 在内存中碰巧相邻,则行为未定义并不是 100% 明确的。该标准承认这种可能性,尽管它明确定义了仅用于在超过一个对象末尾的指针和指向相邻对象的指针之间进行相等比较的行为。请参阅N1570 6.5.9 第 6-7 段。对于减法,您应该假设行为未定义。
          • 嗯,不能保证这些位置在内存中是相邻的,所以至少是实现定义的。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-12-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多