字符串指针和c中的字符数组

我刚开始学习C并发现了一些关于字符串指针和字符串(char数组)的混淆。 任何人都可以帮我清醒一下吗?

// source code char name[10] = "Apple"; char *name2 = "Orange"; printf("the address of name: %p\n", name); printf("the address of the first letter of name: %p\n", &(*name)); printf("first letter of name using pointer way: %c\n", *name); printf("first letter of name using array way: %c\n", name[0]); printf("---------------------------------------------\n"); printf("the address of name2: %p\n", name2); printf("the address of the first letter of name2: %p\n", &(*name2)); printf("first letter of name2 using pointer way: %c\n", *name2); printf("first letter of name2 using array way: %c\n", name2[0]); // output the address of name: 0x7fff1ee0ad50 the address of the first letter of name: 0x7fff1ee0ad50 first letter of name using pointer way: A first letter of name using array way: A --------------------------------------------- the address of name2: 0x4007b8 the address of the first letter of name2: 0x4007b8 first letter of name2 using pointer way: O first letter of name2 using array way: O 

所以我假设name和name2都指向他们自己的第一个字母的地址。 然后我的困惑是(见下面的代码)

 //code char *name3; // initialize a char pointer name3 = "Apple"; // point to the first letter of "Apple", no compile error char name4[10]; // reserve 10 space in the memory name4 = "Apple"; // compile errorrrr!!!!!!!!!! 

我创建一个名为name2的char指针和指向“Apple”的第一个字母的name2指针,这很好,然后我创建另一个char数组并在内存中分配10个空格。 然后尝试使用name4,这是一个地址指向“Apple”的第一个字母。 结果,我收到了编译错误。

我对这种编程语言感到非常沮丧。 有时它们的工作方式相同。 但有时他们没有。 任何人都可以解释原因,如果我真的想在分隔行中创建字符串或字符数组。 我怎么能这样做?

非常感谢…

您可以在声明数组时初始化数组,如下所示:

 int n[5] = { 0, 1, 2, 3, 4 }; char c[5] = { 'd', 'a', 't', 'a', '\0' }; 

但由于我们通常将char数组视为字符串,因此C允许特殊情况:

 char c[5] = "data"; // Terminating null character is added. 

但是,一旦声明了数组,就无法重新分配它。 为什么? 考虑像这样的任务

 char *my_str = "foo"; // Declare and initialize a char pointer. my_str = "bar"; // Change its value. 

第一行声明了一个char指针,并将其“目标”在foo的第一个字母处。 由于foo是一个字符串常量,它驻留在内存中的所有其他常量。 当您重新分配指针时,您将为其指定一个新值: bar的地址。 但原始字符串foo保持不变。 您已移动指针,但未更改数据。

但是,在声明数组时,根本不会声明指针。 你需要保留一定数量的内存并给它一个名字。 所以行

 char c[5] = "data"; 

从字符串常量data ,然后分配5个新字节,将它们称为c ,并将字符串复制到它们中。 您可以访问数组的元素,就像您声明指向它们的指针一样; 数组和指针(以大多数用途)可以这种方式互换。

但由于数组不是指针,因此无法重新分配它们。

你不能在任何其他地方做出“点”,因为它不是指针; 它是记忆区域的名称。

可以更改字符串的值, 一次更改一个字符,或者调用strcpy()类的函数:

 c[3] = 'e'; // Now c = "date", or 'd', 'a', 't', 'e', '\0' strcpy(c, "hi"); // Now c = 'h', 'i', '\0', 'e', '\0' strcpy(c, "too long!") // Error: overflows into memory we don't own. 

效率提示

另请注意,初始化数组通常会复制数据:将原始字符串从常量区域复制到数据区域,程序可以在其中进行更改。 当然,这意味着你使用的内存是你预期的两倍。 您可以通过声明指针来避免复制并通常节省内存。 这使得字符串保持在常量区域,并且只为字符分配足够的内存,而不管字符串的长度如何。

尽管指针和数组似乎很熟悉,但它们却不同。 char *name3只是一个指向char的指针,它不占用内存而不是指针。 它只是在其中存储一个地址,因此您可以为其分配一个字符串,然后存储在name3中的地址将更改为"Apple"的地址。

但是你的name4是一个char[10]的数组,它保存10个字符的内存,如果你想分配它,你需要使用strcpy或其他东西写它的内存,但不要给它分配像"Apple"这样的地址。

您无法直接将值重新分配给数组类型(例如,您的十个charname4数组name4 )。 对于编译器, name4是一个“数组”,您不能使用assignment =运算符来写入具有字符串文字的数组。

为了实际将字符串“Apple”的内容移动到为其分配的十字节数组( name4 )中,必须使用strcpy()或类似的东西。

你在name3上做的事情是完全不同的。 它被创建为char *并初始化为垃圾或零(此时您不确定)。 然后,为其分配静态字符串“Apple”的位置。 这是一个存在于只读内存中的字符串,并且尝试写入name3指针指向的内存永远不会成功。

基于此,您可以推测最后一个语句尝试将静态字符串的内存位置分配给代表10个char集的其他位置。 该语言不为您提供执行此任务的预定方法。

这就是它的力量。

当你说

 char *name3 = "Apple"; 

你声明name3指向静态字符串"Apple" 。 如果您熟悉高级语言,您可以将其视为不可变(我将在此上下文中解释它,因为它听起来像您以前编程;对于技术原理,请检查C标准)。

当你说

 char name4[10]; name4 = "Apple"; 

你得到一个错误,因为你首先声明一个10个字符的数组(换句话说,你“指向” 可变内存的10字节部分的开头),然后尝试将不可变"Apple"分配给此arrays。 在后一种情况下,实际数据分配发生在一些只读存储器段中。

这意味着类型不匹配:

 error: incompatible types when assigning to type 'char[10]' from type 'char *' 

如果您希望name4具有值"Apple" ,请使用strcpy

 strcpy(name4, "Apple"); 

如果你想让name4的初始值为"Apple" ,你也可以这样做:

 char name4[10] = "Apple"; // shorthand for {'A', 'p', 'p', 'l', 'e', '\0'} 

这是有效的,而前一个例子没有,因为"Apple"char[]的有效数组初始化器。 换句话说,您正在创建一个10字节的char数组,并将其初始值设置为"Apple" (其余位置为0)。

如果你想到一个int数组,这可能更有意义:

 int array[3] = {1, 2, 3}; // initialise array 

可能我能想到的最简单的口语解释是,数组是事物的桶的集合,而静态字符串"Apple"在那里是“单一的东西”。

strcpy(name4, "Apple")有效,因为它将strcpy(name4, "Apple")每个东西(字符) name4name4

然而,说“这个桶的数量与那边的东西相同”是没有意义的。 只用值来“填充”桶才有意义。

我认为这也有助于澄清它:

 int main() { char* ptr = "Hello"; char arr[] = "Goodbye"; // These both work, as expected: printf("%s\n", ptr); printf("%s\n", arr); printf("%s\n", &arr); // also works! printf("ptr = %p\n", ptr); printf("&ptr = %p\n", &ptr); printf("arr = %p\n", arr); printf("&arr = %p\n", &arr); return 0; } 

输出:

 Hello Goodbye Goodbye ptr = 0021578C &ptr = 0042FE2C arr = 0042FE1C \__ Same! &arr = 0042FE1C / 

所以我们看到arr == &arr 。 由于它是一个数组,编译器知道你总是想要第一个字节的地址,无论它是如何使用的。

arr是一个7 + 1字节的数组,位于main()堆栈中。 编译器生成保留这些字节的指令,然后用“Goodbye”填充它。 没有指针!

另一方面, ptr是一个指针,一个4字节的整数,也在堆栈上。 这就是为什么&ptr非常接近&arr 。 但它指出的是一个静态字符串(“Hello”),它在可执行文件的只读部分中关闭(这就是为什么ptr的值是一个非常不同的数字)。