代码之家  ›  专栏  ›  技术社区  ›  explorer

在指针算法中使用char*覆盖void*

  •  1
  • explorer  · 技术社区  · 4 年前

    我正在经历这一切 code 出于学习目的,并对以下内容有疑问 this line :

    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* 只是想看看这是否有效。As char* 与…无关 struct 在这个问题上,我只是想,“如果我使用 无效* 那边?“.一个更具体的问题可能是,为什么不 int* float* &为什么? char* 使用?

    0 回复  |  直到 4 年前
        1
  •  2
  •   Eric Postpischil    4 年前

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

    为什么desc被强制转换为char*?As char* 与…无关 struct 有疑问

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

    在标准C中,指针算术使用指向类型的单位。对于指针 p 类型 struct Test , p+1 指向后面的下一个结构 p , p+2 指向之后的结构,依此类推。对于指针 q 类型 char* , q+1 指向下一个 char 在q之后, q+2 指向 烧焦 之后,等等。

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

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

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

    char* 之所以使用,是因为C中的所有对象(位字段除外)都被定义为表示为字节序列。它们不一定是序列 int float . unsigned char * signed char * 也可以使用,以及 无符号字符* 由于体征问题的复杂性,可能更可取。

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

    为什么指向无效工作

    注:我用过 void* 只是想看看这是否有效。

    为了使指针算法工作,编译器需要知道所指向对象的大小。当1被添加到指向a的指针时 结构测试 ,编译器需要知道调整内部地址的字节数。

    void 是一个不完整的类型。没有 无效 对象,以及 无效 没有尺寸。(大小不为零。没有大小。)因此,C标准没有为 p+1 什么时候 p 是a void * .

    然而, GCC defines arithmetic on void * as an extension 它的工作原理就好像 无效 大小为1字节。Clang对此也表示支持。

    由于这种扩展,用 无效* 指针本质上与用 char* 指针。

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

    您可以使用开关禁用此扩展 -Werror -Wpointer-arith ,或者您通常可以通过以下方式要求更符合标准C -Werror -std=c18 -pedantic .我用过 -Werror-std=c18-迂腐 只要有可能,我推荐它。

    脚注

    1. 位字段是保存在某个较大字节容器中的位序列,可能恰好与字节一致。