问题描述
使用指针构建和访问通用数据结构 元素的大小不是固定的(因此不能 使用结构)。
有趣的点在于增加 void *
指针
被强制转换为 int *
以指向下一个 int。
// I want to assign -1 to the two int elements the memory
// is pointing to
*(int *) next++ = -1;
*(int *) next = -1;
可能有不止一个错误混淆运算符 优先级并假设要完成的增量......无论如何......
即使拆分操作也不会递增:
*(int *) next = -1;
(int *)next++; // does increment by one,maybe ++ comes before casting?
*(int *) next = -1;
工作示例是:
*(int *) next = -1;
next = (int *)next + 1; // this increments by 4 as wanted
*(int *) next = -1;
但这让我有点困惑,因为我看不出有太大区别 所以我想我遗漏了一些语法规则 也许你们中的一些人可以给我一个提示!
解决方法
有趣的一点是递增 void *
指针,该指针被强制转换为 int *
以指向下一个 int。
您必须了解的第一件事是,标准 C(或 C++)不会在指向 void
或其他不完整类型的指针上定义增量或其他算术。这与指针算术根据指向对象的大小/类型定义的事实一致,如果这些对象的类型不完整(就指针算术操作数的类型而言),则存在没有足够的信息来使用该指针执行算术运算。
某些编译器,显然包括您的编译器,实现了将指针运算处理为 void *
类型的扩展,就好像指针具有 char *
类型一样。有时这就是人们想要的,但有时却不是。我通常建议避免对此类扩展的任何依赖,并确保启用任何必要的编译器选项,以便在编译时诊断其使用情况。
第一个错误的方法是:
// I want to assign -1 to the two int elements the memory
// is pointing to
*(int *) next++ = -1;
*(int *) next = -1;
这是错误的,因为 next++
确实增加了 1。
是的,后缀 ++
的优先级高于强制转换运算符,因此 *(int *) next++ = -1
被解释为 *(int *)(next++) = -1
。如果编译器接受,那么它是由于我上面描述的扩展,所以 next
递增以指向下一个 char
,而不是下一个 int
。
即使拆分操作也不会递增:
*(int *) next = -1;
(int *)next++; // does increment by one,maybe ++ comes before casting?
*(int *) next = -1;
是的,您在那里遇到了与之前完全相同的运算符优先级问题,但另外,(int *)next++
中的强制转换是无用的,因为结果未被使用。
工作示例是:
*(int *) next = -1;
next = (int *)next + 1; // this increments by 4 as wanted
*(int *) next = -1;
但这让我有点困惑,因为我看不出有太大区别
尽管后缀 ++
运算符的优先级高于强制转换运算符,但加法运算符 (+
) 的优先级较低。因此,(int *)next + 1
被解释为 ((int *)next) + 1
。因为指针算术是根据指向类型完成的,所以会产生一个指向下一个 int
的指针。
那么,回到你这样做的目的:
使用指针构建和访问通用数据结构,其中 元素的大小不是固定的
如果这样的系统需要知道它正在处理的对象的大小,以便将它们存储在一个数组中,例如,它通常携带该大小作为一个或多个相关数据结构的属性或作为需要它的函数的参数。想要执行您所描述的那种增量的严格遵守的程序将通过强制转换为 char *
来实现,可能像这样:
void *next_item(void *current_item,size_t item_size) {
return ((char *) current_item) + item_size;
}
虽然在那里实际上不是必需的,但括号使操作优先级明确,以避免任何混淆。
注意:以上是为 C 编写的。注意事项与 C++ 基本相同,但与 C 不同的是,C++ 需要显式转换才能在 void *
和其他指针类型之间进行转换。 (原始代码中缺少这些意味着 C 是其真正的目标语言。)上面的示例展示了 C 的良好代码风格,但需要将返回值强制转换为 void *
才能在 C++ 中使用。
您不能使用指向 void
的指针执行算术运算。运算符 ++
的优先级高于 (type)
转换,因此 ++
绑定更紧密。所以:
(int *) next++;
相当于:
(int *) (next++);
并且演员的结果被丢弃。
像 ((int *) next)++
这样的东西(这就是您似乎想要获得优先级发生的方式)是行不通的。变量是一个 lvalue,可以赋值给它。当你投射它时,它变成了一个 rvalue,你不能赋值给它,你也不能取它的地址。
由于您不能直接增加 void *
,因此您唯一的选择(将其保留为 void *
)就是对其进行简单的 =
赋值:
next = (int *) next + 1
或者为了完全清楚使用括号,代价是降低了阅读的便利性:
next = ((int *) next) + 1
最好的选择是只使用 int *
开始。如果您需要保持变量类型的灵活性,只需创建一个新变量:
int *next_int = next;
仅当您使用 C++ 时才添加演员表。 (它们是不同的语言,有不同的规则。)
,++
运算符的优先级高于强制转换运算符,因此:
(int *)next++;
解析为:
(int *)(next++);
添加括号无济于事:
((int *)next)++;
因为 ++
运算符需要一个 lvalue,而转换运算符的结果不是一个左值。
虽然 next = (int *)next + 1
有效,但处理此问题的最佳方法是将 next
分配给 int *
类型的变量并使用它。
int *nextInt = next;
*next++ = -1;
*next = -1;
,
保持简单:
int* ip = some_void_pointer;
ip[0] = -1;
ip[1] = -1;
或者不那么优雅:
int* ip = some_void_pointer;
*ip = -1;
ip++;
*ip = -1;
您代码中的问题与运算符优先级有关。简单地避免在同一表达式中将 ++ 与其他运算符混合,因为这很容易出错并且会导致许多常见的错误。优先级问题、无序访问、评估依赖的顺序等。
,虽然不完全适合您的情况,但我已经编写了一个小辅助函数来避免增加指针时的一些陷阱:
/// <summary>
/// Pointer arithmetic on byte level on other object types. This shall be used if the offset is in bytes,but T is some other pointer type.
/// </summary>
template <typename T>
T* AddBytes(T* inPtr,int offset)
{
return (T*)(((byte*)inPtr) + offset);
}
使用喜欢
int value = *AddBytes((int*)startOfStruct,sizeof(int));
这从结构中获取字节偏移量 4 处的整数。 (很好:也适用于作业的左侧)
如果你稍微改变一下,你可以得到 sizeof(int) 的增量:
int* AddByInts(void* inPtr,int dwordCounts)
{
return (int*)(((int*)inPtr) + offset);
}
不允许直接使用 ++ 运算符,但无论如何可以使代码更清晰。