9

我刚开始学习 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 指针和 name2 指向“Apple”的第一个字母的指针,这很好,然后我创建另一个 char 数组并在内存中分配 10 个空间。然后尝试使用name4,它是一个指向“Apple”第一个字母的地址。结果,我得到了一个编译错误。

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

非常感谢...

4

5 回答 5

9

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

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.

效率提示

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

于 2013-06-13T00:45:00.060 回答
2

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

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

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

基于此,您可以推测最后一条语句试图将静态字符串的内存位置分配给代表 10 chars 集合的其他地方。该语言没有为您提供执行此任务的预定方式。

这就是它的力量。

于 2013-06-13T00:31:46.553 回答
2

当你说

char *name3 = "Apple";

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

当你说

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

您会收到一个错误,因为您首先声明了一个 10 个字符的数组(换句话说,您“指向”可变内存的 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一一复制。

但是,说“这个桶的集合等于那边的那个东西”是没有意义的。只有用值“填充”桶才有意义。

于 2013-06-13T00:32:04.200 回答
2

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

但是你name4是一个数组char[10],它拥有10个字符的内存,如果你想分配它,你需要使用strcpy或其他东西来写入它的内存,但不要用像"Apple".

于 2013-06-13T00:33:15.673 回答
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(). 编译器生成保留这些字节的指令,然后用“再见”填充它。没有指针!

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

于 2013-06-13T00:39:47.753 回答