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

时间:2013-06-13 00:24:50

标签: c string pointers compiler-errors

我刚开始学习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”第一个字母的地址。结果,我收到了编译错误。

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

非常感谢......

5 个答案:

答案 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的值很大的原因不同的数字)。