鱼C论坛

 找回密码
 立即注册
查看: 6144|回复: 55

[已解决]malloc函数申请的内存空间为什么要进行类型强制转换?

[复制链接]
发表于 2022-11-26 13:29:18 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 竹逸 于 2022-11-26 13:31 编辑

下面是我个人的理解,对吗?求大神解答以下
因为malloc函数的返回值是无类型指针(无类型地址),即返回分配内存的起始地址,该地址上存储的数据是未定义类型的,所以该段空间属于字节型空间(计算机默认是以字节为单位),假如向该段空间的起始地址存储一个int型数据(int型数据是以4个字节为单位进行传输的)之后,再次读取该int型数据,由于该段空间的属于字节型空间,从起始地址开始读取出来的数据就只能是一个字节。强制类型转换是为了把这段字节型空间转换成以4个字节为单位的空间,这样往这段空间里存储int型数据或者读取都按照4个字节为单位进行传输的,就不会造成精度的损失。
我的猜测自己无法验证,因为malloc函数如果不进行强制转换就无法编译,也看不到内存的存储情况
最佳答案
2022-11-26 16:34:16

报错说 argument type 'void' is incomplete
参数类型'void'不完整
既然不完整,那还少了什么呢?
少了长度信息
完整的类型,像 int、char、short、。。。
这些是完整的类型
他们有两个属性,一个是地址,就是在内存中这个变量从哪里开始存放
另一个是长度,从这个地址开始,后面还有几个字节是属于当前这个变量的

举个例子
sh-5.1$ cat main.c
#include <stdio.h>

int main(void) {
    char a = '0';
    int b = 123;
    double c = 123.456;
    printf("int: %zu\n", sizeof(int));
    printf("double: %zu\n", sizeof(double));
    printf("&a: %p\n", &a);
    printf("&b: %p\n", &b);
    printf("&c: %p\n", &c);
    return 0;
}
sh-5.1$ ./main
int: 4
double: 8
&a: 0x7fff48c90720
&b: 0x7fff48c90730
&c: 0x7fff48c90740
sh-5.1$

变量a是char类型,地址是 0x7fff48c90720,长度是 1
就是说a从 0x7fff48c90720 地址开始,到 0x7fff48c90721 地址结束,不包括这个地址

变量b是int类型,地址是 0x7fff48c90730,长度是 4
就是说b从 0x7fff48c90730 地址开始,到 0x7fff48c90734 地址结束,不包括这个地址

变量c是double类型,地址是 0x7fff48c90730,长度是 8
就是说c从 0x7fff48c90740 地址开始,到 0x7fff48c90748 地址结束,不包括这个地址

但是 void 没有长度信息,只有一个地址信息,这就导致在对data这个变量解引用的时候不知道使用多少个字节,就是说
是知道从哪里开始,但是不知道到哪里结束
只有一个地址信息,知道从哪里开始
没有长度信息,不知道到哪里结束
所以不能对 void * 解引用
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-11-26 13:34:25 | 显示全部楼层
本帖最后由 jackz007 于 2022-11-26 13:39 编辑

        没有别的原因,只有等号两侧的数据类型匹配相容,编译器才能知道如何安排实现,也才能确定变量的使用规则。例如,同样是 p ++ ,如果是 (char *) p,p 的值将增加 1 ,如果是 (int *) p,p 的值将增加 4。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-11-26 13:35:46 | 显示全部楼层
jackz007 发表于 2022-11-26 13:34
没有别的原因,只有等号两侧的数据类型匹配相容,编译器才能知道如何安排实现。


我发现不进行类型转换,可以用字符型指针来接收这段空间的起始地址,但int型指针就无法编译
int main(void)
{
        char *p;

        p = malloc(sizeof(int));
}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 13:40:54 | 显示全部楼层
本帖最后由 jackz007 于 2022-11-26 13:44 编辑
竹逸 发表于 2022-11-26 13:35
我发现不进行类型转换,可以用字符型指针来接收这段空间的起始地址,但int型指针就无法编译


         所以,我说过,等号两侧需要类型相容,指的就是这个意思。加了强制类型转换,代表你自己已经知道这样做所存在的风险,并需要坚持这样做,所以,编译器就不再拦着。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 13:58:02 | 显示全部楼层
先等一等,你的意思是这样的代码有问题?
sh-5.1$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char *a = malloc(3);
    a[0] = 'o';
    a[1] = 'k';
    a[2] = '\0';
    puts(a);
    free(a);
    return 0;
}
sh-5.1$ gcc -g -Wall -o main main.c
sh-5.1$ ./main
ok
sh-5.1$
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 13:59:33 | 显示全部楼层
知道为什么了吗?
为什么?
sh-5.1$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char *a = malloc(3);
    a[0] = 'o';
    a[1] = 'k';
    a[2] = '\0';
    puts(a);
    free(a);
    return 0;
}
sh-5.1$ gcc -g -Wall -o main main.c
sh-5.1$ ./main
ok
sh-5.1$ g++ -g -Wall -o main main.c
main.c: In function ‘int main()’:
main.c:5:21: error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive]
    5 |     char *a = malloc(3);
      |               ~~~~~~^~~
      |                     |
      |                     void*
sh-5.1$
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:01:37 | 显示全部楼层
假如向该段空间的起始地址存储一个int型数据

怎么存?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-11-26 14:01:40 | 显示全部楼层
jackz007 发表于 2022-11-26 13:40
所以,我说过,等号两侧需要类型相容,指的就是这个意思。加了强制类型转换,代表你自己已经 ...

我想起来了,数据要读取多大的长度好像是有栈顶寄存器(SP)来决定偏移多少个字节的,内存空间依然是字节型空间无法改变,改变的是由SP决定读取数据的长度
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:03:03 | 显示全部楼层
这么存?
sh-5.1$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *a = malloc(32);
    *a = 1234;
    printf("%d\n", *a);
    free(a);
    return 0;
}
sh-5.1$ gcc -g -Wall -o main main.c
sh-5.1$ ./main
1234
sh-5.1$
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:05:26 | 显示全部楼层
从起始地址开始读取出来的数据就只能是一个字节
上面的代码读取出来的就不是一个字节,是一个int,因为变量a的类型是int *
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:07:14 | 显示全部楼层
简单来说就是,我没看懂你在说什么
如果你认为这个无法用C语言描述,那就用汇编语言来描述一下你的问题
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-11-26 14:10:50 | 显示全部楼层
人造人 发表于 2022-11-26 14:01
假如向该段空间的起始地址存储一个int型数据

怎么存?

依然是以字节存储的,只不过int型数据是占4个字节,就是要传输4个字节的数据,内存中的数据先是由CPU读取到通用寄存器里,要读取多大的长度由与它关联的寄存器给出,我想起了汇编的知识
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:11:09 | 显示全部楼层
本帖最后由 jackz007 于 2022-11-26 14:12 编辑
竹逸 发表于 2022-11-26 14:01
我想起来了,数据要读取多大的长度好像是有栈顶寄存器(SP)来决定偏移多少个字节的,内存空间依然是字节 ...


       SP 是 16 位程序的栈顶指针,当需要局部变量存储空间的时候,需要多少字节,就把 SP 减掉多少,这样,堆栈操作(push / pop)就不会再使用这些空间,这里需要强调的是,堆栈空间只适合局部变量使用,malloc() 并不是从堆栈,而是从堆上获得的空间,所以,在函数内使用 malloc() 分配到的空间,并不会在函数退出的时候被收回,只要不使用 free() 进行释放,就会一直存在。
       再纠正一点,你读写存储空间的内容与 SP 并没有任何的直接关系。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:21:18 | 显示全部楼层
malloc函数申请的内存空间为什么要进行类型强制转换?

malloc函数申请的内存空间需要进行类型强制转换?
反正我写代码是从来不强制转换的,因为没有必要,因为强制转换既不会增加代码的可读性,也不会增加代码的执行效率,完全就是多余的,就和auto关键字差不多,我见你写代码从来不在变量前面加这个,我也不加,因为没有必要
强制转换也差不多就是这样的,因为没有必要,当然就算是加上也不会有语法错误,只是没有必要
sh-5.1$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char *a = malloc(3);
    a[0] = 'o';
    a[1] = 'k';
    a[2] = '\0';
    puts(a);
    free(a);
    return 0;
}
sh-5.1$ gcc -g -Wall -o main main.c
sh-5.1$ ./main
ok
sh-5.1$

再举个例子?
在C++中,类里面可以不加this直接使用类内部的变量,但是这里我会选择加上
这里
        return x + y;
        return this->x + this->y;
这两个都是可以的,但是这里我会选择加上
因为我认为这样会增加代码的可读性,这完全就是我自己的感觉


其实有些东西就是多余的,完全没有必要的,要不要用,完全取决于你自己
class test_t {
public:
    test_t(int x = 0, int y = 0): x(x), y(y) {}
    int add() {
        //return x + y;
        return this->x + this->y;
    }
private:
    int x, y;
};
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-11-26 14:21:26 | 显示全部楼层
jackz007 发表于 2022-11-26 14:11
SP 是 16 位程序的栈顶指针,当需要局部变量存储空间的时候,需要多少字节,就把 SP 减掉多少, ...

是的,我学汇编的时候就是小甲鱼8086的CPU知识,所以也就只能用8086的来描述,而且malloc函数却是也是从堆中开辟的空间,就是不知道堆指针是由什么寄存器来作偏移量的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:22:31 | 显示全部楼层
竹逸 发表于 2022-11-26 14:10
依然是以字节存储的,只不过int型数据是占4个字节,就是要传输4个字节的数据,内存中的数据先是由CPU读取 ...

11楼
https://fishc.com.cn/forum.php?m ... 281&pid=6063279
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:23:38 | 显示全部楼层
竹逸 发表于 2022-11-26 14:10
依然是以字节存储的,只不过int型数据是占4个字节,就是要传输4个字节的数据,内存中的数据先是由CPU读取 ...

先把我的回复从上到下看一遍,然后再提出你的问题
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-11-26 14:26:54 | 显示全部楼层
本帖最后由 竹逸 于 2022-11-26 14:28 编辑


好神奇,为什么我问问题之前,和你同样的代码无法编译,现在又能编译了你看我问题描述,描述说不进行强制转换就无法编译,为啥现在又能编译了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:28:47 | 显示全部楼层
竹逸 发表于 2022-11-26 14:21
是的,我学汇编的时候就是小甲鱼8086的CPU知识,所以也就只能用8086的来描述,而且malloc函数却是也是从 ...


        你不用关心堆指针的事情,因为,这个完全是由操作系统在掌控(malloc() 是 Windows API),你只需要按规则进行申请、使用和释放就可以了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-26 14:32:27 | 显示全部楼层
竹逸 发表于 2022-11-26 14:26
好神奇,为什么我问问题之前,和你同样的代码无法编译,现在又能编译了你看我问题描述,描述 ...

17楼
https://fishc.com.cn/forum.php?m ... 281&pid=6063298
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-17 11:32

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表