C 字符串中的 %d 个指针到整数的转换?

%d pointer-to-integer conversion in C strings?

通过查看此代码示例:

/* strrchr example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] = "This is a sample string";
  char * pch;
  pch=strrchr(str,'s');
  printf ("Last occurence of 's' found at %d \n",pch-str+1);
  printf("pch number: %d \n str number: %d", pch, str);
  return 0;
}

输出如下:

Last occurence of 's' found at 18 
pch number: -8575 
str number: -8592

我不明白,pchstr 有什么数字,在 printf() 中的 %d 下似乎有什么转换。这是否类似于 存储单元的编号 ,指向第一个字符的指针所在的位置(在 str 的情况下)?如果是这样,为什么 strpch 大?它应该代表指向第一个字符串字符的指针,不是吗?最后,为什么我们在 pch-str+1 中需要 +1

加法:这不是我的代码(第二个printf()除外)。我在 cplusplus.com 上找到它,寻找 strrchr() 参考。

您的代码导致未定义的行为。 printf 函数不是 "magic":它不知道您传入的参数类型是什么。您有责任提供正确的格式说明符,然后 printf 函数将知道在内存中查找的位置以及读取多少字节以获得该格式的值。

如果你不匹配,那么任何事情都可能发生,函数读取错误的区域,你得到的垃圾可能与你传入的值完全无关(或者其他任何事情也可能发生)。

在您的代码中,匹配格式说明符为:"%td" 对应 pch - str + 1"%s" 对应 pch"%s" 对应 str.虽然我不清楚你希望在第二行打印出什么。

strpch 不单独存储值;它们指向内存中的某处,因此它们的值实际上是它们指向的内存中的地址。

In computer science, a pointer is a programming language object, whose value refers to (or "points to") another value stored elsewhere in the computer memory using its address. A pointer references a location in memory, and obtaining the value stored at that location is known as dereferencing the pointer. As an analogy, a page number in a book's index could be considered a pointer to the corresponding page; dereferencing such a pointer would be done by flipping to the page with the given page number.

Wikipedia

结果绝对自然:要测试这个,你必须了解 2 的补码是如何工作的:让我们以维基百科提供的这个例子为例:

Binary Unsigned 2's complement
011     3       3 
010     2       2 
001     1       1 
000     0       0 
111     7       -1 
110     6       -2 
101     5       -3 
100     4       -4 

我想通过这个向您展示的是,最大的无符号二进制值映射到 2 的补码中的最大负值。

要解决此问题,请按如下方式更改您的程序:

/* strrchr example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] = "This is a sample string";
  char * pch;
  pch=strrchr(str,'s');
  printf ("Last occurence of 's' found at %ld \n",pch-str+1);
  printf("pch number: %ld \n str number: %ld", pch, str);
  return 0;
}

您唯一需要做的更改是将 %d 更改为 %ld,因此,程序将打印 long int 值而不是 int 值。

当我测试这段代码时,我得到:

Last occurence of 's' found at 18 
pch number: 140735717807905 
str number: 140735717807888

然而,最典型和正确的打印指针的方式是使用%p。

/* strrchr example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] = "This is a sample string";
  char * pch;
  pch=strrchr(str,'s');
  printf ("Last occurence of 's' found at %p \n",pch-str+1);
  printf("pch number: %p \n str number: %p", pch, str);
  return 0;
}

打印:

Last occurence of 's' found at 0x12 
pch number: 0x7fffdcf2b3f1 
str number: 0x7fffdcf2b3e0

回答你的最后一个问题:

And, finally, why do we need +1 in pch-str+1?

让我们看一个例子:

strings
1-----7 

所以我们假设 pch 等于 7,str 等于 1。pch - str 结果会产生 6,因此我们需要增加该值以显示最后一个 [=23] 的位置=].

pch 和 str 是指针。它们包含您的字符串在内存中的地址和最后一个 s 字符。 (这就是为什么它们相差 18)

您没有正确打印它们,所以您没有看到完整的地址。执行 %p 而不是 %d(告诉编译器这些是指针)

pch - str + 1 有效,因为您可以添加和减去地址。它们只是非常大的数字,但是当您减去它们时,您可以得到很小的数字 (100001 - 100000 = 1)。编译器知道它们是指针,所以 'does the right thing'。为什么没有printf 'do the right thing'?,因为printf的工作方式是在程序运行时使用%d,编译器不知道怎么回事。实际上那是个谎言,大多数现代编译器都知道 printf 的行为,并且会查看格式字符串以查看其是否正常。如果您正确设置编译器选项,它会警告您对指针执行 %d

pch是字符串中最后一个s的地址。 str是字符串开头的地址。 pch-strstrpch之间的字节数,也就是s在字符串中的位置。您添加 1 是因为您希望结果是基于 1 的,就像普通人认为的那样,而不是基于 0 的计算机思考方式(所以如果 s 是第一个字符,它会说 found at 1,不是 found at 0).

正如其他人所说,当您打印 pchstr 时得到奇怪值的原因是因为您使用了错误的 printf 格式运算符。您应该使用 %p 来打印指针。

"Is this something like the number of the memory cell, where the pointer to the first character lies (in case of str)? If so, why str is bigger than pch?" --> 就像问为什么你的家庭地址比你朋友的家庭地址大,即使你们住在同一条街上。有简单的地址。

pch - str + 1 中的 +1 可能是因为作者喜欢从 1 开始计算字符串中的位置。从 0 开始在 C 中更为惯用,因此:pch - str.

使用匹配modifier/specifier.

char str[] = "This is a sample string";
char * pch;
pch = strrchr(str,'s');

// pointer subtraction results in type `ptrdiff_t`.  Use `t`
printf("Last occurrence of 's' found at %td \n", pch - str);

// Use p and add cast to `void *`
printf("pch number: %p \n str number: %p", (void *) pch, (void *) str);