C中的字符指针减法

时间:2016-09-20 13:48:08

标签: c pointers pointer-arithmetic

我已经在这个帖子中读到了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;
}

我的猜测是,编译器端有一些填充内容,我认为不应该是这种情况,因为它是一个char数组,不需要对齐.. < / p>

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

我使用以下在线界面来编译和运行这段代码: http://www.tutorialspoint.com/compile_c_online.php

4 个答案:

答案 0 :(得分:4)

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

这就是为什么,正如评论者指出的那样,规范并不能保证减去属于两个不同数组的指针的结果。好消息是,除非您正在编写操作系统或标准库,否则通常不需要这样做。

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

答案 1 :(得分:1)

您的编译器似乎首先存储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 |
----------------------

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

答案 2 :(得分:1)

我把你的程序改写成了只会转储内存的东西。这应该可以让你更好地了解内存中的内容。

正如其他人所指出的那样,编译器不会为您提供有关内存布局的保证。即使检查内存地址也可以改变编译器组织内存的方式。你的问题不是关于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的倍数开始(这称为字边界对齐,非常标准)。

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

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

  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

答案 3 :(得分:0)

如果正确构建测试,您会发现char指针减法的行为与int指针减法完全相同。即返回的值是两个指针之间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位数据寄存器移位到奇数寻址字节。