gcc 4.4.4
我做错了什么?
char x[10];
char y[] = "Hello";
while(y != NULL)
*x++ = *y++;
非常感谢任何建议。
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;
*(x+i)
和 x[i]
这两种形式? - cafargv
不是声明为char *argv[]
吗(与这些例子中的char buf[]
不同)?这样,只会修改相对于该数组的指针而不是数组本身的基地址。难道不是这样的吗? - user966939char *px = x;
char *py = y;
之后,您可以尽情递增这些指针。
char myString[]
这样在栈上分配的数组是不可变的,因为你无法修改指针的地址。但这并不改变它们仍然是指向位于堆栈中的一块内存开头的指针这一事实。所以当你说“数组不是指针”时,你直接与理解C语言必要的基本实现细节相矛盾。 - Braden Bestprintf("%p", myString)
会输出什么?噢,对了,是一个内存地址。因为p
代表指针。由于它们被实现为指针,所以当你将一个数组送入一个期望得到指针的函数中时不会引发类型错误,即使是在加了-Wall -pedantic
编译选项的C++编译器中也是如此。为什么有人会认为它是一个指针?可能是因为它就是一个指针。也可能是因为 *myString
解引用地址并返回指针所指向的值。指针是矩形而数组是正方形。所有数组都是指针,但并非所有指针都是数组。它们互不排斥。 - Braden Bestarray[n]
是语法糖,表示*(pointer + n)
。你可能会惊讶地发现,在你的幻想中一切都是高级的,机器码不存在的世界里,n[array]
将返回完全相同的结果,并且在使用了-Wall -pedantic
参数编译的C或C++编译器中不会产生任何错误或警告。所有实践中观察到的证据都指向数组是指针的语法糖。唯一的矛盾是,一个数组的地址不能在定义它的作用域内被修改,但这并不意味着什么,因为它仍然可以被传递到另一个函数中。 - Braden Bestprintf
正确地打印了内存地址,因为在 C 语言中,数组 myString
在这个上下文中立即被称为所谓的数组转指针转换(又称“数组类型衰减”)。该转换的临时结果 - 实际上是一个指针 - 被发送到 printf
而不是数组。对于你的所有小实验都适用相同的规则,正如我之前说过的,它们都已经过度探讨了。我给你的 FAQ 链接解释了所有这些。 - AnT stands with Russiaint a;
也是一个指针吗?你是在说每个struct
对象都是一个指针吗?基本上,这种逻辑会让我们得出结论:C中的每个对象和每个函数都是“真正的指针”。抱歉,虽然这在汇编语言术语中可能成立,但这与C毫无关系。在C语言中,内存中的对象称为lvalues。Lvalue是你所谈论的东西的正确C术语。“Lvalue”和“pointer”在C中是两个完全不同的概念。不要混淆它们。而且,在C中,数组不是指针。 - AnT stands with Russiaint arr[] = {1, 2, 3};
// arr is declared as const pointer.
(arr + 1)
是可能的,但是 arr++
不可能,因为 arr
是常量,不能存储另一个地址。
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 Bestx
和y
是数组,而不是指针。
在大多数表达式上下文中,例如您的递增表达式中,它们会退化为指针,但它们会退化为rvalue,而不是lvalue,并且只能将递增运算符应用于lvalue。
大多数情况下,数组就像一个指针。
请记住,您不能修改数组!
y++
相当于 y = y + 1
。
char y[] = "Hello";
当你执行 y++
时,实际上是修改了数组!
这将产生错误:error: lvalue required as increment operand
。
数组是一段静态连续分配的内存块。数组名称是对第一个内存块的不可变引用。试图增加地址(由数组名称引用)将导致其他内存位置的丢失(更好地说是取消引用)。 假设我们有一个数组
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).
数组名不能被修改,但是在 f(int argv[])
中的 argv++
呢?
引用自 K&R 的 p99,“数组名不是一个变量;像 a = pa
和 a++
这样的构造是非法的”,这说明数组名是初始元素位置的同义词。
但是为什么在函数参数 func(char *argv[])
中,我们可以对 argv
进行 argv++
操作,尽管它是数组名。
而在 int *a[10]
中,我们不能像 argv++
那样执行 a++
操作。
数组名是初始元素位置的同义词。---K&R
arrayname++
是非法的。
在函数参数中,例如 char *argv[]
,它和 char **argv
相同。参数中的 type *arrayname_para[]
是另一种 type **arrayname_para
的同义词。
由于您已将x
和y
都定义为数组,因此无法修改它们。一个可能的解决方案是改用指针:
char x[10];
char *xx = x;
char *y = "Hello";
while (*y != '\0')
*xx++ = *y++;
NULL
。数组是常量指针。我们不能更改它们。
int q;
int *const p = &q;
p = NULL; // this is not allowed.