为什么递增数组名称时没有 '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 语言的发明者认为这个怪癖是解决现实世界问题(语义兼容性)的新方法。

参考资料: The Development of the C Language

这一行

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,它将地址存储到实际数组的第一个元素。

现在你有了指针。因此,您可以递增(或对其进行任何指针运算)。