试图理解指针的加法和减法但是

时间:2014-08-12 16:47:46

标签: c pointers long-integer

我不知道如何描述我的问题,但事情就是这样:在阅读K& R的书时,我试图更好地理解指针。我使用了他们使用指针的strlen()版本,并且运行正常。为了进一步尝试指针运算,我写了一个非常简单的代码:

#include <stdio.h>

int main(void)
{
    char s[10];
    char *sp;

    sp = s;

    printf("%d\n", (sp+7) - s);
    return 0;
}

它运行正常,但我不明白发生此警告的原因:

hello3.c:10:17: warning: format specifies type 'int' but the argument has type 
      'long' [-Wformat]
        printf("%d\n", (sp+7) - s);
                ~~     ^~~~~~~~~~
                %ld

我明白错误是什么,但我不知道为什么会这样。那个表达式怎么变成了长型?

2 个答案:

答案 0 :(得分:0)

如果警告在return上,请输入:

int istrlen(char *s)
{
  ...
  return p - s;
}

然后你的编译器很有用......在这种情况下,如果你要求-Wconversion,gcc会生成一个警告,因为......

...定义了总和p - s(C99 6.5.6),其中两者都是指针(对同一个数组对象的元素,或者是同一个数组对象的最后一个元素之后的一个),到给出ptrdiff_t类型的结果 - 这是一个有符号整数。现在,在典型的64位处理器上,ptrdiff_t将是一个64位有符号整数,而int是一个32位有符号整数。因此,您的编译器可能会警告您从64位到32位有符号整数的转换,可能会有溢出。

你可以写:

  return (int)(p - s) ;

对此负责,编译器会耸耸肩,继续它的生命。

[我有点惊讶的是,警告没有直截了当-Wall ......所以这可能不是问题所指的警告。]

我的第二版K&amp; R基本上是C89,还描述了ptrdiff_t及其朋友size_t。我怀疑你正在查看的代码片段来自一个更温和,更温和的年龄intptrdiff_t可以互换(32位整数) - 或者编译器不太热衷于建议程序员可能不知道他们在做什么。 (请注意,在1989年,一个超过2,147,483,647个字符的字符串非常不可能!)


编译器可能有用的另一个地方是:

  printf("%d\n", (sp+7) - s);

同样,减法的结果为ptrdiff_t,但%d需要int。这是一个更严重的问题,因为%d只知道如何吃int,并且可能会错误地吃int,特别是在跟随它的地方有更多参数的情况下。

答案 1 :(得分:0)

减去两个指针会产生类型ptrdiff_t的结果,这是某个实现定义的有符号整数类型的typedef(别名)。

你有:

printf("%d\n", (sp+7) - s);

其中减法的操作数都是char*类型的指针(s是一个衰减到指向其第一个元素的指针的数组)。 "%d"格式需要int类型的参数。 ptrdiff_t显然是您系统上long的typedef。

要解决此问题,请将结果(可能转到long,但int应该有效):

printf("%ld\n", (long)((sp+7) - s));

或使用ptrdiff_t的正确格式字符串:

printf("%td\n", (sp+7) - s);

就个人而言,我可能会使用演员;我不记得tptrdiff_t的长度修饰符,直到我在标准中查找它。 (并且C99之前的实现可能不支持"%td"。)

传递printf格式字符串指定的不正确类型(提升后)的参数会导致未定义的行为。至于为什么它恰好起作用,或许intlong碰巧在您的实现中具有相同的大小和表示,或者long是否宽于int或许{{以这样的方式传递参数:当long尝试获取printf值(可能在堆栈外)时,它恰好获得{的低位int字节{1}}论点。这是未定义行为的本质;可能发生的最差事情似乎是正常工作。 (这很糟糕,因为代码可能会在以后无法预测地失败。)