理解 char array[] 和 string
Posted
技术标签:
【中文标题】理解 char array[] 和 string【英文标题】:Understanding char array[] and string 【发布时间】:2015-06-04 16:23:16 【问题描述】:我是编程新手。我正在学习 C 作为我的第一门编程语言。我发现了一些奇怪的东西。
我了解到,在 C 语言中,我们可以将字符串表示为这样的字符序列(使用 char 数组):
char status[10] = "Married";
我了解到这种方法的问题是我们必须在编译期间告诉status
数组的大小。
但现在我知道我们可以使用 char
指针来表示 string
之类的 -
char status[10] = "Married";
char *strPtr;
strPtr = status;
我没有正确理解它。我的问题是 -
如何使用strPtr
获取索引 4 处的字符(即 Married 中的 i)?
在status
中,string
的末尾有一个空字符 (\0
),由 char
数组表示 - M
-a
-r
-@987654335 @-i
-e
-d
-\0
。所以通过使用空字符(\0
)我们可以理解字符串的结尾。当我们使用strPtr
时,如何理解string
的结尾?
【问题讨论】:
1)*(strPtr+4)
会给你字符i
。 2)strPtr
也指向status
,所以它最后会有(相同的)空字符(\0
)。
由于数组衰减为指向其第一个元素的指针,因此数组和指针通常可以互换使用,因此在数组上使用解引用运算符将起作用,就像在指针上使用数组索引运算符一样。此外,*(arrayOrPointer + X)
等价于 arrayOrPointer[X]
。
检查What does sizeof(&array)
return?
【参考方案1】:
我要发表一个挑衅性的声明:考虑这一点的方式是 C
没有字符串。 C
只有char
的数组。尽管有它的名字,char
实际上是一个 numeric 类型(例如,'A'
只是写数字的一种有趣的方式,通常是 65)。
char
的数组与int
的数组或任何其他数值类型的数组并没有真正的不同;只是该语言提供了一些额外的方法来编写char
类型的对象及其数组,并且有一个通用约定(使用strlen
之类的函数系统化)如何解释存储在char
数组中的数据为字符串的表示。
char status[10]; // declares an array of `char` of length 10.
char *strPtr; // declare a pointer to `char`
strPtr = status; // make `strPtr` point to the first element of `status`
// Declare an array of 6 `char`, and initialize it.
char hello[6] = 'H', 'e', 'l', 'l', 'o', '\0';
// Shorthand notation for initializing an array of 6 `char` as above
char world[6] = "World";
// I want to store numeric data in this one!
char other[6] = 0, 1, 2, 3, 4, 5;
// "World" is shorthand for a constant array of 6 `char`. This is
// shorthand for telling the compiler to actually put that array in
// memory someplace, and initialize worldPtr to point to that memory.
const char *worldPtr = "World";
// This does the same thing as above. But it's still a *constant* array.
// You should *never* do this; it should be syntactically illegal to
// make a nonconstant `char*` to point to it. This is only allowed for
// historical reasons.
char *helloPtr = "Hello";
【讨论】:
【参考方案2】:char *strPtr;
strPtr = status;
现在您的指针 strPtr
指向数组中的第一个字符,您可以这样做
int i =0;
while( strPtr[i] != '\0')
printf("%c ",strPtr[i]);
i++;
*strPtr
被称为解引用指针以获取存储在指针指向的位置的值。
请注意
strPtr[4] = *(strPtr +4);
两者都会得到存储在数组索引 4 处的值。
注意指针和数组名的区别:
----------------------------------
| s | t | r | i | n | g | \0 |
----------------------------------
|
strPtr
status
strPtr ++
将使您的指针指向数组中的下一个元素。
| s | t | r | i | n | g | \0 |
----------------------------------
|
strPtr
而您不能对数组名称执行此操作
status++
不允许,因为an array is not a modifiable lvalue。
【讨论】:
哇!这意味着我们可以使用指针名称-strPtr
作为数组?我现在就试试。感谢您的回复。
@KajolK 检查编辑以获取数组和指针之间的区别
在 C 中,数组索引只是添加后指针的取消引用:x[i]
仅表示 *(x + i)
。
@KajolK;永远记住pointers are not arrays and vice-versa。
@KajolK:不,这意味着索引运算符[]
需要一个指针,而不是数组作为其操作数之一。通常,该指针是数组名称隐式转换的结果。【参考方案3】:
表达式status[10]
只是*(status+10)
的语法糖。
\0
终止用于在后台检查结束,如果您自己实现一些字符串处理程序,您也可以这样做,或者您可以忽略它并使用其他参数 size
字符串,或者你可以(不要!)选择其他任何东西作为终止符号。
这不仅适用于 char
数组或“字符串”,C 数组只是指向类似类型内容的连续块的指针,并在编译时检查“数组”下标不存在t 超出声明时指定的“结束”。使用*(array+offset)
表示法,您需要自己检查。
【讨论】:
@MattMcNabbstatus[10]
在这里不是未定义的行为;该标准定义了取消引用过去数组的最后一个元素。
@BlacklightShining 它只定义是否保证在该位置分配内存(并且某些其他条件为真),char status[10];
不是这种情况
status[10]
与*(status+10)
在任何地方except在问题所询问的特定上下文中的含义相同。【参考方案4】:
很高兴知道:
char status[10] = "Married";
只是 语法糖 的等价物:
char status[10]; // allocate 10 Bytes on stack
status[0] = 'M';
status[1] = 'a';
...
status[6]= 'd';
status[7] = '\0'; // same as 0
不多也不少。
还有:
char c = status[3];
一模一样和
char c = *(status+3);
【讨论】:
除了new
这里没有动态分配内存,所以应该是new (some address in .data or .rodata) char[10];
我不确定,c 中是否有任何“新”关键字?还是我必须为此使用 C++?
@KajolK:C 中现在有new
。如果你想动态分配字符串,你需要malloc()
。
好吧,我做的 C# 太多了。编辑了我的答案。谢谢。
@che C 中没有 new
。*(我花了很长时间寻找带有 new
关键字的 C 版本……)【参考方案5】:
字符串末尾的 '\0' 是一个无用的附加组件,旨在方便或安全。你可以像这样使用'sizeof'来告诉字符串的最后一个字符:
char status[] = "Married";
size_t szLastCharstatus = sizeof(status) / sizeof(status[0]) - 2;
char chLastChar = status[szLastCharstatus];
详细解释:
sizeof(status)
返回数组占用的字节数。
sizeof(status[0])
返回第一个元素占用的字节数(其余的也是如此)。
这两个值之间的除法为我们提供了数组中元素的数量。现在要访问最后一个元素,我们需要减去 2 次,因为数组中的元素从零开始计数,并且字符串中的最后一个字符是 '\0'。
还要注意数组不是指针,反之亦然。数组隐式转换为其第一个元素的指针、常量大小和它们自己的类型。它们可以通过指针或值传递(第二个需要使用结构破解)。
请注意,我使用的是“size_t”,它是存储一定大小数据的变量的类型定义。
【讨论】:
这将给出数组中元素的数量,而不是数组中包含的字符串的长度。由于仅初始化了 8 个字符,因此最后两个字符将具有不确定的值,读取它们将导致未定义的行为。另请注意,这仅适用于“正确”数组,一旦数组衰减到指针,sizeof
技巧将不再起作用。而且终止符也不是没用的,所有标准的 C 字符串函数都依赖于它的存在。
另请注意,问题标记为C
,因此没有std
命名空间。
@Joachim Pileborg 然而,有时这会降低性能,避免它需要复杂的语法。只要数组通过值或指针传递,这个技巧就会起作用。也修复了“C”。
尾随的空字节根本没有用——因为指针衰减,它实际上是 only 找到字符串结尾的方法,如果它是例如作为参数传递给函数。【参考方案6】:
要获取索引 4 strPtr
处的字符,您只需使用 strPtr[4]
(这也适用于 status
)。
要在使用strPtr
时获取字符串的结尾,您需要遍历字符并查找终止符\0
。这就是printf("%s", strPtr)
在打印字符串时所做的(以及在解析"%s"
表达式时,这只是另一个字符串)。要在 C 中的字符串中查找多个有效字符,请使用 strlen()
函数。哦,确保你不要做这样的事情:
char a[3];
strcpy(a, "Hello!");
因为这会将 7 个字节写入 3 个字节的内存空间,因此会覆盖您不想覆盖的内容。
【讨论】:
以上是关于理解 char array[] 和 string的主要内容,如果未能解决你的问题,请参考以下文章
Java 使用 char[] Array 还是 String 存储字符串密码