为什么在C中的%ld中打印指针会产生这个结果?
char txt[] = "abcdefghij";
char *pointer = &txt[1];
printf("%ld\n", pointer - txt);
printf("%d\n", pointer - txt);
这两个都打印1,因为它总是以1的差异减去大数,
例如3032 - 3031
但为什么我会得到这些数字?
答案 0 :(得分:3)
C标准允许减去指向同一对象的两个指针:
9 当减去两个指针时,都指向同一个数组对象的元素,或者指向数组最后一个元素的元素 宾语; 结果是两者下标的差异 数组元素。结果的大小是实现定义的,和 它的类型(有符号整数类型)是
ptrdiff_t
中定义的stddef.h
标题。如果结果在对象中无法表示 该类型,行为未定义。换句话说,如果 表达式P和Q分别指向第i和第j个元素 对于数组对象,表达式(P) - (Q)具有提供的值i-j 该值适合ptrdiff_t类型的对象。而且,如果 表达式P指向数组对象的元素或一个元素 超过数组对象的最后一个元素,表达式Q指向 到同一个数组对象的最后一个元素,表达式 ((Q)+1) - (P)具有与((Q) - (P))+ 1相同的值和 - ((P) - ((Q)+1)), 如果表达式P指向过去的一个,则其值为零 数组对象的元素,即使表达式(Q)+1没有 指向数组对象的元素。
评估pointer - txt
时,数组txt
会衰减到指向第一个元素的指针,即&txt[0]
。由于pointer
指向txt[1]
,因此您实际上拥有&txt[1] - &txt[0]
。如上所述,减法的结果是下标之间的差异,即1。
作为用于正常显示该结果,%d
和%ld
格式说明为printf
是不适合于ptrdiff_t
,这是结果的类型。如上所述,此类型的大小是实现定义的。使用错误的格式说明符printf
会调用undefined behavior。
所以你需要使用t
修饰符,它告诉printf
给定的整数参数类型为ptrdiff_t
:
printf("%td\n", pointer - txt);
答案 1 :(得分:2)
指针初始化的方式,pointer
将指向txt
之后的一个位置,这意味着由于指针算术,差异为1
。
答案 2 :(得分:1)
首先请注意,如果两个指针位于同一个数组中,或者超过数组末尾,则只能取两个指针之间的差异。否则行为未定义。你在这一点上是顺从的。
但您不符合您选择的格式说明符。因此,程序的行为是 undefined 。真的,我想看看
ptrdiff_t c = pointer - txt;
接着是
printf("%td\n", c);
ptrdiff_t
是两个指针之间差异的完美类型,%td
是该类型的正确格式说明符。这是完美的便携式C(如果我可以这么说的话)。
答案 3 :(得分:0)
但为什么我会得到这些数字?
仅供参考,我写了以下内容:
#include <stdio.h>
#include "dumper.h"
int main( void )
{
char txt[] = "abcdefghij";
char *ptr = &txt[1];
char *names[] = {"txt", "ptr"};
void *addrs[] = {txt, &ptr};
size_t sizes[] = {sizeof txt, sizeof ptr};
/**
* Use %p to print out pointer values. %p expects the corresponding
* argument to have type `void *`. Because of how printf works,
* this is one of the few places were you really do need to explicitly
* cast pointer values to `void *` (if they aren't already `void *`)
*/
printf( "txt = %p\n", (void *) txt );
printf( "ptr = %p\n", (void *) ptr );
/**
* Use %td to print out ptrdiff_t values.
*/
printf( "ptr - txt = %td\n", ptr - txt );
putchar( '\n' );
dumper( names, addrs, sizes, 2, stdout );
return 0;
}
dumper
是我写的一个实用程序,用于显示内存中对象的布局。运行此代码,我得到以下输出:
txt = 0x7fff7f137380
ptr = 0x7fff7f137381
ptr - txt = 1
Item Address 00 01 02 03
---- ------- -- -- -- --
txt 0x7fff7f137380 61 62 63 64 abcd
0x7fff7f137384 65 66 67 68 efgh
0x7fff7f137388 69 6a 00 00 ij..
ptr 0x7fff7f137378 81 73 13 7f .s..
0x7fff7f13737c ff 7f 00 00 ....
txt
是从地址char
开始存储的0x7fff7f137380
的11个元素数组,其中包含char
个值"abcdefghij\0"
的序列。 ptr
是从地址char
开始存储0x7fff7f137378
的指针,它存储txt[1]
(0x7fff7f113781
)的地址(x86是little-endian,所以从最低有效字节开始存储多字节值。
但请等一下,如果txt
是数组,如何从指针中减去它?
除非它是sizeof
或一元&
运算符的操作数,或者是用于初始化声明中的字符数组的字符串文字,否则类型为表达式 &#34; T
&#34;的N元素数组;将被转换(&#34;衰减&#34;)到类型为&#34的表达式;指向T
&#34;的指针,并且表达式的值将是第一个元素的地址。阵列。
在表达式ptr - txt
中,txt
不是sizeof
或一元&
运算符的操作数,并且它不是用于初始化另一个运算符的字符串文字在一个声明中的数组,所以它'&#34;衰变&#34;来自char
&#34;的类型&#34; 11元素数组的表达式to&#34;指向char
&#34;的指针,其值为txt[0]
的地址。
好。为什么txt
和ptr
地址的数字如此庞大?在x86和x86-64下,程序布局看起来像这样:
+-----------------------------+
High Address: | Command-line arguments |
| and environment variables |
+-----------------------------+
| Stack |
| | |
| V |
| |
| ^ |
| | |
| Heap |
+-----------------------------+
| Uninitialized Data |
+-----------------------------+
| Initialized Data |
+-----------------------------+
| Program Text |
Low Address: | (machine code) |
+-----------------------------+
txt
和ptr
都是auto
个变量,在x86上表示它们的空间是从堆栈中分配的,它从高地址开始。在我的系统上,指针是64位宽。
现在,在这种情况下,txt[0]
和txt[1]
的地址相差1是正确的。但是,让我们尝试一下 - 让我们改变类型txt
至int []
和ptr
至int *
:
#include <stdio.h>
#include "dumper.h"
int main( void )
{
int txt[] = {0,1,2,3,4,5,6,7,8,9};
int *ptr = &txt[1];
char *names[] = {"txt", "ptr"};
void *addrs[] = {txt, &ptr};
size_t sizes[] = {sizeof txt, sizeof ptr};
printf( "txt = %p\n", (void *) txt );
printf( "ptr = %p\n", (void *) ptr );
printf( "ptr - txt = %td\n", ptr - txt );
putchar( '\n' );
dumper( names, addrs, sizes, 2, stdout );
return 0;
}
运行此命令,我得到以下输出:
txt = 0x7fff317fa850
ptr = 0x7fff317fa854
ptr - txt = 1
Item Address 00 01 02 03
---- ------- -- -- -- --
txt 0x7fff317fa850 00 00 00 00 ....
0x7fff317fa854 01 00 00 00 ....
0x7fff317fa858 02 00 00 00 ....
0x7fff317fa85c 03 00 00 00 ....
0x7fff317fa860 04 00 00 00 ....
0x7fff317fa864 05 00 00 00 ....
0x7fff317fa868 06 00 00 00 ....
0x7fff317fa86c 07 00 00 00 ....
0x7fff317fa870 08 00 00 00 ....
0x7fff317fa874 09 00 00 00 ....
ptr 0x7fff317fa848 54 a8 7f 31 T..1
0x7fff317fa84c ff 7f 00 00 ....
txt
和ptr
的地址差异为4,但ptr - txt
仍然会产生1.为什么会这样?
指针算法将指向对象的大小考虑在内 - 如果p
是指向T
的指针,则p + 1
将生成下一个对象的地址键入T
,而不是下一个字节的地址。无论每个&txt[n] - &txt[n-1]
是1,4,8,16,32还是128字节宽,txt[n]
总是会产生1 。