无法理解为什么那种行为

时间:2012-08-31 11:51:05

标签: c arrays pointers char

我有这段代码:


     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}}移动到下一个字符串。任何人都可以解释一下我的不足之处在哪里?

4 个答案:

答案 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() (其中foo按值返回基本类型) [whoops,这是对C ++的让步]

可以说的是:

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 *指针)。