33

跟随问题:为什么数组的地址等于它在 C 中的值?

#include <stdio.h>
#define N 10    
char str2[N]={"Hello"};
int main(){
    printf("sizeof(str2): %d bytes\n", sizeof(str2));
    printf("sizeof(&str2): %d bytes\n", sizeof(&str2));
    return 0;
}

输出:

sizeof(str2): 10 bytes
sizeof(&str2): 4 bytes

我知道str2仅是数组中第一个元素的地址str2。而 whenstr2是它的一个参数,sizeof它返回整个数组 str2 的大小。

此外,&str2也是 arr 中第一个元素的地址,str2但类型不同(char (*)[N]== 指向数组的指针)。但是&str2当它是 的参数时表现如何sizeof

4

4 回答 4

115

&str和之间的区别str,何时str声明为char str[10]

读取 sizeof运算符:

6.5.3.4 sizeof 运算符,1125:
sizeof运算符应用于数组类型时,结果是数组中的总字节数。

因此,根据您的声明,sizeof(str2)给出的完整数组大小为 10 个字节(因为 N 定义为 10,而 char 大小为 1 个字节)。
而在表达式sizeof(&str2)中,&str2是一个数组的地址,并且该地址的大小在您的系统上是 4 个字节。(在某些系统中,地址大小可能为 8 个字节,例如 64 位)。

另外,&str2也是 arr 中第一个元素的地址str2吗?

,在价值方面两者 &str2str是相同的,但在语义上两者是不同的。一个是 10 个字符的数组的地址,而另一个是一个字符的地址。

您在自己的示例中看到它们之间的一个区别是(@ouah 在答案中对此进行了解释)。

  • 类型strchar[10]
  • 类型&strchar(*)[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

键盘链接

请注意,在第一行,输出地址相差一个字节,但在第二行,相差 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

键盘链接

在输出

str2 gives  0xbf587046 
*(str2)     H 
*(&str2)    0xbf587046 
**(&str2)   H 

这意味着*(&str2) == str2和价值是地址。因此*(str2) = **(&str2)值为H

编辑:上面我显示了 type 数组之间的 区别&strstrwhere 。strchar[10]

char *str两者之间以及char str[]如何存储在内存中的区别

假设我们有两个如下声明:

char *str1 = "hello";   
char str2[] = "hello";  

在上面的声明str1中是一个指向 的指针char,它指向一个常量字符串文字(通过保存字符串中第一个字符h的地址"hello")。

C 中的字符串属于char[N](array) 类型,这就是为什么sizeof("hello")给出 6 的原因,因为"hello"字符串是 6 个字符长的数组(包括\0nul、字符串终止、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 points to hello  str1-->"hello"
 str1 = "world";  //Now, str1 points to world  str1-->"world"

现在str1指向其他常量字符串世界。

 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';将是非法的,因为您正在只读内存上写入,并且此行为在运行时未定义(尽管没有编译错误,因为在语法上它是正确的)。

同样,因为str1is 一个指针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

键盘链接

因此,要分配一个新字符串,我只需分配一个新字符串的地址。但我不能称strcpy()它会尝试在只读内存位置上写入,这是非法的。

在第二个声明char str2[] = "hello";中,str2[]是一个\0终止的字符(或字符串)数组,但不是指针。注意,因为在这个声明中 size 没有给出默认大小,我们看到常量字符串“hello”的大小是 6。类型str2char[6]

当我们char str2[] = "hello";创建一个 char 数组时,hello 字符串将被复制到该数组中。所以str2不仅仅是一个指针,而是一个存储完整字符串的数组。

从概念上讲,它就像

       str2:
       103  104  105  106  107  108
      +----+----+----+----+----+----+
      | h  | e  |  l | l  | o  | \0 |    
      +----+----+----+----+----+----+

在这种情况下,最近在您的代码中您不允许这样做str2[] = "world";or 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: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
 str2[2] = 'A';
 printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
 strcpy(str2, "world");
 printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
 return 1; 
}

输出:

str2: hello, address: 0xbf58d056, sizeof(str2): 6
str2: heAlo, address: 0xbf58d056, sizeof(str2): 6
str2: world, address: 0xbf58d056, sizeof(str2): 6

键盘链接

注意:相同地址空间上的字符串值不同。sizeof(str2)= 6 从较早的答案中可以完全理解,即数组的大小(以字节为单位)。

要阅读关于二维数组的类似描述,请阅读:两者在内存中存储的区别char* str[]和方式?char str[][]

于 2013-03-02T17:45:56.630 回答
21

&str2是一个指针。所以你只是在你的平台上看到一个指针的大小。

于 2013-03-02T17:38:10.497 回答
10

str2是类型char [10](即,数组10 ofchar`)

&str2是类型char (*)[10](即,指向 的数组10的指针char)。

所以sizeof (&str2) 产生指针类型的对象的大小char (*)[10]

于 2013-03-02T17:38:34.277 回答
1

当您使用arrto do+时,它会衰减为指向第一个元素-的 const 指针。char (* const) arr虽然一些操作:sizeof(arr)&arr,使用所有类型信息char arr[10],包括大小,10

于 2021-05-01T18:45:23.500 回答