字符串指针和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"
这样的地址。
您无法直接将值重新分配给数组类型(例如,您的十个char
的name4
数组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")
每个东西(字符) name4
到name4
。
然而,说“这个桶的数量与那边的东西相同”是没有意义的。 只用值来“填充”桶才有意义。
我认为这也有助于澄清它:
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
的值是一个非常不同的数字)。