为什么2个地址之间的差异不是元素大小的倍数

Hem*_* Kr 2 c++ pointers pointer-arithmetic chararray

我不明白为什么 var 是 6,它是如何计算的

#include <iostream>
using namespace std;
  
int main()
{
    char *A[] = { "abyx", "dbta", "cccc"};
    int var = *(A+1) - *A+1;
    cout << "1: " << (*(A+1)) << "\n";
    cout << "2: " << (*A+1) << "\n";
    cout << "char: " << var << "\n";
    cout << &A[0][1] - &A[1][0] << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

pad*_*ddy 6

首先,这段代码不是格式良好的 C++ 代码。您拥有的是一个指针数组,其中每个指针保存字符串文字的地址。因为指针是非常量的,但字符串文字是常量,所以任何明智的 C++ 编译器至少应该发出警告。

但无论如何,您拥有的数组在内存中可能如下所示(在 64 位系统上):

// char *A[] = { "abyx", "dbta", "cccc" };

        01234567
       +--------+
0x0000 | A[0]   | ----> address of "abyx\0"
       +--------+
0x0008 | A[1]   | ----> address of "dbta\0"
       +--------+
0x0010 | A[2]   | ----> address of "cccc\0"
       +--------+
Run Code Online (Sandbox Code Playgroud)

数组中的每个单元格都有一个指针值。它指向内存中的某个位置,指向程序存储这些字符串文字的位置。预示一下,你不知道它在哪里做的,也不知道它们是如何排列的。

让我们将其与定义为固定大小的 char 值数组进行比较:

// char B[][5] = { "abyx", "dbta", "cccc" };

        01234567
       +--------+
0x0000 |abyx~dbt| (the value '~' denotes a NUL byte)
       +--------+
0x0008 |a~cccc~?|
       +--------+
Run Code Online (Sandbox Code Playgroud)

似乎纯粹是偶然(并且绝不保证),您的编译器已像第二个示例一样在内存中排列了字符串文字,因此您最终得到了类似的结果:

char B[][5] = { "abyx", "dbta", "cccc" };
char *A[] = { B[0], B[1], B[2] };
Run Code Online (Sandbox Code Playgroud)

让我们暂时把它放在一边(但请记住这一点)并讨论您的var计算:

int var = *(A+1) - *A+1
Run Code Online (Sandbox Code Playgroud)

您已将这些内容与空格组合在一起,但您需要注意同一嵌套级别的加法和减法运算符将从左到右进行计算。如果我添加大量荒谬的括号来说明求值顺序,那么实际上是这样的:

int var = ((*(A+1)) - (*A)) + (1);
Run Code Online (Sandbox Code Playgroud)

所以它的作用是取指针A[1],减去指针A[0],然后加 1。因为纯粹是运气,这些指针被编译器像数组一样排列B,然后你就得到了两个以 NUL 结尾的字符串指针之间的 5 个字符的差异,加1,就是6。

如果您实际上想*A+1从中减去该值*(A+1),则需要将其放在括号中:

int var = *(A+1) - (*A+1);  // equivalent to *(A+1) - *A - 1
Run Code Online (Sandbox Code Playgroud)

但是,由于您对不相关的指针进行算术的方式,结果值仍然无法保证。

让我们回想一下为什么我要费力地取出内存位并与其他名为 的数组进行比较B。两个原因:

  1. 为了说明程序中使用的这些字符串文字的地址是不可预测的(因此,指针算术具有未定义的行为)

  2. 显示该数据的一种可能的表示形式(如果您按照我定义 B 的方式定义,则可以A明确表示)并确保您了解字符串在内存中以 NUL 结尾(意味着分配了一个额外的字符)。

希望这能消除一些混乱。