我有这段代码:
char *name[] = { "a1", "b2", "c3", "d4" };
printf("%s\n", *name); //the critical line
与critical line
相关:
在此表单中,输出很简单:a1
。
如果我将critical line
替换为:
printf("%s\n", ++*name);
然后输出为1
。我想到现在一切都很好。
考虑到name
是指向第一个字符串的指针"a1"
,我将critical line
替换为:
printf("%s\n", ++name);
希望我得到"b2"
结果作为输出。但是我得到了这个错误:
../src/test.c:32: error: lvalue required as increment operand
。
问题:我无法理解为什么++*name
是合法的 - name
是指向第一个字符串的指针 - 而++name
则不是。++name
。在我看来,name
应该将{{1}}移动到下一个字符串。任何人都可以解释一下我的不足之处在哪里?
答案 0 :(得分:3)
当您编写++name
时,数组 name
将转换为指针到数组的第一个元素。此转换的结果不是左值,并且无法使用++
或其他方式进行修改。你可以改为写name+1
,这会打印正确的东西。当name
是数组时,无法修改它以引用除该数组[*]之外的任何内容。
还要考虑:
char **p = name; // OK, `name' converts to pointer
++p; // OK, `p' is an lvalue
++(p+1); // not OK, `p+1' is not an lvalue
++((char**)p); // not OK, `(char**)p' is not an lvalue
++*name; // OK, `*name' is an lvalue
粗略地说,"左值"是一个引用一个对象的表达式,而一个"不是一个左值"是一个有值的表达式。对象和值之间的区别在于对象是用于存储值的位置(一次一个值)。永远不能修改值,对象有时可以。
每当你有一个子表达式是一个左值但是需要当前值时,就会读取/加载对象/你想要调用它的任何东西。在C ++中,这被称为"左值转换",我不记得它是否在C中调用任何东西而不是"评估子表达式"。
[*]你可以用内部范围中的另一个变量name
来遮蔽它,它引用其他东西。但是,这仍然没有修改外部name
,只是暂时隐藏它。
答案 1 :(得分:2)
name
是一个数组,因此,除非您将其用作sizeof
或&
运算符的操作数,否则它将被计算为指向数组对象的初始成员的指针并且不是左值。
因此,您无法使用name
之类的运算符直接修改++
(请记住,后缀增量运算符需要可修改的左值作为操作数)。否则,您可以使用临时指针(以下示例中的p
)。
#include <stdio.h>
const char *name[] = { "a1", "b2", "c3", "d4" };
const char **p = name;
printf("%s\n", *p); /* Output: a1 */
*++p; /* or *p++ */
printf("%s\n", *p); /* Output: b2 */
答案 2 :(得分:1)
首先,请确保您对以下事实感到非常满意和自信:数组不是指针。
第二,name
中的内容是什么?数组衰减成指向第一个元素的指针。衰减后,表达式name
的类型为char **
(指向char*
数组的第一个元素的指针。但是,衰减的表达式是 rvalue 。你不能修改它!当然,因为修改指向固定数组的指针是没有意义的。
因此,你不能直接递增衰减name
结果的指针,只能说++5
或++foo()
(其中 [whoops,这是对C ++的让步] 。foo
按值返回基本类型)
你可以说的是:
char ** np = name;
++np;
printf("%s", *np);
这与打印name[1]
具有相同的效果,但现在您还有一个包含指向第二个数组元素的指针的变量。
答案 3 :(得分:1)
虽然name指向第一个元素的地址,但name不是type char *
,而是char *[4]
。因此,sizof(name) == sizeof(char *)*4
增加指针总是意味着添加它指向的数据的大小。因此,在递增之后,它将指向整个数组。就像你有
一样 int i, a;
int *p = &i;
p++;
p现在指向后面 i。它将指向a,如果编译器决定放弃i。
另请注意,您的数组只包含4个指针,而不是4个字符串。如上所述,这些字符串实际上是编译器的选择。因此,第一个字符串的结尾不一定在第二个字符串的开头旁边。特别是如果您稍后将其他值(字符串地址)分配给名称[1]。因此,您可以将名称转换为char **
,但不应转换为char *
。增加前者将指向第二个元素(第二个char *指针)。