首页 > 解决方案 > 在指针算术中使用 char* over void*

问题描述

我正在浏览此代码以用于学习目的,并对这一行有疑问:

return (char*)desc + sizeof *desc;

为什么要desc投到char*?我试图用我自己的代码来模仿它:

#include <stdio.h>
#include <stdlib.h>

struct Test {
    int value;
};

int main() {

    struct Test* test = malloc(sizeof test);

    struct Test* test1 = (void*)test + sizeof *test;
    test1->value = 1;
    printf("%d\n", test1->value);

    struct Test* test2 = (void*)test1 + sizeof *test;
    test2->value = 10;
    printf("%d\n", test2->value);

}

这也有效。但是,有什么区别呢?为什么char*使用?

注意:我以前void*只是看看这是否有效。由于char*与问题无关struct,我只是想,“如果我void*在那里使用呢?”。一个更具体的问题可能是,为什么不使用int*float*为什么char*使用?

标签: ccastingpointer-arithmetic

解决方案


为什么使用指向字符的指针

为什么 desc 转换为 char*?......因为char*与问题无关struct......</p>

在 C 中,除了位域之外的每个对象都由字节序列组成。1将对象的地址转换为指向对象char *第一个字节的指针,您可以使用该指针访问对象的各个字节。

在标准 C 中,指针算术使用指向类型的单元。p对于类型的指针struct Testp+1指向 之后的下一个结构pp+2指向之后的结构,依此类推。q对于类型为 的指针char *q+1指向 q 之后的下一个,指向charq之后,依此类推。q+2char

因此,要访问对象的各个字节,您可以将其地址转换为 achar *并使用它。

为什么不使用其他类型的指针

一个更具体的问题可能是,为什么不使用int*float*为什么char*使用?

char *之所以使用,是因为 C 中的所有对象(位域除外)都被定义为表示为字节序列。它们不一定是intor的序列floatunsigned char *并且signed char *也可以使用,并且unsigned char *由于符号问题的并发症可能更可取。

C 标准有关于使用字符指针访问对象的特殊规则,因此它保证以这种方式访问​​对象的字节是可行的。相反,使用int *or访问对象float *可能不起作用。允许编译器期望指向 an 的指针int不会用于访问float对象,并且当它为程序生成机器指令时,它可能会根据该期望编写这些指令。使用字符指针可防止编译器假定 achar *与另一种指针指向的位置不同。

为什么指向无效工作的指针

注意:我以前void*只是看看这是否有效。

为了使指针算法起作用,编译器需要知道指向对象的大小。将 1 添加到指向 a 的指针时struct Test,编译器需要知道调整内部地址的字节数。

void是不完全类型。没有void物体,void也没有大小。(大小不为零。没有大小。)因此,C 标准没有定义p+1when pis a 的任何含义void *

但是,GCC 将算术 on 定义void *为扩展。它就像void大小为 1 字节一样工作。Clang 也支持这一点。

由于这个扩展,用指针做算术void *本质上和用指针做算术是一样的char *

这个扩展是不必要的;任何进行算术运算的代码void *都可以重写以char *替代使用。有时这需要额外的强制转换来转换指针类型,这可能就是将扩展添加到 GCC 的原因(以减少所需的代码量并使其看起来更好)。

您可以使用开关禁用此扩展-Werror -Wpointer-arith,或者您通常可以使用-Werror -std=c18 -pedantic. 我-Werror -std=c18 -pedantic尽可能使用它,我推荐它。

脚注

1位域是保存在一些较大的字节容器中的位序列,可能恰好与字节重合。


推荐阅读