与char**混淆

Yah*_*aly 0 c arrays pointers char

如果我写

//case 1
char *animals[2] = {"cat", "dog"};
char **animal_ptr = animals; 
printf("%s\n", *(animal_ptr + 1)); //fine
Run Code Online (Sandbox Code Playgroud)

而且,以不同的方式:

//case 2
char *animal = "cat";
char **animal_ptr = &animal;
 *(animal_ptr + 1) = "dog";
printf("%s\n", *(animal_ptr + 1)); //fine
Run Code Online (Sandbox Code Playgroud)

所以,我对上面的两个例子感到困惑.

  1. 在案例1中,我理解这animal_ptr是一个指向指针集合的指针,并且由于指针保存地址,我不需要添加&.但是在第2种情况下,我必须添加一个&for才能工作,即使animal它已经是一个指针.为什么?

  2. 在这两种情况下,为什么通过另一个可接受的指针修改字符串文字?据我所知,当你声明一个像char *x = "..etc";这样的字符串时,它被放在一个无法修改的内存中.那么,为什么在情况1,无论animalanimal_ptr可修改字符串?

  3. 为什么strcpy(*(animal_ptr + 1), "bird")失败,程序停止,即使任务在2中工作.?

  4. 在案例2中:

    • 当我这样做printf("%s", *animal_ptr),它工作正常,对我有意义.
    • 当我这样做时printf("%s", *animal),它会停止.为什么?

谢谢,很抱歉很多问题.

Ste*_*mit 5

指针和字符串的东西是你必须跟踪它们的空间分配位置,以及它是否可写.您还必须了解使用复制字符串strcpy与重新排列指针之间的区别.

当您使用字符串常量时"cat","dog"编译器会自动为字符串分配空间,但字符串不可修改.(也就是说,你不能使用它们复制新的字符串strcpy.)

在你的情况1中,你有一个两个字符串的"数组".有几种方法可以考虑这个问题.由于字符串是一个字符数组,并且您有两个字符串,因此您可以将其视为"二维数组".这就是为什么animals有一*对和一对括号[],为什么animal_ptr有两个*.或者,因为a char *是我们通常在C中引用字符串的方式,你可以看到这animals是一个包含两个字符串的数组.

检查所有内容的分配也很重要.编译器负责"cat""dog".您分配animals为大小为2的数组,因此需要处理.最后,animal_ptr设置指向任何地方animals,所以它也有适当的分配.(但请注意animalsanimal_ptr参考相同的存储空间.)

但是,情况2的情况有所不同.你只用一个字符串开始,用"cat"一个指针指向animal.你再次指向那个带有指向指针的字符串,animal_ptr.到目前为止一切都还可以,但我们拥有的是一个字符串数组的等价物.它相当于

char *animals[1]={"cat"};
char **animal_ptr=animals;
Run Code Online (Sandbox Code Playgroud)

所以,当你后来说*(animal_ptr+1)="dog",你正在写一个不存在的"数组"的单元格.你最终覆盖了内存的其他部分.有时你可以侥幸逃脱,有时它会使你程序的其他部分行为错误,有时会导致你的程序崩溃.

可能更容易看到这个,而不是*(animal_ptr+1)="dog"我们写的等价物

animal_ptr[1] = "dog";
Run Code Online (Sandbox Code Playgroud)

由于animal_ptr相当于1元素数组,唯一合法的下标是[0],而不是[1].

现在回答您的具体问题:

Q1.在两个示例中,animal_ptr都是指向指针的指针.在情况1中,animal是一个数组,在C中,您自动获得指向数组的第一个元素的指针,而不使用显式&.在情况2中,animal是一个简单的指针,因此您需要一个显式的&,以获取其地址,以生成所需的指针指针animal_ptr.

Q2.在任何情况下你都不修改任何字符串.你是对的,字符串本身是不可写的,但animals案例1中的数组可写的,所以插入新的指针是好的.你可以这么说animals[0] = "chicken",animals[1] = "pig"直到奶牛回家.在第2种情况下,你可以说animal = "chicken"或者animal_ptr[0] = "pig",因为第一个指针(animal)是可写的,但是你不能修改animal_ptr[1],不是因为它不可写,而是因为它不存在.

Q3.您不能使用strcpy这些示例中的任何一个,因为它试图将新字符复制到您现有的字符串之一,并且由于所有现有字符串都是编译器分配且不可写的,因此失败.

如果你想看看它是如何strcpy工作的,你必须分配一些可写存储空间strcpy来复制到.你可以做点什么

char newarray[10];
animal_ptr[0] = newarray;
strcpy(animal_ptr[0], "bird");
Run Code Online (Sandbox Code Playgroud)

要么

animal_ptr[0] = malloc(10);
strcpy(animal_ptr[0], "bird");
Run Code Online (Sandbox Code Playgroud)

Q4.当你这样做时printf("%s",*animal_ptr)相当于

printf("%s", animal_ptr[0]);
Run Code Online (Sandbox Code Playgroud)

printf %s想要一个指针,你给它一个.但是,当你写的printf("%s", *animal),表达*animal是取第一个字符指向animal,可能是信'c'"cat".然后,您正在接受该角色,并要求printf将其打印为字符串%s.但正如我们刚才所见,%s想要一个指针.所以它试图在内存中的地址99处打印字符串(因为ASCII值为'c'99),并且崩溃了.

更多关于指针赋值与 strcpy

关于C中字符串和指针的另一个问题是,它首先分配它们意味着什么令人困惑.C没有真正的"第一类"内置字符串类型,并且字符串表示为字符/指针数组的方式导致我们始终要记住指针指针之间区别指向.

为了清楚地说明这一点,让我们暂时改变一下,并考虑指向整数的指针.假设我们有

int four = 4;
int five = 5;
int *intptr = &four;
Run Code Online (Sandbox Code Playgroud)

所以我们有一个指针intptr,它指向的是值4(它恰好也是变量的值four).如果我们再说

intptr = &five;
Run Code Online (Sandbox Code Playgroud)

我们正在改变指针的值.它曾经指向four/ 4,现在它指向five/ 5.现在假设我们说

*intptr = 6;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们已经改变了指向的值. intptr指向它之前的相同位置(&five),但我们已将该位置的值更改56.(当然,现在如果我们说printf("%d", five)我们有点奇怪了6.)

现在,回到字符串.如果我有

char *animal = "cat";
Run Code Online (Sandbox Code Playgroud)

然后我说

animal = "dog";
Run Code Online (Sandbox Code Playgroud)

我已经改变了指针指向的位置.它曾经指向包含该字符串的编译器分配的只读内存块"cat",现在它指向包含该字符串的不同的,编译器分配的只读内存块"dog".

现在假设我说

strcpy(animal, "elephant");
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我没有更改指针animal,我要求strcpy将新字符写入指向的位置animal.但是,请记住,animal当前指向包含该字符串的编译器分配的只读内存块"dog".由于它是只读的,因此尝试编写新字符的尝试失败了.即使它没有因为这个原因而失败,我们也会遇到不同的问题,因为当然"elephant"要大于"dog".

底线是在C中"分配"字符串有两种完全不同的方式:分配指针和调用strcpy.但他们真的完全不同.

您可能听说过,"用你不能比较字符串==,<或者>,你必须调用strcmp".比较运算符比较指针(通常不是你想要的),同时strcmp比较指向的字符.但是当涉及到赋值时,你可以这样做:通过重新分配指针,通过复制字符strcpy.

但是如果你正在调用strcpy,你总是必须确保目标指针(a)确实指向某个地方,并且它指向的内存区域是(b)足够大且(c)可写.目标指针通常指向已分配的字符数组,或指向的动态内存区域malloc.(这就是我在回答你的Q3时向你展示的内容.)但是目标不能是指向编译器分配字符串的指针.这很容易犯我的错误,但它不起作用,正如你所见.