为什么在 C 语言中可以通过指针交换字符串?

Zar*_*uta 1 c arrays string pointers

据我所知,当我们创建数组或字符串时,它们的名称是指向其第一个元素的指针。在这种情况下,指针无法更改。

然而,这段代码可以工作:

#include <stdio.h>

void swapString(char **str1_ptr, char **str2_ptr)
{
    char *temp = *str1_ptr;
    *str1_ptr = *str2_ptr;
    *str2_ptr = temp;
}

int main()
{
    char strings[][30] = {"Test1","Test2","Test3"};
    int i;
    
    swapString((char **)&strings[0],(char **)&strings[1]);
    
    for(i=0;i<3;i++){
        printf("%s\n",strings[i]);
    }
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

为什么这个效果很好?以这种方式交换字符串(或一般的数组)是一个好习惯吗?什么时候使用这种方法不是一个好主意?

Ger*_*rdh 5

\n

为什么这个效果很好?

\n
\n

它不能很好地工作。\n它似乎工作得很好,因为你对编译器撒了谎并且使用了不足的测试数据。

\n
void swapString(char **str1_ptr, char **str2_ptr)\n{\n    char *temp = *str1_ptr;\n    *str1_ptr = *str2_ptr;\n    *str2_ptr = temp;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您声称提供了一个指向指针的指针。因此,该函数在两个位置之间交换的字节数与指针所占用的字节数一样多。在大多数系统上,这是 4 或 8 个字节。

\n

但随后您传递数组的地址,而不是指向指针的指针:

\n
    char strings[][30] = {"Test1","Test2","Test3"};\n    \n    swapString((char **)&strings[0],(char **)&strings[1]);\n
Run Code Online (Sandbox Code Playgroud)\n

这会导致未定义的行为。您正在使用强制转换,因为编译器告诉您,如果删除它们,这就是螃蟹:

\n
test.c:15:28: warning: passing argument 2 of \xe2\x80\x98swapString\xe2\x80\x99 from incompatible pointer type [-Wincompatible-pointer-types]\n   15 |     swapString(&strings[0],&strings[1]);\n      |                            ^~~~~~~~~~~\n      |                            |\n      |                            char (*)[30]\ntest.c:3:41: note: expected \xe2\x80\x98char **\xe2\x80\x99 but argument is of type \xe2\x80\x98char (*)[30]\xe2\x80\x99\n
Run Code Online (Sandbox Code Playgroud)\n

因此,该函数认为它交换了一个指针,但实际上它交换了数组的前 8 个字节。

\n

要验证这一点,您可以使用更长的字符串:

\n
test.c:15:28: warning: passing argument 2 of \xe2\x80\x98swapString\xe2\x80\x99 from incompatible pointer type [-Wincompatible-pointer-types]\n   15 |     swapString(&strings[0],&strings[1]);\n      |                            ^~~~~~~~~~~\n      |                            |\n      |                            char (*)[30]\ntest.c:3:41: note: expected \xe2\x80\x98char **\xe2\x80\x99 but argument is of type \xe2\x80\x98char (*)[30]\xe2\x80\x99\n
Run Code Online (Sandbox Code Playgroud)\n

然后你就会得到你应得的结果。;)

\n
Test$ ./test\nTest222211\nTest111122\nTest333333\n
Run Code Online (Sandbox Code Playgroud)\n

如果是 32 位地址,您需要使字符串的前 4 个字节可区分。

\n

正如您所看到的,根本没有交换指针。只是你的数组的开头就被搞乱了。

\n

如果您想通过交换指针来交换字符串,则需要更改数组定义:

\n
#include <stdio.h>\n\nvoid swapString(char **str1_ptr, char **str2_ptr)\n{\n    char *temp = *str1_ptr;\n    *str1_ptr = *str2_ptr;\n    *str2_ptr = temp;\n}\n\nint main()\n{\n    char strings[][30] = {"Test111111","Test222222","Test333333"};\n    int i;\n\n    swapString((char **)&strings[0],(char **)&strings[1]);\n\n    for(i=0;i<3;i++){\n        printf("%s\\n",strings[i]);\n    }\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

作为副作用,您还可以从函数调用中删除强制转换,因为类型现在符合预期。

\n