我正在学习C,今天我坚持使用C语言中的“字符串”。基本上我明白C语言中没有字符串这样的东西。 在C字符串中是一个以\ 0结尾的数组字符。 到目前为止一切都很好。
char *name = "David";
char name[] = "David";
char name[5] = "David";
这是令人困惑的开始。声明“字符串”的三种不同方式。你能给我一个简单的例子,说明哪种情况可以使用。我在网上看了很多教程,但仍然无法理解。
我在stackoverflow上阅读了这个How to declare strings in C的问题,但仍然无法区别对待..
答案 0 :(得分:3)
第一个char *name = "David";
是字符串文字,位于内存的只读部分。你不能对它做任何修改。最好写
const char * name =“David”;
第二个char name[] = "David";
是6
个字符串,包括'\0'
。可以进行修改。
char name[5] = "David";
调用未定义的行为。 "David"
是一个包含6个字符的字符串(包括终止'\0'
)。你需要一个包含6个字符的数组来存储它。
char name [6] =“David”;
进一步阅读:C-FAQ 6. Arrays and Pointers。
答案 1 :(得分:0)
这link提供了很好的解释。
char[]
指的是数组,char*
指的是指针,它们不是同一个东西。
char a[] = "hello"; // array
char *p = "world"; // pointer
根据附件J.2 / 1的标准,在以下情况下它是未定义的行为:
- 程序尝试修改字符串文字(6.4.5)。
6.4.5 / 5说:
在转换阶段7中,附加值为零的字节或代码 每个由字符串文字产生的多字节字符序列 或文字。
因此,您实际上需要一个包含六个元素的数组来计算NUL字符。
答案 2 :(得分:0)
在第一个示例中,您声明了一个指向变量的指针:
// A variable pointer to a variable string (i.e. an array of 6 bytes).
char *pName = "David";
此时,您可以修改'D', 'a', 'v', 'i', 'd', '\0'
占用的6个字节:
pName[0] = 'c';
*pName = 'c';
*(pName+0) = 'c';
strcpy(pName, "Eric"); // Works well
但只有6个字节:
// BUG: Will overwrite 2 random bytes located after \0 in RAM.
strcpy(pName, "Fredrik");
指针可以在运行时更改为指向另一个变量字符串,例如
pName = "Charlie Chaplin";
然后可以修改
pName[0] = 'c';
*pName = 'c';
*(pName+0) = 'c';
// OK now, since pName now points to the CC array
// which is 16 bytes located somewhere else:
strcpy(pName, "Fredrik");
正如其他人所说,您通常会在指针案例中使用const char *
,这也是使用字符串的首选方式。原因是编译器将帮助您从最常见(和难以发现)的内存传输错误中获取:
// A variable pointer to a constant string (i.e. an array of 6 constant bytes).
const char *pName = "David";
// Pointer can be altered runtime to point to another string e.g.
pName = "Charlie";
// But, the compiler will warn you if you try to change the string
// using any of the normal ways:
pName[0] = 'c'; // BUG
*pName = 'c'; // BUG
*(pName+0) = 'c'; // BUG
strcpy(pName, "Eric");// BUG
使用数组的其他方式提供的灵活性较低:
char aName[] = "David"; // aName is now an array in RAM.
// You can still modify the array using the normal ways:
aName[0] = 'd';
*aName = 'd';
*(aName+0) = 'd';
strcpy(aName, "Eric"); // OK
// But not change to a larger, or a different buffer
aName = "Charlie"; // BUG: This is not possible.
同样,常量数组可以帮助您:
const char aName[] = "David"; // aName is now a constant array.
// The compiler will prevent modification of it:
aName[0] = 'd'; // BUG
*aName = 'd'; // BUG
*(aName+0) = 'd'; // BUG
strcpy(aName, "Eric");// BUG
// And you cannot of course change it this way either:
aName = "Charlie"; // BUG: This is not possible.
使用指针与数组声明之间的主要区别是返回值sizeof()
:sizeof(pName)
是指针的大小,即通常为4. sizeof(aName)
返回的大小数组,即字符串的长度+ 1。
如果变量是在函数内部声明的话,最重要的是,特别是如果字符串很长:它占用了更多的珍贵堆栈。因此,通常避免数组声明。
将变量传递给使用sizeof()
的宏时也很重要。这些宏必须提供预期的类型。
如果您想要,例如交换字符串。声明为指针的字符串是直接的,需要CPU访问较少的字节,只需移动指针的4个字节:
const char *pCharlie = "Charlie";
const char *pDavid = "David";
const char *pTmp;
pTmp = pCharlie;
pCharlie = pDavid;
pDavid = pTmp;
pCharlie
现在是“大卫”,pDavid
现在是“查理”。
使用数组,你必须为最大的字符串提供足够大的临时存储空间,并使用strcpy(),它需要更多的CPU,在字符串中按字节复制字节。
最后一种方法很少使用,因为编译器会自动计算出David需要6个字节。无需告诉它什么是显而易见的。
char aName[6] = "David";
但是,它有时用于数组必须是固定长度的情况,与其内容无关,例如在二进制协议或文件中。在这种情况下,为了从编译器获得帮助,手动添加限制可能是有益的,如果有人在将来偶然添加或删除字符串中的字符。