用作 strcat() 的目标数组时 C 中的指针算法

Pointer arithmetic in C when used as a target array for strcat()

在研究 C 中的字符串操作时,我遇到了一种效果,这与我对 strcat() 的预期不太一样。拿下面的小程序来说:

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

int main()
{
    char string[20] = "abcde";
    strcat(string + 1, "fghij");
    printf("%s", string);

    return 0;
}

我希望这个程序能够打印出 bcdefghij。我的想法是,在 C 中,字符串是字符数组,数组的名称是指向其第一个元素的指针,即索引为零的元素。所以变量 string 是指向 a 的指针。但是如果我计算 string + 1 并将其用作与 strcat() 连接的目标数组,我会得到一个指向内存地址的指针,该地址是一个数组元素(1 * sizeof(char),在本例中),因此指向 b 的指针。所以我的想法是目标目的地是以 b 开头(并以不可见的空字符结尾)的数组,并且 fghij 被连接起来,给我 bcdefghij.

但这不是我得到的 - 程序的输出是 abcdefghij。它与我使用 strcat(string, "fghij"); 得到的输出完全相同 - 将 1 添加到 string 被忽略。我也得到了相同的输出加上另一个数字,例如strcat(string + 4, "fghij");,就此而言。

有人可以向我解释为什么会这样吗?我最好的猜测是它与 + 运算符的绑定优先级有关,但我对此不确定。

编辑:我用 char string[20] 增加了原始数组的大小,这样它在任何情况下都足以容纳连接的字符串。输出仍然相同,我认为这意味着数组溢出不是我问题的关键。

    char string[] = "abcde";
    strcat(string + 1, "fghij");

将五个字符附加到完整的字符串数组。繁荣。未定义的行为。

向字符串数组添加一些内容是一种性能优化,它告诉运行时该字符串已知至少有那么多字符长。

您似乎认为字符串是它自己的东西而不是数组,并且 strcat 正在对它的第一个参数做一些事情。那不是那样的。字符串是数组*; strcat 正在修改数组内容。

*有人会过来声称堆分配的字符串不是数组。 OP 还没有处理堆。

您将得到 abcdefghij 的输出,因为您对 strcat 的调用没有更改 string 的地址(而且 也不能 你改变它 - 它在声明它的块的持续时间内是固定的,就像任何其他变量的地址一样)。您传递给 strcat 的是 string 数组的 second 元素的地址:但它仍然被解释为 nul 终止的字符串,调用将第二个(源)参数附加到该字符串。将第二个参数的内容附加到 stringstring + 1string + n 将在 string 数组中产生相同的结果,只要存在 nul 终止符在 n 索引处或之后。

要打印您 实际上 传递给 strcat 调用的字符串的值(即从 'b' 字符开始),您可以保存 return value of the call 并打印:

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

int main()
{
    char string[20] = "abcde";
    char* result = strcat(string + 1, "fghij"); // strcat will return the "string + 1" pointer
    printf("%s", result); // bcdefghij

    return 0;
}

数组是 non-modibfiable 左值。例如你不能写

char string[20] = "abcde";
char string2[] = ""fghij"";

string = string2;

用于表达式的数组在极少数情况下被隐式转换为指向其第一个元素的指针。

如果你将写例如string + 1那么数组的地址将不会改变。

在本次通话中

strcat(string + 1, "fghij");

从数组的第二个元素开始覆盖数组字符串的元素。

在此声明中

printf("%s", string);

从第一个字符开始输出整个数组(再次将用作参数的数组指示符转换为指向其第一个元素的指针)。

你可以这样写

printf("%s", string + 1);

在这种情况下,数组从第二个元素开始输出。

这些只是指向同一数组内同一内存不同部分的两个指针。您的代码中没有任何内容可以创建第二个数组。 “数组的名称是指向其第一个元素的指针”好吧,不是真的,它 decays 成为指向其第一个元素的指针,无论何时在表达式中使用。所以在 string + 1 的情况下,这种衰减首先发生在 string 操作数上,然后你得到指针算术。实际上,您永远不能对数组类型进行指针运算,只能对衰减的指针进行运算。详情在这里:

至于strcat,它基本上做了两件事:在原始字符串上调用strlen找到它的结束位置,然后调用strcpy在该位置追加新字符串存储空终止符的位置。这与输入 strcpy(&src[strlen(src)], dst);

完全一样

因此,如果您传递 string + 1string 并不重要,因为在任何一种情况下 strcat 都会查找空终止符而不是其他任何内容。