需要左值作为递增操作数

36

gcc 4.4.4

我做错了什么?

char x[10];
char y[] = "Hello";
while(y != NULL)
    *x++ = *y++;

非常感谢任何建议。


21
数组不是指针。 - Michael Foukarakis
10个回答

41

x++x = x + 1 的简写形式。然而,x 在这里是一个数组,你不能修改数组的地址。你的变量 y 也是如此。

不要试图增加数组,你可以声明一个整数 i 并对其进行递增,然后访问数组的第 i 个索引。

char x[10], y[5] = "Hello";
int i = 0;
while (y[i] != 0)
{
    x[i] = *y[i];
    i++;
}
x[i] = 0;

5
为什么你会在同一个代码里混用 *(x+i)x[i] 这两种形式? - caf
@caf 为了展示它们之间的关系。 - John Roberts
为什么可以执行argv++操作?argv有什么特殊之处? - David
@David,我自己也是C语言的初学者,所以我的理解可能有误,但argv不是声明为char *argv[]吗(与这些例子中的char buf[]不同)?这样,只会修改相对于该数组的指针而不是数组本身的基地址。难道不是这样的吗? - user966939
应该不是 while(y[i] != '\0') 吧? - Alex
显示剩余2条评论

34
很可能你受到了一个普遍的误解,即“数组是指针”,也就是说,在定义数组时,实际上得到的是指向某些内存块的普通指针。在你的代码中,你试图增加那个指针。
代码不“起作用”的原因是因为实际上数组不是指针。数组就是数组。C 语言中没有“增加数组”的操作。事实上,C 中的数组本身都是不可修改的左值(non-modifiable lvalues)。在 C 中没有操作能够修改数组本身(只有单独的元素可以是可修改的)。
如果你想要使用“滑动指针”技术来遍历你的数组(这正是你所试图做的),你需要显式地创建指针,并使它们指向你的数组的起始元素。
char *px = x;
char *py = y;

之后,您可以尽情递增这些指针。


6
在“实际情况”中,数组是指针。的确,像char myString[]这样在栈上分配的数组是不可变的,因为你无法修改指针的地址。但这并不改变它们仍然是指向位于堆栈中的一块内存开头的指针这一事实。所以当你说“数组不是指针”时,你直接与理解C语言必要的基本实现细节相矛盾。 - Braden Best
4
好的,那么printf("%p", myString)会输出什么?噢,对了,是一个内存地址。因为p代表指针。由于它们被实现为指针,所以当你将一个数组送入一个期望得到指针的函数中时不会引发类型错误,即使是在加了-Wall -pedantic编译选项的C++编译器中也是如此。为什么有人会认为它是一个指针?可能是因为它就是一个指针。也可能是因为 *myString 解引用地址并返回指针所指向的值。指针是矩形而数组是正方形。所有数组都是指针,但并非所有指针都是数组。它们互不排斥。 - Braden Best
4
array[n]是语法糖,表示*(pointer + n)。你可能会惊讶地发现,在你的幻想中一切都是高级的,机器码不存在的世界里,n[array]将返回完全相同的结果,并且在使用了-Wall -pedantic参数编译的C或C++编译器中不会产生任何错误或警告。所有实践中观察到的证据都指向数组是指针的语法糖。唯一的矛盾是,一个数组的地址不能在定义它的作用域内被修改,但这并不意味着什么,因为它仍然可以被传递到另一个函数中。 - Braden Best
2
@Braden Best:不是的。你的 printf 正确地打印了内存地址,因为在 C 语言中,数组 myString 在这个上下文中立即被称为所谓的数组转指针转换(又称“数组类型衰减”)。该转换的临时结果 - 实际上是一个指针 - 被发送到 printf 而不是数组。对于你的所有小实验都适用相同的规则,正如我之前说过的,它们都已经过度探讨了。我给你的 FAQ 链接解释了所有这些。 - AnT stands with Russia
2
那么,你是在说int a;也是一个指针吗?你是在说每个struct对象都是一个指针吗?基本上,这种逻辑会让我们得出结论:C中的每个对象和每个函数都是“真正的指针”。抱歉,虽然这在汇编语言术语中可能成立,但这与C毫无关系。在C语言中,内存中的对象称为lvaluesLvalue是你所谈论的东西的正确C术语。“Lvalue”和“pointer”在C中是两个完全不同的概念。不要混淆它们。而且,在C中,数组不是指针。 - AnT stands with Russia
显示剩余14条评论

15
在C语言中,数组确实是指针,但是是常量指针,这意味着在声明后它们的值不能被改变。
int arr[] = {1, 2, 3};
// arr is declared as const pointer.

(arr + 1) 是可能的,但是 arr++ 不可能,因为 arr 是常量,不能存储另一个地址。


6
char x[10];
char y[] = "Hello";
char *p_x = &x[0];
char *p_y = &y[0];
while(*p_y != '\0') *p_x++ = *p_y++;

由于您无法修改数组地址(在您的代码中通过x++y++完成),而且您可以修改指针地址,因此我将数组地址复制到单独的指针中,然后递增它们。

如果您想要的话,我相信您可以减少符号,但是我希望您能理解重点。


char *p_y = &y[0]; 而不是 char *p_y = y;?感觉有点多余。 - Braden Best

5

xy是数组,而不是指针。

在大多数表达式上下文中,例如您的递增表达式中,它们会退化为指针,但它们会退化为rvalue,而不是lvalue,并且只能将递增运算符应用于lvalue。


3

大多数情况下,数组就像一个指针。

请记住,您不能修改数组!

y++ 相当于 y = y + 1

char y[] = "Hello";

当你执行 y++ 时,实际上是修改了数组!

这将产生错误:error: lvalue required as increment operand


2

数组是一段静态连续分配的内存块。数组名称是对第一个内存块的不可变引用。试图增加地址(由数组名称引用)将导致其他内存位置的丢失(更好地说是取消引用)。 假设我们有一个数组

int p[]={10,20,30}

我将为您翻译关于编程的内容。在这里,p(根据设计)指的是索引0。假设我们在某个地方递增p:p++
假设允许增量,则会导致p指向索引1。现在考虑p[1]=?
p[1]将转换为取消引用当前p位置右侧一位的值

或原始数组的位置p [2]
重复此操作,很快跟踪数组索引就会变得非常麻烦。
因此,为了避免这种不幸的事情,数组名称(lvalues)地址是不可变的。 另一个概念是字寻址和字节寻址:-

C语言中的字符数组(字符串)是按字节寻址而不是按字寻址。所以如果我们有一个数组

int a[]={10,20,30}

printf("%d",*(a+1)) //Ouput:20 (int is word addressable)
printf("%d",*(++a)) // Error: Array references are immutable

int* cpya=a;
printf("%d",*(++cpya)) /* Output:20 (cpy is not array reference but a copy. Hence is Mutable) */

char b[]="Hello"
printf("%c",*(++b)) // Error : Array references are immutable
printf("%c",*(b+1)) /* undefined_behavior as character Array(strings) are byte addressable and not word addressable */
printf("%c",*(b+sizeof(char)*1)) // Output :e (It's byte addressable).

B编程语言

2

数组名不能被修改,但是在 f(int argv[]) 中的 argv++ 呢?

引用自 K&R 的 p99,“数组名不是一个变量;像 a = paa++ 这样的构造是非法的”,这说明数组名是初始元素位置的同义词。

但是为什么在函数参数 func(char *argv[]) 中,我们可以对 argv 进行 argv++ 操作,尽管它是数组名。

而在 int *a[10] 中,我们不能像 argv++ 那样执行 a++ 操作。

数组名是初始元素位置的同义词。---K&R

arrayname++ 是非法的。

在函数参数中,例如 char *argv[],它和 char **argv 相同。参数中的 type *arrayname_para[] 是另一种 type **arrayname_para 的同义词。


2

由于您已将xy都定义为数组,因此无法修改它们。一个可能的解决方案是改用指针:

char x[10];
char *xx = x;

char *y = "Hello";

while (*y != '\0')
    *xx++ = *y++;

请注意,我还修复了您的终止条件 - 指针不会仅仅因为到达字符串的末尾而变为NULL

我想,nul比null好。 - ant2009

0

数组是常量指针。我们不能更改它们。

int q;
int *const p = &q;
p = NULL; // this is not allowed.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接