&str
和str
之间的区别,当str
声明为char str[10]
时?
阅读sizeof
运算符:
6.5.3.4 sizeof运算符,1125:
当对数组类型应用sizeof
运算符时,结果是数组中字节的总数。
因此,根据您的声明,sizeof(str)
会给出完整的数组大小,即10个字节(因为N定义为10,而char大小为1字节)。
而在表达式sizeof(&str)
中,&str
是一个数组的地址,该地址在您的系统上的大小为4字节。(在某些系统上,如64位系统中,地址大小可能为8字节)。
此外,&str
也是arr str
中第一个元素的地址吗?
不是,就值而言,&str
和str
是相同的,但从语义上讲,它们是不同的。其中一个是10个字符数组的地址,而另一个是字符的地址。
您可以在自己的示例中看到它们之间的一个区别(@ouah在答案中解释了这一点)。
str
的类型是char[10]
&str
的类型是char(*)[10]
其次:观察下面的图表将帮助您观察到另一个区别。
for declaration:
#define N 10
char str2[N] = {"Hello"};
str2 Array in memory is something like:
str
+
|'H' |'e' |'l' |'l' |'o' |'\0'|'\0'|'\0'|'\0'|'\0'|| '@'|
+
201 202 203 204 205 206 207 208 209 210 211
▲ ▲ ▲ ▲
| | | |
|(str2) (str2 + 1) |
| |
|
|201 |
| |
| |
(&str2) = 201 (&str2 + 1) = 211
* assuming str address start from 201
* str[N] is 10 char long 201-210, partially initialized
* at uninitialized position, str2[i] = '\0'
* location 211 is unallocated, having garbage value,
access to this location is illegal-Undefined Behavior
对于上面的图表,你可以编写如下代码:
#include <stdio.h>
#define N 10
int main(){
char str2[N]={"Hello"};
printf("\n %p, %p\n",str2, str2+1);
printf("\n %p, %p\n",(&str2), (&str2+1));
}
输出:
0xbf67e142, 0xbf67e143
0xbf67e142, 0xbf67e14c
一个codepad链接:
请注意,在第一行,输出地址与原地址相差一个字节,但在第二行中,由于这是指向数组的指针(如上图所示),因此差异为10个字节。
根据指针算术规则,将1添加到指针变量时,它开始指向其自身类型的下一个元素。这就是10字节差异的原因,因为&str2
是指向数组的地址。
第三个差异:
通过使用*str2
,您可以访问第一个元素。而*(&str2)
不会给您第一个元素,而是会给出第一个元素的地址。
这里有一个例子:
#include <stdio.h>
#define N 10
int main(){
char str2[N]={"Hello"};
printf("\n%p %c, %p %c\n",str2, *(str2), *(&str2), **(&str2));
}
输出:
0xbf587046 H, 0xbf587046 H
Codepad链接
输出中
str2 gives 0xbf587046
*(str2) H
*(&str2) 0xbf587046
**(&str2) H
这意味着 *(&str2) == str2
并且value是地址。因此,*(str2) = **(&str2)
,值为 H
。
编辑:上面我展示了&str
和str
之间的区别,其中str
是类型为char[10]
的数组。
char *str
和char str[]
之间的区别以及它们在内存中的存储方式
假设我们有以下两个声明:
char *str1 = "hello"
char str2[] = "hello"
在上述声明中,
str1
是指向常量字符串字面值的
char
指针(通过保存在
"hello"
字符串中第一个字符
h
的地址进行指向)。
C 中的字符串是 char[N]
(数组)类型,这就是为什么 sizeof("hello")
返回 6 的原因,因为 "hello"
字符串是一个 6 个字符长的数组(包括 \0
空字符,用于字符串终止,hello 的类型是 char[6]
)。
在内存中,"hello"
字符串的存储方式如下:
str1 23 24 25 26 27 28
+----+ +----+----+----+----+----+----+
| 23 | | h | e | l | l | o | \0 |
+----+ +----+----+----+----+----+----+
+-----------▲
here the address of the hello string is the first address = 23.
str1: is a pointer capable of storing an address.
"hello" consists of 6 chars
char* str1 = "hello";
基本上将字符串“hello”的地址存储到指针变量str1
中,正如我在上面的图中所示。
注意:如果需要,可以将str1
更改为指向其他字符串。但是无法修改字符串“hello”。例如,以下代码是有效的:
char* str1 = "hello"
str1 = "world"
现在
str1
指向另一个常量字符串“world”。
str1 93 94 95 96 97 98
+----+ +----+----+----+----+----+----+
| 93 | | w | o | r | l | d | \0 |
+----+ +----+----+----+----+----+----+
+-----------▲
here address of world string is first address = 93.
str1: value change to point string world.
需要注意的是:str1
指向常量字符串,因此您不能通过访问/索引内存位置来修改字符串,例如 str1[i] = 'A'
; 这样做将是非法的,因为您正在写入只读内存,这在运行时的行为是未定义的(虽然没有编译错误,因为从语法上讲它是正确的)。
同样,由于 str1
是指针,在同一机器上使用 sizeof(str1)
将会返回 4。
以下是我的代码及其运行结果:
#include <stdio.h>
int main(){
char* str1="Hello";
printf("\nstr1: %s, address: %p, sizeof(str1): %u", str1, str1, sizeof(str1));
str1 = "world";
printf("\nstr1: %s, address: %p, sizeof(str1): %u", str1, str1, sizeof(str1));
return 1;
}
输出:
str1: Hello, address: 0x80485e8, sizeof(str1): 4
str1: world, address: 0x8048619, sizeof(str1): 4
Codepad链接
为了分配新的字符串,我只需分配一个新字符串的地址。但是我不能调用strcpy()
,因为它会尝试写入只读内存位置,这是不合法的。
在第二个声明中char str2[] = "hello";
,str2 []
是以\0
结尾的字符数组(或字符串),而不是指针。请注意,因为在此声明中未给定大小默认大小,我们可以看到常量字符串“hello”的大小为6。 str2
的类型是char [6]
。
当我们执行char str2[] = "hello";
时,将创建一个char数组,并将hello字符串复制到该数组中。str2
不仅仅是一个指针,而是一个存储完整字符串的数组。
从概念上讲,就像
str2:
103 104 105 106 107 108
+
| h | e | l | l | o | \0 |
+
最近在你的代码中,你不能使用str2[] = "world";
或者str2 = "world"
,这将会导致编译时错误。
示例代码:
#include<stdio.h>
int main(){
char str2[] = "hello";
str2[] = "world";
str2 = "world";
return 1;
}
编译错误:
In function 'main':
Line 4: error: expected expression before ']' token
Line 5: error: incompatible types in assignment
Codescape链接
当这个数组str2
不是常量时,我们可以修改它的内容,例如执行str2[2] ='A'
是完全有效的。我们也可以调用strcpy来改变内容(地址空间不会改变)。
strcpy(str2, "world");
str2:
103 104 105 106 107 108
+
| w | o | r | l | d | \0 |
+
Note that when "world" is copied into a same memory space, the addresses of both "world" and "hello"
string are the same.
代码示例:
#include<stdio.h>
int main(){
char str2[] = "hello";
printf("\nstr2:
str2[2] = 'A';
printf("\nstr2:
strcpy(str2, "world");
printf("\nstr2:
return 1;
}
输出:
str2: hello, address: 0xbf58d056, sizeof(str2): 6
str2: heAlo, address: 0xbf58d056, sizeof(str2): 6
str2: world, address: 0xbf58d056, sizeof(str2): 6
Codepad链接
注意:在同一地址空间中,字符串值是不同的。sizeof(str2)
= 6,从早期的答案可以完全理解这是字节数组的大小。
要阅读关于二维数组的类似描述,请阅读:char* str[]和char str[][]之间的区别以及两者在内存中如何存储?
str2
本身是数组str2
中第一个元素的地址” - 其实不是。在大多数情况下,它会被转换为指向其第一个元素的指针,但sizeof
是其中的例外之一。 - Daniel Fischerprintf("%p %p",str2, str2+1);
打印第一个和第二个元素的地址? - user1980750sizeof
、_Alignof
或取地址运算符&
的操作数,或在字符串字面值的情况下作为char[]
的初始化器,否则数组类型的表达式将被转换为指向该数组第一个元素的指针。因此,在大多数情况下,数组的名称会被评估为指向其第一个元素的指针,但它确实是不同的东西(即数组的名称)。 - Daniel Fischer_Alignof
不是其中的例外,因为_Alignof
只能应用于带括号的类型名,而不能应用于表达式。这是N1570 草案中的错误,在发布的 ISO C11 标准中已经得到了纠正。(至于 why_Alignof
不能应用于表达式,那就是另外一个问题了。) - Keith Thompson