void foo(void *ptr, int numBytes)
{
(char*)ptr += numBytes;
}
这在C语言中无法编译通过。我知道替代方案,但为什么这段代码不能工作呢?问题出在哪里?
void foo(void *ptr, int numBytes)
{
(char*)ptr += numBytes;
}
这在C语言中无法编译通过。我知道替代方案,但为什么这段代码不能工作呢?问题出在哪里?
问题
问题在于(char*)ptr
将不会产生一个lvalue,这意味着该值无法被修改——可以将其视为转换的临时结果,转换将产生类型为char*
的rvalue。
从语义上讲,这与下面的示例相同,转换会产生一个临时值,这样的值不能被赋予新值。
int x = 123;
(float)x += 0.12f; /* (1), illegal */
/* ^-- sementically equivalent to `123.f += 0.12f` */
解决方案
在你的问题中,你已经提到了一个解决该问题的方法,但我想明确地写出解决方案,以展示如何修改ptr
的值,即使转换得到的值是不可修改的。
char*
类型来修改这个左值,就像原始的void*
是char*
类型一样。
*((char**)&ptr) += numbytes; // make `ptr` move forward `numbytes`
注意: 当解引用一个指针时,你会得到一个左值(lvalue),否则将无法改变存储在指针地址中的指向值(pointed to value)的值。那是因为(char*)ptr
不是左值。
尝试使用以下代码:
void foo(void *ptr, int numBytes)
{
char* p = (char*)ptr;
p += numBytes;
}
更新
可以在cppreference.com上找到有关各种值类型的简要说明。这篇文章讨论了C++中的值类型,但核心思想也适用于C。
对于本文的讨论,
左值是指标识非临时对象或非成员函数的表达式。
您可以获取左值的地址并将其分配给不同的值。
例如:
int i;
int* p = &i;
i = 20;
int* p = &42;
42 = 53;
char* p = (char*)ptr;
从 (char*)ptr
创建了一个 lvalue (p
)。 因此,可以使用:
p += numBytes;
=
的左侧,您需要一个 lvalue
。但是 (char *)ptr
不是一个lvalue。
5 += 7
失败。 - milleniumbugvoid *
上进行算术运算(将其视为与此目的同义的char *
),但这是标准的扩展,更常意外使用而非有意使用。避免在void *
上进行算术运算! - Jonathan Lefflerptr += numBytes;
对于除GCC(以及可能是在其GCC兼容模式下的clang)之外的编译器都不可接受,而不是为什么强制转换无法正常工作。这些信息是正确的,但略微偏离主题。 - Jonathan Leffler