C中的字符指针减法

Char pointer subtraction in C

我在这个线程中阅读了 C 中的整数指针减法:Pointer subtraction confusion,这很简单,可以掌握和测试。

但是,我尝试用 char* 复制类似的场景,但我得到的结果没有多大意义。

这是我尝试过的场景:

#include <stdio.h>
#include <string.h>

int main() {

    char a_arr[16] = "";
    char *a = a_arr;
    char b_arr[1] = "";
    char *b = b_arr;

    printf("\nThe amount by which they differ is: %d\n", a-b);
    // a-b = 1, which makes sense since they are 1 char away

    return 0;
}

接下来我尝试的是我无法理解的东西

#include <stdio.h>
#include <string.h>

int main() {

    char a_arr[16] = "";
    char *a = a_arr;
    char b_arr[2] = "";
    char *b = b_arr;

    printf("\nThe amount by which they differ is: %d\n", a-b);
    // a-b = 16, which doesn't really make sense to me..    

    return 0;
}

我的猜测是编译器端进行了一些填充,我认为不应该是这种情况,因为它是一个字符数组,不需要对齐..

我不确定为什么它是 16 个字节。非常感谢任何帮助!

我使用了如下的在线接口来编译运行这段代码: http://www.tutorialspoint.com/compile_c_online.php

在这个例子中,a_arrab_arrb可能都分配在堆栈上。编译器不必为您提供有关堆栈上变量排列的任何特定保证。因此,编译器可能会填充到 16 字节的倍数,或者可能会在 ab 之间引入其他数据,或者可能会在 ab 之间保存寄存器值, 或者...

这就是为什么正如评论者指出的那样,规范不保证减去属于两个不同数组的指针的结果。好消息是你通常不需要这样做,除非你正在编写 OS 或标准库 :) .

编辑 此外,内存的排列,以及寄存器中和堆栈中保存的内容,可能会根据您的优化级别而改变。我不认为这可能是这里的一个因素,但要记住这一点。

您的编译器似乎首先存储 b,然后在您的第一个示例中将 a 存储在内存中,在第二个示例中首先存储 a。当我 运行 他们时,我得到:

The amount by which they differ is: 1

The amount by which they differ is: 2

所以我的编译器总是将 b 存储在低于 a 的地址。

你的记忆可能是什么样的:

First Example:
____________________
|B|        A       |
--------------------

Second Example:
______________________
|        A        |B |
----------------------

正如评论者所指出的,无法保证数组的位置。在两个不同的数组中减去指针是未定义的行为。

如果您正确构建测试,您会发现 char 指针减法与 int 指针减法的行为方式完全相同。 IE。返回的值是两个指针之间 char 的数量和 而不是 它们之间的内存地址(可能是也可能不是字节)的数量。

#include <stdio.h>
#include <string.h>

int main()
{
    char a_arr[16] = "";
    char *a = a_arr;
//    char b_arr[1] = "";
    char *b = &a_arr[8];

    printf("\nThe amount by which they differ is: %d\n", b-a);
    // b-a = 8, which makes sense since they are 8 chars away.

    printf("\nThe amount by which their addresses differ is: %d\n", (int)b-(int)a);
    // Which will depend on the implementation and may be something unexpected!

    return 0;
}

我使用的微控制器有 16 位数据总线和寄存器,默认情况下,将字符串的字符存储在备用(偶数)地址。在这种情况下,第一个输出将是 8,第二个输出将是 16。有一些编译器选项可以将字符串的字符存储在连续的内存位置,但这访问速度较慢,因为它涉及移动 16 位数据寄存器以获取奇数地址字节。

我将您的程序重写为只会转储内存的程序。这应该可以让您更好地了解内存中的内容。

正如其他人所指出的,编译器不为您提供有关内存布局的保证。即使 检查内存地址 也可以改变编译器组织其内存的方式。您的问题与其说是关于 C 的问题,不如说是关于您的特定编译器的怪癖。

#include <stdio.h>
#include <string.h>

int main()
{
    char a_arr[16] = "";
    char *a = a_arr;
    char b_arr[1] = "";
    char *b = b_arr;

    void *min, *max, *curr;


    min = &a_arr;
    if (min > (void *)&a) {
        min = &a;
    }
    if (min > (void *)&b_arr) {
        min = &b_arr;
    }
    if (min > (void *)&b) {
        min = &b;
    }

    max = (void *)&a_arr + sizeof(a_arr);
    if (max < (void *)&a + sizeof(a)) {
        max = (void *)&a + sizeof(a);
    }
    if (max < (void *)&b_arr + sizeof(b_arr)) {
        max = (void *)&b_arr + sizeof(b_arr);
    }
    if (max < (void *)&b + sizeof(b)) {
        max = (void *)&b + sizeof(b);
    }

    // Now print them.
    for (curr = min; curr <= max; ++curr) {
        if (curr == &a_arr)
            printf ("%10p: %10x - a_arr\n", curr, *((char *)curr));
        else if (curr == &a)
            printf ("%10p: %10x - a\n", curr, *((char *)curr));
        else if (curr == &b_arr)
            printf ("%10p: %10x - b_arr\n", curr, *((char *)curr));
        else if (curr == &b)
            printf ("%10p: %10x - b\n", curr, *((char *)curr));
        else
            printf ("%10p: %10x\n", curr, *((char *)curr));
    }

    printf ("\nThe amount by which they differ is: %d\n", a-b);

    return 0;
}

这是它在我的机器上的运行方式。注意 b_arr 之后的三个浪费字节。这些字节用于使每个变量从 4 的倍数的地址开始(这称为字边界对齐,并且是非常标准的)。

我怀疑您的编译器在 16 字节边界上对齐 b_arr。这很不寻常,但并不奇怪。编译器为了速度做了最奇怪的事情。

的另一个问题很好地说明了内存对齐的不可预测性。通常,您不应该将内存布局视为确定性的。

  ffbfefbc:   ffffffff - b
  ffbfefbd:   ffffffbf
  ffbfefbe:   ffffffef
  ffbfefbf:   ffffffc0
  ffbfefc0:          0 - b_arr
  ffbfefc1:          0
  ffbfefc2:          0
  ffbfefc3:          0
  ffbfefc4:   ffffffff - a
  ffbfefc5:   ffffffbf
  ffbfefc6:   ffffffef
  ffbfefc7:   ffffffc8
  ffbfefc8:          0 - a_arr
  ffbfefc9:          0
  ffbfefca:          0
  ffbfefcb:          0
  ffbfefcc:          0
  ffbfefcd:          0
  ffbfefce:          0
  ffbfefcf:          0
  ffbfefd0:          0
  ffbfefd1:          0
  ffbfefd2:          0
  ffbfefd3:          0
  ffbfefd4:          0
  ffbfefd5:          0
  ffbfefd6:          0
  ffbfefd7:          0
  ffbfefd8:          0

The amount by which they differ is: 8