为什么我不能对void指针的转换进行算术运算?

4
void foo(void *ptr, int numBytes)
{
    (char*)ptr += numBytes;
}

这在C语言中无法编译通过。我知道替代方案,但为什么这段代码不能工作呢?问题出在哪里?


3
同样的原因,为什么 5 += 7 失败。 - milleniumbug
1
因为您没有使用GCC。更准确地说,C标准禁止它,因为指向的对象大小未知且指针算术运算依赖于对象的大小。您也不能增加函数指针。GCC允许在void *上进行算术运算(将其视为与此目的同义的char *),但这是标准的扩展,更常意外使用而非有意使用。避免在void *上进行算术运算! - Jonathan Leffler
1
实际上,我的先前评论(感谢上帝它只是一条评论)大多与问题无关;它详细说明了为什么ptr += numBytes;对于除GCC(以及可能是在其GCC兼容模式下的clang)之外的编译器都不可接受,而不是为什么强制转换无法正常工作。这些信息是正确的,但略微偏离主题。 - Jonathan Leffler
3个回答

5

问题

问题在于(char*)ptr将不会产生一个lvalue,这意味着该值无法被修改——可以将其视为转换的临时结果,转换将产生类型为char*rvalue

从语义上讲,这与下面的示例相同,转换会产生一个临时值,这样的值不能被赋予新值。

int x = 123;

(float)x += 0.12f; /* (1), illegal                                     */
                   /*  ^-- sementically equivalent to `123.f += 0.12f` */

解决方案

在你的问题中,你已经提到了一个解决该问题的方法,但我想明确地写出解决方案,以展示如何修改ptr的值,即使转换得到的值是不可修改的。

  1. 指向void的指针的地址。
  2. 将这个地址强制转换为指向char指针的指针
  3. 对指针进行引用操作,得到指向char的指针
  4. 按照char*类型来修改这个左值,就像原始的void*char*类型一样。

*((char**)&ptr) += numbytes; // make `ptr` move forward `numbytes`
注意: 当解引用一个指针时,你会得到一个左值(lvalue),否则将无法改变存储在指针地址中的指向值(pointed to value)的值。

4

那是因为(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;

相比之下,
一个prvalue(“pure” rvalue)是指标识临时对象(或其子对象)的表达式,或者是与任何对象都没有关联的值。
字面值42是一个rvalue。你不能这样写:
int* p = &42;
42 = 53;

在这行中,
    char* p = (char*)ptr;

(char*)ptr 创建了一个 lvalue (p)。 因此,可以使用:

    p += numBytes;

你能再详细解释一下吗?对于一个初学者来说,这两个看起来是一样的。它只是看起来你使用了一个临时中间值,然后它就神奇地工作了。 - Mr. Llama
这个答案中有一个误导性的链接指向C++语言参考资料,并且还提到了“成员函数”,这在C语言中是不存在的。不要混淆或让其他人混淆C和C++,这是有害的。 - The Paramagnetic Croissant

3
< p>在 = 的左侧,您需要一个 lvalue 。但是 (char *)ptr 不是一个lvalue。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接