我刚开始学习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”第一个字母的地址。结果,我收到了编译错误。
我对这种编程语言感到非常沮丧。有时他们的工作方式相同。但有时他们没有。任何人都可以解释原因,如果我真的想在分隔行中创建字符串或字符数组。我怎么能这样做???
非常感谢......
答案 0 :(得分:7)
您可以在声明数组时初始化数组,如下所示:
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
,并将字符串复制到它们中。您可以访问数组的元素,就像您声明指向它们的指针一样;数组和指针(在大多数情况下)可以这种方式互换。
但由于数组不是指针,因此无法重新分配它们。
你不能在其他任何地方使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.
效率提示
另请注意,初始化数组通常会复制数据:将原始字符串从常量区域复制到数据区域,程序可以在其中进行更改。当然,这意味着你使用的内存是你预期的两倍。您可以通过声明指针来避免复制并通常节省内存。这使得字符串保持在常量区域,并且只为字符分配足够的内存,而不管字符串的长度。
答案 1 :(得分:2)
您无法直接将值重新分配给数组类型(例如,您的十个char
s name4
数组。对于编译器,name4
是一个“数组”,您不能使用赋值=
运算符来写入具有字符串文字的数组。
为了将字符串“Apple”的内容实际移动到您为其分配的十字节数组(name4
),您必须使用strcpy()
或类似的东西。
您使用name3
所做的事情非常不同。它被创建为char *
并初始化为垃圾或零(此时您不确定)。然后,为其分配静态字符串“Apple”的位置。这是一个存在于只读内存中的字符串,并且尝试写入name3
指针指向的内存永远不会成功。
基于此,您可以推测最后一个语句尝试将静态字符串的内存位置分配给代表10 char
s集合的其他位置。该语言无法为您提供执行此任务的预定方法。
这就是它的力量。
答案 2 :(得分:2)
当你说
时char *name3 = "Apple";
您声明name3
指向静态字符串"Apple"
。如果您熟悉高级语言,您可以将其视为不可变(我将在此上下文中解释它,因为它听起来像您以前编程;对于技术原理,请检查C标准)。
当你说
时char name4[10];
name4 = "Apple";
你得到一个错误,因为你首先声明一个10个字符的数组(换句话说,你“指向” mutable 内存的10字节部分的开头),然后尝试将不可变值"Apple"
分配给此数组。在后一种情况下,实际数据分配发生在一些只读存储器段中。
这意味着类型不匹配:
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")
有效,因为它会将"Apple"
中的每个内容(字符)逐个复制到name4
。
然而,说“这个桶的数量等于那边的东西”是没有意义的。只用值来“填充”桶才是有意义的。
答案 3 :(得分:2)
尽管指针和数组似乎很熟悉,但它们却不同。 char *name3
只是指向char
的指针,它不会占用内存而不是指针。它只是在其中存储一个地址,因此您可以为其分配一个字符串,然后name3
中存储的地址将更改为"Apple"
的地址。
但是你的name4
是一个char[10]
的数组,它保存了10个字符的内存,如果要分配它,你需要使用strcpy
或其他东西写它的内存,但不为其分配"Apple"
等地址。
答案 4 :(得分:0)
我认为这也有助于澄清它:
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
的值很大的原因不同的数字)。