为啥在增加数组名称时没有“需要左值”错误[重复]

Posted

技术标签:

【中文标题】为啥在增加数组名称时没有“需要左值”错误[重复]【英文标题】:why is there NO 'l-value required' error while incrementing array name [duplicate]为什么在增加数组名称时没有“需要左值”错误[重复] 【发布时间】:2017-12-11 07:20:26 【问题描述】:

据我所知,你不能修改数组变量

这段代码怎么运行没有任何错误。 有什么我在这里想念的吗? (这不是关于为什么会有 '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!='\0';++i, ++s) ; //++s:  there is NO 'L-VALUE REQUIRED ERROR'
    return i;



【问题讨论】:

s 不是strlens 中的数组名,它是一个指针。数组和指针是不同的类型。 当您执行char s[] 时,s 是一个名称(编译时符号;没有运行时意义)。它没有分配任何空间,因此不存储地址。如果要进行指针运算,则需要将地址存储在某个地方,不是吗?在函数参数的情况下,您的原始字符数组的地址被压入堆栈,您的函数参数s 指的是那个。因此,函数参数s 为其分配了存储地址的空间。您现在可以进行指针运算,因为您可以对其进行运算。 @Yashas:“数组参数表现为指针”:它们不只是“表现为指针”,它们指针。 错误信息具有误导性。 smain 中的左值。它不是一个可修改的左值。 明确说明这一点:int strlens(char s[]); 100% 等效int strlens(char * s); 它是可互换的,编译器应该创建 exact两者都有一些代码。 【参考方案1】:

对于经验丰富的 C 程序员来说,C 语言的一个怪癖是数组是“通过引用传递”的,但它却让新的 C 程序员无计可施。一般来说,大多数表达式中使用的数组名称会“衰减”到其第一个元素的地址。函数将这种情况带到了一个极端情况,其中函数参数中的数组语法实际上是指针类型本身的别名。

这在 c11 的 §6.7.6.3 函数声明符的第 7 段中有所描述:

将参数声明为“类型的数组”应调整为“限定指针 type'',其中类型限定符(如果有)是在 [] 的 数组类型推导。


从历史上看,这个怪癖是为了保持与 C 的前辈 B 和 BCPL 的行为兼容性以及高效的结构布局。 C 的前身具有数组的语义,因为它的物理布局实际上是一个在运行时动态分配和初始化的指针。当传递给一个过程时,指针语义是一种自然的采用。 Dennis Ritchie 发明了允许数组语法表示数组实际地址的概念,然后在传递给函数时保持指针语义。因此,C 语言的发明者认为这个怪癖是解决现实世界问题(语义兼容性)的新方法。

参考资料: The Development of the C Language

【讨论】:

数组是“通过引用传递”——你的意思当然是正确的,标准引用是相关的,但我还是不想这么说方式 - C 中没有“按引用传递”,但指针允许程序员“模拟”它。因此,我会更好地说明数组只是 根本无法传递 以及表达式评估和类型调整的规则(通常一起称为“数组 decay 到指针”,其中也不在标准中)帮助“轻松”传递指针。 我认为更精确的术语将通过指针/地址传递。 @FelixPalmen:这些引号是为了表明我没有按字面意思使用这个词。然而,“指针是引用”的概念是从 Ritchie 的论文 C 语言的发展 中传授给我的。 Decay 是我在精通 C 时学到的一个术语。我可能在 C-faq 中看到了它。它也应该用引号引起来,我已经进行了编辑。 同意@FelixPalmen 数组不是通过引用传递的。如果是,则意味着更改被调用者中的“数组引用”将更改调用者中的数组。即数组实际上是内存中的不同位置。正确的一点是根本不传递数组。 @JeremyP:这种解释(数组实际上是内存中的不同位置)并不是对更改通过引用传递的函数参数意味着什么的通常解释。您将指针参数混为一谈,就好像指针对象本身就是通过引用传递的一样。 Dennis Ritchie 试图模拟数组的引用传递以避免堆栈负担。【参考方案2】:

这一行

char s[]="get me length of this string ";

定义一个字符数组。 s 不是一个指针,它计算为一个地址(例如,当提供给一个指针时,或者当访问像 s[i] 这样的值时,相当于*(s+i)),或者表示数组占用的空间(例如在@ 987654325@)

但是在这样的函数签名中

int strlens(char s[])

char s[]等价于char *s,您可以将s 视为指针。

【讨论】:

【参考方案3】:

char arr[] = "asds"

在这里,arr 只是一个名称。它指的是内存位置,但不是指针。编译器直接替换使用arr 的地址。它不是指针,因为与指针不同,它没有分配任何空间来存储地址。它只是一个编译时符号。因此,在运行时没有什么可以做指针运算的。如果您必须增加某些内容,那么该内容应该在运行时存在。

更多详情:

基本上,文字“asds”存储在您的可执行文件中,编译器知道它的确切位置(好吧,编译器将它放在可执行文件中,所以它应该知道?)。

标识符arr 只是该位置的名称。如,arr 不是指针,即:它不存在于存储地址的内存中


void func(char arr[])

在函数参数的情况下,arr 在运行时确实存在于内存中,因为参数在进行函数调用之前被压入调用堆栈。由于数组是通过引用传递的,因此实际参数的第一个元素的地址被压入调用堆栈。

因此,arr 在堆栈上分配了一些空间,用于存储实际数组第一个元素的地址。

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

【讨论】:

“像一个指针”->它一个指针,句号。 "它没有为它分配任何空间",因为这句话是错误的。 arr 很好地“使用”内存,即 4+1 chars. @alk 我更新了它,但看起来不太好。我不知道如何把它变成一个简单的正确句子 这样更好。顺便说一句,我没有 DVed。

以上是关于为啥在增加数组名称时没有“需要左值”错误[重复]的主要内容,如果未能解决你的问题,请参考以下文章

为啥名称在我的数组中重复,我将如何获得一个 txt 文件进行排序

为啥在检查 null 的长度时我没有收到错误 [重复]

为啥循环时数组大小不增加? [复制]

即使在正确转换之后,增加 void 指针也需要左值

为啥我不能在我的结构中打印名称?

使用 C++ 时需要左值作为赋值错误的左操作数