• C- 一个程序引发的问题


    C程序如下:

    #include 
    #include 
    #include 
    
    struct student_t
    {
        char *id;
        char *name;
        char *score;
    };
    
    typedef struct student_t *Student_t;
    
    void print_stu_info(Student_t stu)
    {
        printf("%s  %s  %s\n", stu->id, stu->name, stu->score);
    }
    
    int main()
    {
        Student_t stu = malloc(sizeof(*stu));
        stu->id = "001";
        stu->name = "xiaonan";
        stu->score = "95";
        print_stu_info(stu);
    
        *((char **)((char *)stu + 2*sizeof(char *))) = "100";
        print_stu_info(stu);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    程序运行结果如下:

    majn@tiger:~/C_Project/dl_project$ ./hack_struct 
    001  xiaonan  95
    001  xiaonan  100
    
    • 1
    • 2
    • 3

    问题一


    Student_t stu = malloc(sizeof(*stu));
    
    • 1

    第一次见这种写法,真的没有问题吗?写成

    Student_t stu = malloc(sizeof(*Student_t)); 
    
    • 1

    为什么编译报错?


    在这行代码中出现了错误:

    Student_t stu = malloc(sizeof(*Student_t));
    
    • 1

    问题在于 *Student_t。在这里,Student_t 是一个类型,而不是一个变量。不能对类型进行解引用

    正确的方式是,使用 sizeof(struct student_t)sizeof(*stu) 来获取结构体的大小。如果我们要分配一个结构体的大小并返回一个指向该结构体的指针,应该这样写:

    Student_t stu = malloc(sizeof(struct student_t));
    
    • 1

    或者:

    Student_t stu = malloc(sizeof(*stu));
    
    • 1

    将上述更正后的代码行替换原有代码,应该能够成功编译。


    关于第一种写法的问题,这实际上与C语言的声明和初始化语法有关。

    在C语言中,当我们写:

    Student_t stu = malloc(sizeof(*stu));
    
    • 1

    以下是发生的事情的简化描述:

    1. 编译器首先处理 Student_t stu 部分。这里,它知道声明了一个名为 stuStudent_t 类型的指针变量。从这一点开始,stu 已经被声明并可以在其后的代码中使用。

    2. 接下来,编译器处理 = malloc(sizeof(*stu)) 部分。这时,由于 stu 已经被声明,所以可以在 sizeof(*stu) 中使用它。

    因此,尽管 = 是右结合的,但在整个声明和初始化过程中,声明总是先于初始化发生。

    如果将这两个步骤分开,那么确实需要确保在使用一个变量之前已经声明了它。但在同一语句中进行声明和初始化时,该语句的声明部分会首先被处理。


    问题二

    如何通过直接操作内存地址的方式来修改 stu->score 的值?

    如果想通过直接操作内存地址来修改 stu->score 的值,首先,需要确保准确地计算了偏移量,并且将正确的值赋给正确的地址。

    struct student_t 中,字段按以下顺序出现:

    1. char *id
    2. char *name
    3. char *score

    所以,stu 指针首先指向 id,然后是 name,最后是 score

    我们可以通过增加 stu 的地址来修改 score,但需要知道系统的指针大小(通常是 4 字节(32位系统)或 8 字节(64位系统))。

    以下是可能的写法(假设在 64 位系统上):

    *((char **)((char *)stu + 2*sizeof(char *))) = "100";
    
    • 1

    解释:

    • 首先,我们将 stu 转换为 char * 类型,使我们能够按字节递增其地址。
    • 接下来,我们增加了 2 * sizeof(char *) 字节,这样我们就可以跳过前两个 char * 字段,指向 score
    • 然后我们将结果转换回 char ** 类型,以便我们可以将字符串的地址("100")赋给它。

    下面,我们来详细地探讨这行代码的每一个部分:

    首先,来看这个代码的目的:它旨在直接更改 stu->score 的指针值,使其指向新的字符串字面量 "100"

    现在,我们一步一步来分析这行代码:

    1. (char *)stu:
      这将 stu 指针从 Student_t 类型转换为一个字符指针 (char *)。这样做允许我们以字节为单位进行指针算术。

    2. 2*sizeof(char *):
      这里我们计算两个指针的大小。每个 char * 指针的大小通常为 4 字节(32位系统)或 8 字节(64位系统)。因此,这部分代码计算 stu 结构中前两个 char * 指针(idname)所占用的总字节。

    3. (char *)stu + 2*sizeof(char *):
      这将上述两个部分结合起来,即将 stu 的起始地址增加了前两个 char * 指针所占用的字节,从而将地址定位到 score 字段。

    4. (char **)((char *)stu + 2*sizeof(char *)):
      现在,我们再次进行类型转换,将计算出的 score 地址从 char * 转换为 char **。因为我们最终的目标是更改一个指针的值(指向一个新的字符串),而不仅仅是更改一个字符。

    5. ((char **)((char *)stu + 2*sizeof(char *))) = “100”:
      最后,我们使用解引用运算符 * 来访问和更改 score 指针的值,使其指向新的字符串 "100"

    直接操作内存地址是非常危险的,并且容易引入错误。这种做法破坏了抽象,使代码变得难以阅读和维护。因此,在实际编程中,应尽量避免使用这样的方式。

  • 相关阅读:
    圆梦腾讯之后,我收集整理了这份“2022Java 常见面试真题汇总
    Access-Control-Allow-Origin跨域问题,使用Nginx配置来解决
    力扣每日一题46:全排列
    金蝶系统组织架构封存sql使用
    C语言:指针笔试真题
    代码随想录-算法训练营day55【动态规划16:两个字符串的删除操作、编辑距离、编辑距离总结篇】
    C++11 for循环(基于范围的循环)详解
    两步随机接入机制的深度解析和未来增强
    SSD1306 oled显示屏的驱动SPI接口
    护眼灯真的可以保护眼睛吗?2022双十二选哪款护眼灯对孩子眼睛好
  • 原文地址:https://blog.csdn.net/weixin_43844521/article/details/133419591