C 试图理解指针加减法,但是

C 试图理解指针加减法,但是,c,pointers,long-integer,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; } 我知道错误是什么,但我不知道为什么会发

我不知道如何描述我的问题,但问题是:在阅读K&R的书时,我试图更好地理解指针。我使用了他们版本的strlen(),它使用指针,效果很好。为了进一步试验指针算法,我编写了一个非常简单的代码:

#include <stdio.h>

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

    sp = s;

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

我知道错误是什么,但我不知道为什么会发生这种情况。该表达式是如何变成长型的?

如果警告出现在
返回中,则在:

int istrlen(char *s)
{
  ...
  return p - s;
}
那么你的编译器是有帮助的。。。在这种情况下,如果您请求
-Wconversion
,gcc将生成警告,因为

定义了求和(C99 6.5.6),其中两个都是指针(指向同一数组对象的元素,或指向同一数组对象最后一个元素的过去一个),以给出类型为
ptrdiff\u t
的结果,这是一个有符号整数。现在,在典型的64位处理器上,
ptrdiff\u t
将是一个64位有符号整数,
int
是一个32位有符号整数。因此,编译器可能会警告您,从64位到32位有符号整数的转换可能会出现溢出

你可以写:

  return (int)(p - s) ;
要对此负责,编译器将耸耸肩,继续它的生命

[我有点惊讶的是,这个警告没有直接的
-Wall
…所以这可能不是问题所指的警告。]

我的第二版K&R基本上是C89,还描述了
ptrdiff\u t
及其pal
size\u t
。我怀疑你所看到的代码片段来自一个更友善、更温和的时代,
int
ptrdiff\u t
是可互换的(32位整数)——或者编译器不太热衷于暗示程序员可能不知道他们在做什么。(请注意,在1989年,长度超过2147483647个字符的字符串极不可能出现!)


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

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

其中,减法的结果也是
ptrdiff\u t
,但是
%d
需要
int
。这是一个更严重的问题,因为
%d
只知道如何吃
int
,吃
int
可能会出错,特别是在后面有更多参数的情况下。

减去两个指针会产生类型
ptrdiff\u t
的结果,这是一个typedef(别名)对于某些实现定义的有符号整数类型

你有:

printf("%d\n", (sp+7) - s);
其中减法运算的操作数都是
char*
类型的指针(
s
是一个数组,它衰减为指向其第一个元素的指针)。
“%d”
格式需要类型为
int
的参数
ptrdiff_t
显然是系统上的
long
类型定义

要解决此问题,可以强制转换结果(可能是
long
,但
int
应该可以):

或者为
ptrdiff\u t
使用正确的格式字符串:

printf("%td\n", (sp+7) - s);
就我个人而言,我可能会使用演员阵容;我不记得
t
ptrdiff\u t
的长度修饰符,直到我在标准中查找了它。(而且C99之前的实现可能不支持
“%td”


传递的
printf
参数不是格式字符串指定的正确类型(升级后),会导致未定义的行为。至于它为什么会工作,可能
int
long
在实现中具有相同的大小和表示形式,或者如果
long
int
更宽,可能
long
参数的传递方式使得
printf
尝试获取
int
值(可能是堆栈外)它碰巧得到了
long
参数的低位
sizeof(int)
字节。这就是未定义行为的本质;可能发生的最糟糕的事情是它似乎工作正常。(这很糟糕,因为代码可能会在以后意外失败。)

不要使用屏幕截图;将文本复制并粘贴到程序中。问题中的代码不会产生警告;屏幕截图中的代码会产生警告。向我们展示产生警告的代码以及问题中的警告本身。指针是
长的
类型(例如32位)。但您告诉C将其打印为int(
%d
),这是一种16位数据类型。
0x12340001
0x98760001
理论上都会打印出
0x0001
,并且看起来完全相同,即使它们的值完全不同。@MarcB:不,指针不是“
长的
类型”.指针是指针。减去两个指针的结果是类型
ptrdiff\u t
,这是在
中定义的类型定义,对应于一些实现定义的带符号整数类型。
int
至少是16个but,但通常更宽。但是,由于产生错误的代码不在问题中,我建议我们选择回答时d关闭。屏幕截图中显示的警告出现在一段代码上,而该代码没有出现在您的问题中!!!抱歉,各位已经修复了它。该语句不会产生报告的警告,添加强制转换也没有帮助。链接的屏幕截图显示了实际代码及其产生的警告。问题中当前的代码是基本上不相关。这个问题需要更新。(最好更改
istrlen
,使其返回
size\u t
,而不是
int
)@KiethThompson…在我的gcc上,
-Wconversion
生成警告:“从'long int'转换为'int'可能会改变其值”。然而,我承认,我有点惊讶于
-Wall
-Wextra
是不够的!我宁愿假设是
返回s-p;
生成了警告。抱歉。在更成熟的思考中,并提供了进一步的信息
printf("%ld\n", (long)((sp+7) - s));
printf("%td\n", (sp+7) - s);