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_arr
、a
、b_arr
和b
可能都分配在堆栈上。编译器不必为您提供有关堆栈上变量排列的任何特定保证。因此,编译器可能会填充到 16 字节的倍数,或者可能会在 a
和 b
之间引入其他数据,或者可能会在 a
和 b
之间保存寄存器值, 或者...
这就是为什么正如评论者指出的那样,规范不保证减去属于两个不同数组的指针的结果。好消息是你通常不需要这样做,除非你正在编写 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
我在这个线程中阅读了 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_arr
、a
、b_arr
和b
可能都分配在堆栈上。编译器不必为您提供有关堆栈上变量排列的任何特定保证。因此,编译器可能会填充到 16 字节的倍数,或者可能会在 a
和 b
之间引入其他数据,或者可能会在 a
和 b
之间保存寄存器值, 或者...
这就是为什么正如评论者指出的那样,规范不保证减去属于两个不同数组的指针的结果。好消息是你通常不需要这样做,除非你正在编写 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