C中void指针的指针运算

发布于 2024-09-15 15:14:02 字数 150 浏览 5 评论 0原文

当指向特定类型(例如 intcharfloat 等)的指针递增时,其值会增加该数据类型。如果指向大小为 x 的数据的 void 指针递增,它如何指向前面 x 字节?编译器如何知道将 x 添加到指针的值中?

When a pointer to a particular type (say int, char, float, ..) is incremented, its value is increased by the size of that data type. If a void pointer which points to data of size x is incremented, how does it get to point x bytes ahead? How does the compiler know to add x to value of the pointer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

若无相欠,怎会相见 2024-09-22 15:14:02

最终结论:void* 上的算术在 C 和 C++ 中都是非法的。

GCC 允许将其作为扩展,请参阅 Arithmetic on void-和函数指针(请注意,本节是手册“C 扩展”一章的一部分)。 Clang 和 ICC 可能允许 void* 算术,以便与 GCC 兼容。其他编译器(例如 MSVC)不允许对 void* 进行算术,如果指定了 -pedantic-errors 标志,或者如果 -Werror=指定了pointer-arith 标志(如果您的代码库还必须使用MSVC 进行编译,则该标志很有用)。

C Standard Speaks

引述取自 n1256 草案。

标准对加法运算的描述如下:

6.5.6-2:对于加法,两者之一
操作数应具有算术类型,
或者一个操作数应该是一个指向
一个对象类型,另一个应
具有整数类型。

因此,这里的问题是 void* 是否是指向“对象类型”的指针,或者同等地,void 是否是“对象类型”。 “对象类型”的定义是:

6.2.5.1:类型分为对象类型(完整描述对象的类型)、函数类型(描述函数的类型)和不完整类型(描述对象但缺乏确定其大小所需的信息的类型)。

标准将 void 定义为:

6.2.5-19:void 类型包括
一组空值;
这是一个不完整的类型,不能
已完成。

由于 void 是不完整类型,因此它不是对象类型。因此它不是加法运算的有效操作数。

因此,您无法对 void 指针执行指针算术。

注释

最初,由于 C 标准的以下部分,人们认为 void* 算术是允许的:

6.2.5-27:指向 void 的指针应具有相同的表示和对齐方式
需求作为指向
字符类型。

然而,

相同的表示和对齐方式
要求意味着
可互换性作为参数
函数,返回值来自
职能和工会成员。

因此,这意味着无论 x 的类型为 char* 还是 void*,printf("%s", x) 都具有相同的含义,但这并不意味着您可以对 void* 进行算术运算。

Final conclusion: arithmetic on a void* is illegal in both C and C++.

GCC allows it as an extension, see Arithmetic on void- and Function-Pointers (note that this section is part of the "C Extensions" chapter of the manual). Clang and ICC likely allow void* arithmetic for the purposes of compatibility with GCC. Other compilers (such as MSVC) disallow arithmetic on void*, and GCC disallows it if the -pedantic-errors flag is specified, or if the -Werror=pointer-arith flag is specified (this flag is useful if your code base must also compile with MSVC).

The C Standard Speaks

Quotes are taken from the n1256 draft.

The standard's description of the addition operation states:

6.5.6-2: For addition, either both
operands shall have arithmetic type,
or one operand shall be a pointer to
an object type and the other shall
have integer type.

So, the question here is whether void* is a pointer to an "object type", or equivalently, whether void is an "object type". The definition for "object type" is:

6.2.5.1: Types are partitioned into object types (types that fully describe objects) , function types (types that describe functions), and incomplete types (types that describe objects but lack information needed to determine their sizes).

And the standard defines void as:

6.2.5-19: The void type comprises
an empty set of values;
it is an incomplete type that cannot
be completed.

Since void is an incomplete type, it is not an object type. Therefore it is not a valid operand to an addition operation.

Therefore you cannot perform pointer arithmetic on a void pointer.

Notes

Originally, it was thought that void* arithmetic was permitted, because of these sections of the C standard:

6.2.5-27: A pointer to void shall have the same representation and alignment
requirements as a pointer to a
character type.

However,

The same representation and alignment
requirements are meant to imply
interchangeability as arguments to
functions, return values from
functions, and members of unions.

So this means that printf("%s", x) has the same meaning whether x has type char* or void*, but it does not mean that you can do arithmetic on a void*.

时光无声 2024-09-22 15:14:02

void* 指针不允许进行指针算术。

Pointer arithmetic is not allowed on void* pointers.

可爱咩 2024-09-22 15:14:02

将其转换为 char 指针,并将指针向前增加 x 个字节。

cast it to a char pointer an increment your pointer forward x bytes ahead.

橘虞初梦 2024-09-22 15:14:02

C 标准不允许void指针算术。然而,考虑到 void 的大小为 1GNU C 是允许的。

C11 标准§6.2.5

第 19 段

void 类型包含一组空值;这是一个不完整的
无法完成的对象类型。

以下程序在 GCC 编译器中运行良好。

#include<stdio.h>

int main()
{
    int arr[2] = {1, 2};
    void *ptr = &arr;
    ptr = ptr + sizeof(int);
    printf("%d\n", *(int *)ptr);
    return 0;
}

可能是其他编译器产生错误。

The C standard does not allow void pointer arithmetic. However, GNU C is allowed by considering the size of void is 1.

C11 standard §6.2.5

Paragraph - 19

The void type comprises an empty set of values; it is an incomplete
object type
that cannot be completed.

Following program is working fine in GCC compiler.

#include<stdio.h>

int main()
{
    int arr[2] = {1, 2};
    void *ptr = &arr;
    ptr = ptr + sizeof(int);
    printf("%d\n", *(int *)ptr);
    return 0;
}

May be other compilers generate an error.

梦纸 2024-09-22 15:14:02

正是由于这个原因,您不能对 void * 类型进行指针算术!

You can't do pointer arithmetic on void * types, for exactly this reason!

嘿看小鸭子会跑 2024-09-22 15:14:02

空指针可以指向任何内存块。因此,当我们尝试对 void 指针进行指针算术时,编译器不知道要增加/减少多少字节。因此,void 指针必须首先被类型转换为已知类型,然后才能参与任何指针算术。

void *p = malloc(sizeof(char)*10);
p++; //compiler does how many where to pint the pointer after this increment operation

char * c = (char *)p;
c++;  // compiler will increment the c by 1, since size of char is 1 byte.

Void pointers can point to any memory chunk. Hence the compiler does not know how many bytes to increment/decrement when we attempt pointer arithmetic on a void pointer. Therefore void pointers must be first typecast to a known type before they can be involved in any pointer arithmetic.

void *p = malloc(sizeof(char)*10);
p++; //compiler does how many where to pint the pointer after this increment operation

char * c = (char *)p;
c++;  // compiler will increment the c by 1, since size of char is 1 byte.
森林迷了鹿 2024-09-22 15:14:02

在进行指针算术之前,您必须将其转换为另一种类型的指针。

You have to cast it to another type of pointer before doing pointer arithmetic.

我还不会笑 2024-09-22 15:14:02

[答案复制自 评论 ://stackoverflow.com/questions/73996340">稍后,重复的问题]

允许对空指针进行算术是一个有争议的非标准扩展。如果你用汇编语言思考,其中指针只是地址,那么对 void 指针进行算术是有意义的,加 1 只是加 1。但是如果你用 C 术语思考,使用 C 的指针算术模型,对任意值加 1指针 p 实际上将 sizeof(*p) 添加到地址,这就是您希望指针算术执行的操作,但是由于 sizeof(void) > 为 0,它会分解为 void 指针。

如果您用 C 术语思考,您不介意它崩溃,并且您不介意将显式转换插入 (char *)(如果这是您想要的算术)。但是,如果您在汇编程序中思考,您希望它能够正常工作,这就是为什么扩展(尽管偏离了 C 中指针算术的正确定义)在某些圈子中是可取的,并且由某些编译器提供。

[answer copied from a comment on a later, duplicate question]

Allowing arithmetic on void pointers is a controversial, nonstandard extension. If you're thinking in assembly language, where pointers are just addresses, arithmetic on void pointers makes sense, and adding 1 just adds 1. But if you're thinking in C terms, using C's model of pointer arithmetic, adding 1 to any pointer p actually adds sizeof(*p) to the address, and this is what you want pointer arithmetic to do, but since sizeof(void) is 0, it breaks down for void pointers.

If you're thinking in C terms you don't mind that it breaks down, and you don't mind inserting explicit casts to (char *) if that's the arithmetic you want. But if you're thinking in assembler you want it to just work, which is why the extension (though a departure from the proper definition of pointer arithmetic in C) is desirable in some circles, and provided by some compilers.

活泼老夫 2024-09-22 15:14:02

void 指针中不允许进行指针算术。

原因:指针算术与普通算术不同,因为它是相对于基地址发生的 >。

解决方案:在算术运算时使用类型转换运算符,这将使执行指针算术的表达式知道基本数据类型。
ex: point 是 void 指针

*point=*point +1; //Not valid
*(int *)point= *(int *)point +1; //valid

Pointer arithmetic is not allowed in the void pointer.

Reason: Pointer arithmetic is not the same as normal arithmetic, as it happens relative to the base address.

Solution: Use the type cast operator at the time of the arithmetic, this will make the base data type known for the expression doing the pointer arithmetic.
ex: point is the void pointer

*point=*point +1; //Not valid
*(int *)point= *(int *)point +1; //valid
桃扇骨 2024-09-22 15:14:02

编译器通过类型转换知道。给定一个 void *x

  • x+1 将一个字节添加到 x,指针指向字节 x+1
  • (int*)x+1 添加 sizeof(int) 字节,指针指向字节 x + sizeof(int)
  • (float *)x+1 addres sizeof(float) 字节,
    虽然

第一项不可移植并且与 C/C++ 的 Galateo 相悖,但它仍然是 C 语言正确的,这意味着它将在大多数编译器上编译为某些内容,可能需要适当的标志(例如 -Wpointer-arith)

Compiler knows by type cast. Given a void *x:

  • x+1 adds one byte to x, pointer goes to byte x+1
  • (int*)x+1 adds sizeof(int) bytes, pointer goes to byte x + sizeof(int)
  • (float*)x+1 addres sizeof(float) bytes,
    etc.

Althought the first item is not portable and is against the Galateo of C/C++, it is nevertheless C-language-correct, meaning it will compile to something on most compilers possibly necessitating an appropriate flag (like -Wpointer-arith)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文