字符串文字和数组的地址
Address of a string literal and array
int main(){
char *str1="Hi", *str2 = "Bye";
printf("%u,%u\n",&str1,str1);
int arr[5]={1,2,3,4,5};
printf("%u,%u",arr,&arr);
}
这里发生了什么? str
和 &str
给出不同的地址,arr
和 &arr
给出相同的地址。
我的理解是 arr
指向第一个元素的地址,即 &arr[0]
和 &arr
也会给出相同的地址,但它是整个 arr[5]
的地址。如果我们将 &arr
递增 1,那么它将指向 arr[4] 的下一个元素。但问题是为什么这个过程在字符串的情况下是不同的。请帮我想象一下这里的概念。
1.
char *str1="Hi";
printf("%u,%u\n",&str1,str1);
首先,您对 str1
和 &str1
使用了错误的转换说明符 %u
,这会调用未定义的行为。对于 str1
它应该是 %s
而对于 &str1
它应该是 %p
:
char *str1="Hi";
printf("%p,%s\n",(void*) &str1, str1);
解释:
str1
是指向字符串文字 "Hi"
的第一个元素的地址的指针。 &str1
是指针str1
本身的地址。这就是下面数组版本的区别。
2.
int arr[5]={1,2,3,4,5};
printf("%u,%u",arr,&arr);
同样,此处的转换说明符错误。如果要打印 arr
的第一个元素,则应为 %d
或 %i
,因为 arr
是 int
的数组而不是 unsigned int
或%p
如果要打印第一个元素的地址:
int arr[5]={1,2,3,4,5};
printf("%p,%p",(void*) arr, (void*) &arr);
解释:
arr
(在数组到指针衰减规则之后)衰减到指向 arr
的第一个元素的指针,而 &arr
实际上是指向 [ 的第一个元素的指针=27=]。他们实际上评估相同。
请注意,转换为 void*
是使代码符合 C 标准所必需的。
在 C 中,所有字符串文字实际上都存储为(只读)字符数组,包括空终止符。与任何其他数组一样,它们会衰减到指向其第一个元素的指针。
对于您代码中的 str1
,编译器生成了一些类似于以下的代码:
// Compiler-generated array for the string
char global_array_for_Hi[] = { 'H', 'i', '[=10=]' };
int main(void)
{
char *str1 = global_array_for_Hi;
...
}
您的指针变量 str1
指向该数组的第一个元素('H'
)。当您打印 str1
的值时,这就是您获得的值。
当您打印 &str1
的值时,您会得到变量 str1
本身的位置,您会得到一个指向 str1
类型 char **
的指针。
在图形上有点像
+-------+ +------+ +-----+-----+------+
| &str1 | --> | str1 | --> | 'H' | 'i' | '[=36=]' |
+-------+ +------+ +-----+-----+------+
对于 arr
你有一个数组,它衰减到指向它的第一个元素的指针。当您使用 arr
时,它与 &arr[0]
相同(这是因为 arr[i]
正好等于 *(arr + i)
)。 arr
(和&arr[0]
)的类型是int *
。
当你使用&arr
时你得到一个指向整个数组的指针,它的类型是int (*)[5]
.
&arr[0]
和 &arr
的位置相同,但它们的类型非常不同。
在相关说明中,printf
格式说明符 %u
用于打印 unsigned int
类型的值。要打印指针(更具体地说 void *
类型的值),您必须使用格式说明符 %p
。格式说明符和参数类型不匹配导致 未定义的行为。
array
不是指针。它是连续的内存块。这就是为什么 array
&& &array
具有相同地址
的原因
pointer
是一个保存引用的单独对象。所以 pointer
- 给你指针持有的引用 &pointer
给你 pointer
本身的引用。由于指针是一个单独的对象,因此您有不同的地址
在此声明中
char *str1="Hi", *str2 = "Bye";
声明了两个局部变量str1
和str2
,具有自动存储持续时间。
它们由具有静态存储持续时间的字符串文字的第一个字符的地址初始化。
所以str1
的值就是字符串文字第一个字符的地址"Hi"
。表达式&str1
的值是局部变量str1
本身的地址。
你可以这样想象
&str1 ---> str1 ---> "Hi"
数组是连续的内存范围。所以数组本身的地址和它的第一个元素的地址是相同的。数组占用内存范围的地址。
你可以这样想象
| 1 | 2 | 3 | 4 | 5 |
^
|
&arr----
^
|
arr-----
请注意,在极少数情况下,表达式中使用的数组指示符会转换为指向其第一个元素的指针。所以使用 din 调用 printf 表达式 arr
等同于 &arr[0]
.
关于您的评论
Vlad I've a doubt regard string constant, Why can't we modify this
char *s="HI" but we can modify this char *s[]="HI" I know about second
case it is simple array but can you please clear why can't i modify
string constant
然后根据 C 标准(6.4.5 字符串文字)
7 It is unspecified whether these arrays are distinct provided their
elements have the appropriate values. If the program attempts to
modify such an array, the behavior is undefined.
注意这个声明
char *s[]="HI";
无效。声明了一个指针数组。所以要初始化它你必须写
char * s[] = { "HI" };
并且您可以通过为数组的元素分配其他字符串文字来更改它们。也就是说,您可以更改指针本身,而不是指针指向的字符串文字。
在C语言中,像"Hi, Guys"这样的常量字符串存储在共享内存中。
考虑以下示例:
str = "Hi, there";
上面代码中的字符串是连续存储的。变量 str
指向
字符串的第一个字符,So character 'H' 这里。
因此,str
给出存储在内存某处的第一个字符的地址。
&str
给出变量 str
本身的地址。
数组中的大小写与上面不同。
数组是变量(当然是常量变量),它包含数组的第一个元素(即 &arr[0])的地址。当您执行 &arr
时,它将与 &arr[0]
相同。 &arr 实际上是整个数组的地址,与数组第一个元素的地址相同。
注意:打印&arr + 5
和&arr[0] +5
,你可能会得到一些光。
int main(){
char *str1="Hi", *str2 = "Bye";
printf("%u,%u\n",&str1,str1);
int arr[5]={1,2,3,4,5};
printf("%u,%u",arr,&arr);
}
这里发生了什么? str
和 &str
给出不同的地址,arr
和 &arr
给出相同的地址。
我的理解是 arr
指向第一个元素的地址,即 &arr[0]
和 &arr
也会给出相同的地址,但它是整个 arr[5]
的地址。如果我们将 &arr
递增 1,那么它将指向 arr[4] 的下一个元素。但问题是为什么这个过程在字符串的情况下是不同的。请帮我想象一下这里的概念。
1.
char *str1="Hi";
printf("%u,%u\n",&str1,str1);
首先,您对 str1
和 &str1
使用了错误的转换说明符 %u
,这会调用未定义的行为。对于 str1
它应该是 %s
而对于 &str1
它应该是 %p
:
char *str1="Hi";
printf("%p,%s\n",(void*) &str1, str1);
解释:
str1
是指向字符串文字 "Hi"
的第一个元素的地址的指针。 &str1
是指针str1
本身的地址。这就是下面数组版本的区别。
2.
int arr[5]={1,2,3,4,5};
printf("%u,%u",arr,&arr);
同样,此处的转换说明符错误。如果要打印 arr
的第一个元素,则应为 %d
或 %i
,因为 arr
是 int
的数组而不是 unsigned int
或%p
如果要打印第一个元素的地址:
int arr[5]={1,2,3,4,5};
printf("%p,%p",(void*) arr, (void*) &arr);
解释:
arr
(在数组到指针衰减规则之后)衰减到指向 arr
的第一个元素的指针,而 &arr
实际上是指向 [ 的第一个元素的指针=27=]。他们实际上评估相同。
请注意,转换为 void*
是使代码符合 C 标准所必需的。
在 C 中,所有字符串文字实际上都存储为(只读)字符数组,包括空终止符。与任何其他数组一样,它们会衰减到指向其第一个元素的指针。
对于您代码中的 str1
,编译器生成了一些类似于以下的代码:
// Compiler-generated array for the string
char global_array_for_Hi[] = { 'H', 'i', '[=10=]' };
int main(void)
{
char *str1 = global_array_for_Hi;
...
}
您的指针变量 str1
指向该数组的第一个元素('H'
)。当您打印 str1
的值时,这就是您获得的值。
当您打印 &str1
的值时,您会得到变量 str1
本身的位置,您会得到一个指向 str1
类型 char **
的指针。
在图形上有点像
+-------+ +------+ +-----+-----+------+ | &str1 | --> | str1 | --> | 'H' | 'i' | '[=36=]' | +-------+ +------+ +-----+-----+------+
对于 arr
你有一个数组,它衰减到指向它的第一个元素的指针。当您使用 arr
时,它与 &arr[0]
相同(这是因为 arr[i]
正好等于 *(arr + i)
)。 arr
(和&arr[0]
)的类型是int *
。
当你使用&arr
时你得到一个指向整个数组的指针,它的类型是int (*)[5]
.
&arr[0]
和 &arr
的位置相同,但它们的类型非常不同。
在相关说明中,printf
格式说明符 %u
用于打印 unsigned int
类型的值。要打印指针(更具体地说 void *
类型的值),您必须使用格式说明符 %p
。格式说明符和参数类型不匹配导致 未定义的行为。
array
不是指针。它是连续的内存块。这就是为什么 array
&& &array
具有相同地址
pointer
是一个保存引用的单独对象。所以 pointer
- 给你指针持有的引用 &pointer
给你 pointer
本身的引用。由于指针是一个单独的对象,因此您有不同的地址
在此声明中
char *str1="Hi", *str2 = "Bye";
声明了两个局部变量str1
和str2
,具有自动存储持续时间。
它们由具有静态存储持续时间的字符串文字的第一个字符的地址初始化。
所以str1
的值就是字符串文字第一个字符的地址"Hi"
。表达式&str1
的值是局部变量str1
本身的地址。
你可以这样想象
&str1 ---> str1 ---> "Hi"
数组是连续的内存范围。所以数组本身的地址和它的第一个元素的地址是相同的。数组占用内存范围的地址。
你可以这样想象
| 1 | 2 | 3 | 4 | 5 |
^
|
&arr----
^
|
arr-----
请注意,在极少数情况下,表达式中使用的数组指示符会转换为指向其第一个元素的指针。所以使用 din 调用 printf 表达式 arr
等同于 &arr[0]
.
关于您的评论
Vlad I've a doubt regard string constant, Why can't we modify this char *s="HI" but we can modify this char *s[]="HI" I know about second case it is simple array but can you please clear why can't i modify string constant
然后根据 C 标准(6.4.5 字符串文字)
7 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.
注意这个声明
char *s[]="HI";
无效。声明了一个指针数组。所以要初始化它你必须写
char * s[] = { "HI" };
并且您可以通过为数组的元素分配其他字符串文字来更改它们。也就是说,您可以更改指针本身,而不是指针指向的字符串文字。
在C语言中,像"Hi, Guys"这样的常量字符串存储在共享内存中。 考虑以下示例:
str = "Hi, there";
上面代码中的字符串是连续存储的。变量 str
指向
字符串的第一个字符,So character 'H' 这里。
因此,str
给出存储在内存某处的第一个字符的地址。
&str
给出变量 str
本身的地址。
数组中的大小写与上面不同。
数组是变量(当然是常量变量),它包含数组的第一个元素(即 &arr[0])的地址。当您执行 &arr
时,它将与 &arr[0]
相同。 &arr 实际上是整个数组的地址,与数组第一个元素的地址相同。
注意:打印&arr + 5
和&arr[0] +5
,你可能会得到一些光。