为什么递增数组名称时没有 'l-value required' 错误
why is there NO 'l-value required' error while incrementing array name
据我所知,您不能修改数组变量
为什么这段代码运行没有任何错误。
有什么我在这里遗漏的吗? (这不是关于为什么有 'L-VALUE REQUIRED' 错误,而是关于为什么没有。)
#include<stdio.h>
int strlens(char *s);
void main(){
char s[]="get me length of this string ";
// s++ ; this would give 'L-VALUE REQUIRED ERROR'
printf("%d",strlens(s));
}
int strlens(char s[]){
int i;
for(i=0; *s!='[=11=]';++i, ++s) ; //++s: there is NO 'L-VALUE REQUIRED ERROR'
return i;
}
C 语言的一个怪癖是数组是 "pass by reference",对于经验丰富的 C 程序员来说是众所周知的,但却让新的 C 程序员无所适从。一般而言,大多数表达式中使用的数组名称将 "decay" 指向其第一个元素的地址。函数将其用于极端情况,函数参数中的数组语法实际上是指针类型本身的别名。
这在 c11 的 §6.7.6.3 的第 7 段中有描述 函数声明符:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [
and ]
of the
array type derivation.
从历史上看,这个怪癖是为了保持与 C 的前身 B 和 BCPL 的行为兼容性以及高效的结构布局。 C 的前身具有数组语义,因为它的物理布局实际上是一个在运行时动态分配和初始化的指针。当传递给一个过程时,指针语义是一种自然的采用。 Dennis Ritchie 发明了允许数组语法表示数组实际地址的概念,然后在传递给函数时保持指针语义。因此,C 语言的发明者认为这个怪癖是解决现实世界问题(语义兼容性)的新方法。
这一行
char s[]="get me length of this string ";
定义一个字符数组。 s
不是一个指针,它求值为一个地址(例如,当提供给一个指针时,或者当访问一个像 s[i]
等同于 *(s+i)
的值时),或者表示 space被数组占用(eg in sizeof(s)
)
但是在这样的函数签名中
int strlens(char s[]){
char s[]
等同于 char *s
,您可以将 s
视为指针。
char arr[] = "asds"
在这里,arr
只是一个名字。它指的是内存位置,但不是指针。在使用 arr
的地方,编译器会直接替换地址。它不是指针,因为与指针不同,它没有分配任何 space 来存储地址。它只是一个编译时符号。因此,在 运行 时间没有任何东西可以进行指针运算。如果你必须增加一些东西,那东西应该存在于 运行 时间。
更多详情:
基本上,文字 "asds" 存储在您的可执行文件中,编译器知道它的确切位置(好吧,编译器将它放在可执行文件中,所以它应该知道?)。
标识符 arr
只是该位置的名称。例如,arr
不是指针,即:它 不存在于存储地址 的内存中。
void func(char arr[])
在函数参数的情况下,arr
在 运行 时确实存在于内存中,因为参数在进行函数调用之前被压入调用堆栈。由于数组是通过引用传递的,因此实际参数的第一个元素的地址被压入调用堆栈。
因此,arr
在堆栈上分配了一些 space,它将地址存储到实际数组的第一个元素。
现在你有了指针。因此,您可以递增(或对其进行任何指针运算)。
据我所知,您不能修改数组变量
为什么这段代码运行没有任何错误。 有什么我在这里遗漏的吗? (这不是关于为什么有 'L-VALUE REQUIRED' 错误,而是关于为什么没有。)
#include<stdio.h>
int strlens(char *s);
void main(){
char s[]="get me length of this string ";
// s++ ; this would give 'L-VALUE REQUIRED ERROR'
printf("%d",strlens(s));
}
int strlens(char s[]){
int i;
for(i=0; *s!='[=11=]';++i, ++s) ; //++s: there is NO 'L-VALUE REQUIRED ERROR'
return i;
}
C 语言的一个怪癖是数组是 "pass by reference",对于经验丰富的 C 程序员来说是众所周知的,但却让新的 C 程序员无所适从。一般而言,大多数表达式中使用的数组名称将 "decay" 指向其第一个元素的地址。函数将其用于极端情况,函数参数中的数组语法实际上是指针类型本身的别名。
这在 c11 的 §6.7.6.3 的第 7 段中有描述 函数声明符:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the
[
and]
of the array type derivation.
从历史上看,这个怪癖是为了保持与 C 的前身 B 和 BCPL 的行为兼容性以及高效的结构布局。 C 的前身具有数组语义,因为它的物理布局实际上是一个在运行时动态分配和初始化的指针。当传递给一个过程时,指针语义是一种自然的采用。 Dennis Ritchie 发明了允许数组语法表示数组实际地址的概念,然后在传递给函数时保持指针语义。因此,C 语言的发明者认为这个怪癖是解决现实世界问题(语义兼容性)的新方法。
这一行
char s[]="get me length of this string ";
定义一个字符数组。 s
不是一个指针,它求值为一个地址(例如,当提供给一个指针时,或者当访问一个像 s[i]
等同于 *(s+i)
的值时),或者表示 space被数组占用(eg in sizeof(s)
)
但是在这样的函数签名中
int strlens(char s[]){
char s[]
等同于 char *s
,您可以将 s
视为指针。
char arr[] = "asds"
在这里,arr
只是一个名字。它指的是内存位置,但不是指针。在使用 arr
的地方,编译器会直接替换地址。它不是指针,因为与指针不同,它没有分配任何 space 来存储地址。它只是一个编译时符号。因此,在 运行 时间没有任何东西可以进行指针运算。如果你必须增加一些东西,那东西应该存在于 运行 时间。
更多详情:
基本上,文字 "asds" 存储在您的可执行文件中,编译器知道它的确切位置(好吧,编译器将它放在可执行文件中,所以它应该知道?)。
标识符 arr
只是该位置的名称。例如,arr
不是指针,即:它 不存在于存储地址 的内存中。
void func(char arr[])
在函数参数的情况下,arr
在 运行 时确实存在于内存中,因为参数在进行函数调用之前被压入调用堆栈。由于数组是通过引用传递的,因此实际参数的第一个元素的地址被压入调用堆栈。
因此,arr
在堆栈上分配了一些 space,它将地址存储到实际数组的第一个元素。
现在你有了指针。因此,您可以递增(或对其进行任何指针运算)。