[shlug] C中malloc返回值的疑惑。

43 views
Skip to first unread message

孑影

unread,
Aug 16, 2012, 9:28:31 AM8/16/12
to sh...@googlegroups.com
在C 中动态分配内存,一般都是使用malloc分配。mailloc是在系统空闲的内存块中分配一块空闲内存返回给申请者。

malloc返回的类型是void *

然后需要将void *转换为你需要使用的相应类型。


例如如下代码。

-------------------------------------------------------------------------------------


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

typedef struct
{
int age;
int sex;
char name[20];
}Node;


typedef struct
{
int age;
int sex;
}Temp;


int main()
{
Node * p_node;
Temp *p_temp;

int i;

printf("Node size:[%d]\n",sizeof(Node));

p_node=(Node *)malloc(sizeof(Node)*10);


for(i=0;i<10;i++)
printf("Node[%d]:age[%d]sex:[%d]name:[%s]\n",i,p_node->age,p_node->sex,p_node->name);

memset(p_node,0,sizeof(Node)*10);


p_temp=(Temp *)(p_node+sizeof(Node));

p_temp->age=10;

printf("age[%d]\n",(p_node+sizeof(Node))->age);
free(p_node);

return 0;

}

-------------------------------------------------------------------------------------


返回的是申请到的内存的首地址,按照概念上这块内存在堆上。


而地址不过是给指针,虽然在赋值的时候进行了强制类型转换,
但是按照C里面的所以有变量不过是一个地址,一种内存块使用的方式定义,(个人理解)所以最终还是对内存的
使用。
1、那这个地方,我将malloc出来的内存是不是可以转换成另外一种类型呢?

2、那个地方为什么一定要强制进行类型转换呢?

3、若是采用一种方式绕过类型检测,会不会对程序有什么危害,比如说内存对齐方面的问题。


PS:上面的结果输出来是 age =10

Dunrong Huang

unread,
Aug 16, 2012, 10:26:33 AM8/16/12
to sh...@googlegroups.com
在 2012年8月16日 下午9:28,孑影 <yhsp...@gmail.com> 写道:
> 在C 中动态分配内存,一般都是使用malloc分配。mailloc是在系统空闲的内存块中分配一块空闲内存返回给申请者。
>
> malloc返回的类型是void *
>
> 然后需要将void *转换为你需要使用的相应类型。
>
>
> 例如如下代码。
>
> -------------------------------------------------------------------------------------
>
>
> #include <stdlib.h>
> #include <stdio.h>
> #include <string.h>
>
> typedef struct
> {
> int age;
> int sex;
> char name[20];
> }Node;
>
>
> typedef struct
> {
> int age;
> int sex;
> }Temp;
>
>
> int main()
> {
> Node * p_node;
正常程序里, 应该是 Node **p_node; 用二级指针, 因为你malloc了一个数组.
这样写虽然能工作, 但是可读性很差.

> Temp *p_temp;
>
> int i;
>
> printf("Node size:[%d]\n",sizeof(Node));
>
> p_node=(Node *)malloc(sizeof(Node)*10);
>
>
> for(i=0;i<10;i++)
> printf("Node[%d]:age[%d]sex:[%d]name:[%s]\n",i,p_node->age,p_node->sex,p_node->name);
应该是 printf("Node[%d]:age[%d]sex:[%d]name:[%s]\n",i,(p_node+i)->age,(p_node+i)->sex,(p_node+i)->name);
>
> memset(p_node,0,sizeof(Node)*10);
memset 应该在前面.

>
>
> p_temp=(Temp *)(p_node+sizeof(Node));
>
> p_temp->age=10;
>
> printf("age[%d]\n",(p_node+sizeof(Node))->age);
> free(p_node);
>
> return 0;
>
> }
>
>
>
>
>
> -------------------------------------------------------------------------------------
>
>
> 返回的是申请到的内存的首地址,按照概念上这块内存在堆上。
>
>
> 而地址不过是给指针,虽然在赋值的时候进行了强制类型转换,
> 但是按照C里面的所以有变量不过是一个地址,一种内存块使用的方式定义,(个人理解)所以最终还是对内存的
> 使用。
> 1、那这个地方,我将malloc出来的内存是不是可以转换成另外一种类型呢?
>
Sure
> 2、那个地方为什么一定要强制进行类型转换呢?
>
不需要, 只是你的编码习惯而已, 或者说增加可读性.
> 3、若是采用一种方式绕过类型检测,会不会对程序有什么危害,比如说内存对齐方面的问题。
>
没有这样的说法, malloc 在返回用户之前就多了必要的对其.
>
> PS:上面的结果输出来是 age =10
因为你的第二个成员的前4B(或者8B)的内存值已经被 p_temp 修改为 10.
>
> --
> -- You received this message because you are subscribed to the Google Groups Shanghai Linux User Group group. To post to this group, send email to sh...@googlegroups.com. To unsubscribe from this group, send email to shlug+un...@googlegroups.com. For more options, visit this group at https://groups.google.com/d/forum/shlug?hl=zh-CN
>
>

--
Best Regards,

Dunrong Huang

SuperCat

unread,
Aug 16, 2012, 10:53:20 AM8/16/12
to sh...@googlegroups.com
C下面是可以进行隐式的void * -> 其它指针类型的转换的,C下面完全OK,而在C++里面可能会报错或者警告。 一般强制转换是为了讨好C++编译器或者是增加可读性之类的。

2012/8/16 Dunrong Huang <riega...@gmail.com>

孑影

unread,
Aug 16, 2012, 11:03:07 AM8/16/12
to sh...@googlegroups.com
3q

Dunrong Huang

那个地方我先printf 是调试用的。


那个printf语句没有详细考虑,

写这个例子只是验证一些猜想,引申一下讨论。

不过谢谢您的指出。

----------------------------------

SuperCat

我这个地方 可能需要将一块内存进行多次重用,类似内存池的机制。

可能需要赋值给不同的结构体类型来使用,每次使用前会重新擦零,这样是不是我应该将 Node 定义为void * 然后每次使用前强制转换呢?

貌似以前写某个接口地方用国这种方法,但是这样用会不会造成隐藏的bug呢?

none_nobody

unread,
Aug 16, 2012, 11:16:46 AM8/16/12
to sh...@googlegroups.com
定义一个宏

#define Calloc(n, t)   (t *) calloc( (size_t) (n), sizeof(t) )

以后每次调用直接用, 比如要分配LZ要的结构

p_node= Calloc(10,Node);

咋样。

SuperCat

unread,
Aug 16, 2012, 11:21:07 AM8/16/12
to sh...@googlegroups.com
BUG是不会的,除非有人搞错。
内存池俺用过GLib(不是glibc!)里面的g_slice,专门针对频繁分配相同大小块的(内部实现应该是内存池)。


2012/8/16 孑影 <yhsp...@gmail.com>

孑影

unread,
Aug 16, 2012, 11:35:50 AM8/16/12
to sh...@googlegroups.com
这个方法有思路,但是我可能进了胡同,

我这里需要频繁的分配内存,操作上W的数据,且这些数据,可能是各种不同的结构,

我的思路1就是利用类似内存池的机制来处理,但是内存池多庞大的一个东东阿,所以我尝试找简化版本的来试试。

思路2,分配一块够大的空间,定义成void * 不停的转换成其他类型,然后用完就擦零,不释放。重复使用,也类似内存池。

思路3 ,估计那些结构也就20来种,穷举这些结构类型定义一个操作集合来操作,不过问题是有些结构可能是不定长度的。

2012/8/16 none_nobody <lyx...@gmail.com>:

孑影

unread,
Aug 16, 2012, 11:36:16 AM8/16/12
to sh...@googlegroups.com
有源码吗? 能否给我参考一下

Dunrong Huang

unread,
Aug 16, 2012, 11:43:32 AM8/16/12
to sh...@googlegroups.com
在 2012年8月16日 下午11:35,孑影 <yhsp...@gmail.com> 写道:
> 这个方法有思路,但是我可能进了胡同,
>
> 我这里需要频繁的分配内存,操作上W的数据,且这些数据,可能是各种不同的结构,
>
> 我的思路1就是利用类似内存池的机制来处理,但是内存池多庞大的一个东东阿,所以我尝试找简化版本的来试试。
>
> 思路2,分配一块够大的空间,定义成void * 不停的转换成其他类型,然后用完就擦零,不释放。重复使用,也类似内存池。
>
> 思路3 ,估计那些结构也就20来种,穷举这些结构类型定义一个操作集合来操作,不过问题是有些结构可能是不定长度的。
>
既然你的这个系统这种需求的结构才20来种, 你完全可以实现一个简单的内存池来里面之分配这几种类型的内存,
不会有多少代码, 或者干脆在进程栈上创建足够的数组来分配.

> 2012/8/16 none_nobody <lyx...@gmail.com>:
>> 定义一个宏
>>
>> #define Calloc(n, t) (t *) calloc( (size_t) (n), sizeof(t) )
>>
>> 以后每次调用直接用, 比如要分配LZ要的结构
>>
>> p_node= Calloc(10,Node);
>>
>> 咋样。
>>
>>
>> --
>> -- You received this message because you are subscribed to the Google Groups
>> Shanghai Linux User Group group. To post to this group, send email to
>> sh...@googlegroups.com. To unsubscribe from this group, send email to
>> shlug+un...@googlegroups.com. For more options, visit this group at
>> https://groups.google.com/d/forum/shlug?hl=zh-CN
>>
>>
>
> --
> -- You received this message because you are subscribed to the Google Groups Shanghai Linux User Group group. To post to this group, send email to sh...@googlegroups.com. To unsubscribe from this group, send email to shlug+un...@googlegroups.com. For more options, visit this group at https://groups.google.com/d/forum/shlug?hl=zh-CN
>
>

--
Best Regards,

Dunrong Huang

David pulq

unread,
Aug 16, 2012, 12:23:42 PM8/16/12
to sh...@googlegroups.com
其实是会有问题的。比如
int *p = (int*)malloc(sizeof(char)*4);
这个东西放到arm上这个*p值很有可能不是你想要的结果。因为arm cpu上int需要4字节对其访问。

Dunrong Huang

unread,
Aug 16, 2012, 12:40:20 PM8/16/12
to sh...@googlegroups.com
我可能意思没有表达清楚, 我说的是malloc在管理这部分内存的时候本身做了一些简单的
对齐, 对malloc出来的内存, glibc 就不关心了, 剩下就是用户的事情了. 孑影说的是分配后的
问题, 这个需要程序员自己控制.

另外: 假设 ARM 上 int 是 4B的, int *p = (int*)malloc(sizeof(char)*4); 和 int
*p = (int*)malloc(sizeof(int)); 不一样?
会有问题?

David pulq

unread,
Aug 16, 2012, 1:03:55 PM8/16/12
to sh...@googlegroups.com
hmm,是我举的例子不恰当。。因为for arm的libc的实现里malloc一般会对齐到8byte(有兴趣的可以验证一下)所以举的例子不会有问题。只有在类似:
char *buf = malloc(xxx);
*(int*)(buf+1);
*(int*)(buf+2);
*(int*)(buf+3);
之类的情况才会有问题。。

Dunrong Huang

unread,
Aug 16, 2012, 2:19:15 PM8/16/12
to sh...@googlegroups.com
在 2012年8月17日 上午1:03,David pulq <pulq...@gmail.com> 写道:
> hmm,是我举的例子不恰当。。因为for
> arm的libc的实现里malloc一般会对齐到8byte(有兴趣的可以验证一下)所以举的例子不会有问题。只有在类似:
我没有记错的话, uclibc 里面就是根据 sizeof(double) 来对齐的, glibc 的源码没有看过.

> char *buf = malloc(xxx);
> *(int*)(buf+1);
> *(int*)(buf+2);
> *(int*)(buf+3);
如果是这种情况, 一般都是用 uint8_t 之类的 type 来定义的.
#include <stdint.h>
uint8_t *buf = malloc(xxx);
所以, 这个真得靠程序员自觉了.

none_nobody

unread,
Aug 16, 2012, 8:18:38 PM8/16/12
to sh...@googlegroups.com
每秒上W次分配回收才可能出问题。

或者就对同类型的数据分配一块固定大小吧。这样简单。

David pulq

unread,
Aug 16, 2012, 9:48:57 PM8/16/12
to sh...@googlegroups.com


On Friday, August 17, 2012 2:19:15 AM UTC+8, mathslinux wrote:
在 2012年8月17日 上午1:03,David pulq <pulq...@gmail.com> 写道:
> hmm,是我举的例子不恰当。。因为for
> arm的libc的实现里malloc一般会对齐到8byte(有兴趣的可以验证一下)所以举的例子不会有问题。只有在类似:
我没有记错的话, uclibc 里面就是根据 sizeof(double) 来对齐的, glibc 的源码没有看过.
> char *buf = malloc(xxx);
> *(int*)(buf+1);
> *(int*)(buf+2);
> *(int*)(buf+3);
如果是这种情况, 一般都是用 uint8_t 之类的 type 来定义的.
#include <stdint.h>
uint8_t *buf = malloc(xxx);
所以, 这个真得靠程序员自觉了.
这个buf在各个地方传来传去最后有人想取个int回来的时候就不小心犯这种错误了。gstreamer里以前很多类似的,我还专门丢过patch去fix mpeg2 demuxer里类似的错误.. 

SuperCat

unread,
Aug 16, 2012, 9:53:21 PM8/16/12
to sh...@googlegroups.com
LS是用GStreamer搞开发的啊,俺用GStreamer搞音乐播放器的说~

2012/8/17 David pulq <pulq...@gmail.com>

David pulq

unread,
Aug 16, 2012, 9:59:14 PM8/16/12
to sh...@googlegroups.com
没有,我只是很早时候Android 1.5的年代用gstreamer替换过OpenCore..

David pulq

unread,
Aug 16, 2012, 9:59:40 PM8/16/12
to sh...@googlegroups.com
其实除非万不得已并不推荐自己搞个这东西。 malloc的实现(比如dlmalloc)里已经考虑到这些了。如果实在想这么干,推荐 TCMalloc  


On Thursday, August 16, 2012 11:35:50 PM UTC+8, 孑影 wrote:
On Thursday, August 16, 2012 11:35:50 PM UTC+8, 孑影 wrote:
On Thursday, August 16, 2012 11:35:50 PM UTC+8, 孑影 wrote:

Bojie Li

unread,
Aug 16, 2012, 1:19:41 PM8/16/12
to sh...@googlegroups.com
2012/8/17 Dunrong Huang <riega...@gmail.com>:

> 另外: 假设 ARM 上 int 是 4B的, int *p = (int*)malloc(sizeof(char)*4); 和 int
> *p = (int*)malloc(sizeof(int)); 不一样?
> 会有问题?

我想不明白sizeof(char)*4和sizeof(int)有什么区别,malloc只是一个接受参数的普通函数啊。

size_t是implementation defined,如果没记错是32位机4字节,64位机8字节
对齐默认是2 * sizeof(size_t)
每个allocated chunk至少有4/8字节的overhead(就是INTERNAL_SIZE_T)。

#define INTERNAL_SIZE_T size_t
#define SIZE_SZ (sizeof(INTERNAL_SIZE_T))
#define MALLOC_ALIGNMENT (2 * SIZE_SZ)
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
#define MINSIZE (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) &
~MALLOC_ALIGN_MASK))
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
简化版的malloc_usable_size=chunksize(p)-SIZE_SZ

具体的可以参考glibc/malloc/malloc.c,有很好的注释。
记得有人说过malloc是不是线程安全的问题,注释里说是的,而且在代码里能看到很多mutex_lock和mutex_unlock。
我有一次代码动态链接的参数表不一致,导致内存被破坏了,malloc陷入了死锁,让我百思不得其解。

此外,关于自己为细粒度malloc建立内存池的问题,先看看malloc是不是已经帮忙做了:

The main properties of the algorithms are:
* For large (>= 512 bytes) requests, it is a pure best-fit allocator,
with ties normally decided via FIFO (i.e. least recently used).
* For small (<= 64 bytes by default) requests, it is a caching
allocator, that maintains pools of quickly recycled chunks.
* In between, and for combinations of large and small requests, it does
the best it can trying to meet both goals at once.
* For very large requests (>= 128KB by default), it relies on system
memory mapping facilities, if supported.

George

unread,
Aug 19, 2012, 12:16:18 PM8/19/12
to sh...@googlegroups.com
#define alloc(a) ({a = (typeof(a)) malloc(sizeof(*a)); )})
or
#define alloc(a, bytes) ({a = (typeof(a)) malloc(bytes)})
����Լ���Ҫ�ɣ� �Ҿ����õ�һ�ַ�����

Reply all
Reply to author
Forward
0 new messages