2012-08-31 31 views
2

我有这样的代码:无法理解为什么这种行为


    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移动到下一个字符串。任何人都可以解释我的内裤缺乏吗?

回答

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,只是暂时隐藏它。

+0

请参阅我对@ Kerrek的回复的评论。 – artaxerxe

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 */ 
+0

是的,我完全同意。我只是想强调,'我'需要是一个可修改的左值才能执行此操作。注意:我已将其删除。 – md5

1

首先,请确保您完全满意以下事实:数组不是指针。

二,什么是name?数组衰减成为指向第一个元素的指针。衰变后,表达式name的类型为char **(指向数组char*的第一个元素,然而,衰减的表达式是的右值。您无法修改它!当然,因为修改指针是没有意义的一个指针到固定阵列。

因此,不能直接递增指针,它是衰减name,不超过可以说++5++foo()(其中foo返回由值基本类型) [结果哎呀,那是一个让步于C++的让步]

什么你可以说的是:

char ** np = name; 
++np; 
printf("%s", *np); 

这与印刷name[1]同样的效果,但现在你也有具有指向第二个数组元素的变量。

+0

“腐烂的表情是一个右值” - 或用C标准术语,它是“表达的结果”。愚蠢的C标准术语。同样,在C中,函数调用'foo()'不能返回一个左值。 –

+0

即使是我的代码,我执行你的代码后,我会得到二进制字符串,如下所示:$ ' * @ 而不是'“b2”'。什么解释? – artaxerxe

+0

@artaxerxe:我无法重现:http://ideone.com/S63K7。也许你运行的代码还有其他问题。 –

1

虽然名称指向第一个元素的地址,但名称不是type char *,而是char *[4]。因此,sizof(name) == sizeof(char *)*4

增加一个指针总是意味着添加它指向的数据的大小。所以,增加后,它指向整个阵列后面。就像如果你有

int i, a; 
    int *p = &i; 
    p++; 

P中就指向背后我。它会指向一个,如果编译器决定把我放在后面。

另请注意,您的数组只包含4个指针,而不是4个字符串。像上面那样,这些字符串实际上就是编译器的选择。所以,第一个字符串的结尾不一定要在第二个字符串的开头。特别是如果您稍后将其他值(字符串地址)分配给名称[1]。因此,您可以将名称转换为char **,但不应将其转换为char *。增加前者会指向第二个元素(第二个char *指针)。