面试题:malloc(0)会发生什么?

故事要从前两天交流群中一位同学提到的这个问题开始:
这个问题看起来十分刁钻,不过稍有常识的人都知道,制定 c 标准的那帮语言律师也不是吃白饭的,对这种奇奇怪怪的问题一定会有定义。翻阅c17 标准 草案 n2176,在 7.22.3 节里,有如下说法:
the order and contiguity of storage allocated by successive calls to the aligned_alloc, calloc, malloc, and realloc functions is unspecified. the pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated). the lifetime of an allocated object extends from the allocation until the deallocation. each such allocation shall yield a pointer to an object disjoint from any other object. the pointer returned points to the start (lowest byte address) of the allocated space. if the space cannot be allocated, a null pointer is returned. if the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned to indicate an error, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.在这里,标准委员会明确规定了:当 malloc 接到的参数为 0 时,其行为是由实现定义的(implementation-defined)。由实现定义的行为这个词就提醒我们,在实际编程时如果要考虑到程序在多个运行环境下进行运行时,不能对 malloc 返回的数值进行任何假设。换言之,没事儿不要吃饱了撑的在实际编程中写下 malloc(0) 这种天怒人怨的代码。但是,这个无意义的问题吸引了我的兴趣。因此我开始查阅 glibc 的源代码,依此了解在 glibc 下,mallloc(0) 的行为。在 glibc2.27/malloc/malloc.c 中,有如下注释:/* malloc(size_t n) returns a pointer to a newly allocated chunk of at least n bytes, or null if no space is available. additionally, on failure, errno is set to enomem on ansi c systems. if n is zero, malloc returns a minumum-sized chunk. (the minimum size is 16 bytes on most 32bit systems, and 24 or 32 bytes on 64bit systems.) on most systems, size_t is an unsigned type, so calls with negative arguments are interpreted as requests for huge amounts of space, which will often fail. the maximum supported value of n differs across systems, but is in all cases less than the maximum representable value of a size_t.*/ 注释已经说的很清楚了,当我们执行 malloc(0) 时,我们实际会拿到一个指向一小块内存的指针,这个指针指向的(分配给我们的)内存的大小是由机器决定的。 细读代码,可以发现,将读入的内存大小进行转换是由宏 checked_request2size 实现的。
相关的宏定义如下:/* pad request bytes into a usable size -- internal version */#define request2size(req) (((req) + size_sz + malloc_align_mask < minsize) ? minsize : ((req) + size_sz + malloc_align_mask) & ~malloc_align_mask)/* same, except also perform an argument and result check. first, we check that the padding done by request2size didn't result in an integer overflow. then we check (using request_out_of_range) that the resulting size isn't so large that a later alignment would lead to another integer overflow. */#define checked_request2size(req, sz) ({ (sz) = request2size (req); if (((sz) < (req)) || request_out_of_range (sz)) { __set_errno (enomem); return 0; } }) 也就是说,我们能申请到的数值最小为 minsize ,这个 minsize 的相关定义如下: /* the smallest possible chunk */#define min_chunk_size (offsetof(struct malloc_chunk, fd_nextsize))/* the smallest size we can malloc is an aligned minimal chunk */#define minsize (unsigned long)(((min_chunk_size+malloc_align_mask) & ~malloc_align_mask))/* the corresponding bit mask value. */#define malloc_align_mask (malloc_alignment - 1)/* malloc_alignment is the minimum alignment for malloc'ed chunks. it must be a power of two at least 2 * size_sz, even on machines for which smaller alignments would suffice. it may be defined as larger than this though. note however that code and data structures are optimized for the case of 8-byte alignment. */#define malloc_alignment (2 * size_sz < __alignof__ (long double) ? __alignof__ (long double) : 2 * size_sz)#ifndef internal_size_t# define internal_size_t size_t#endif/* the corresponding word size. */#define size_sz (sizeof (internal_size_t))/* this struct declaration is misleading (but accurate and necessary). it declares a view into memory allowing access to necessary fields at known offsets from a given base. see explanation below.*/struct malloc_chunk { internal_size_t mchunk_prev_size; /* size of previous chunk (if free). */ internal_size_t mchunk_size; /* size in bytes, including overhead. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk; /* only used for large blocks: pointer to next larger size. */ struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ struct malloc_chunk* bk_nextsize;};// gcc 提供/* offset of member member in a struct of type type. */#define offsetof(type, member) __builtin_offsetof (type, member) 至此,我们就可以根据这些计算出使用 glibc 在我们的电脑上运行时 malloc 出的最小空间的大小了。计算完后,还可以根据 malloc_usable_size 判断自己的计算是否正确,样例代码如下:#include #include int main(void) { char *p = malloc(0); printf(address: 0x%x.length: %ld.,p,malloc_usable_size(p)); return 0;} 该样例在我电脑内输出的结果为 24。
因此,我们知道了,在 glibc 下,执行 malloc 会得到一个指向分配给我们的大小为 24 字节的内存空间的指针。
但这只是在 glibc 下的结果,在其他 c 标准库实现内,可能你会得到一个空指针。因为标准中提到了,对于 malloc(0) 这种故意挑事的代码,实现时可以返回一个空指针作为回礼。

5G商用部署提速,租赁将平摊风险
集成电路优缺点、检测分析、检测常识、分类
中国移动对入网的不同基站射频单元设备开启深度休眠功能
用单片机配置CPLD器件
电动机的传动装置安装方法
面试题:malloc(0)会发生什么?
电流互感器变比检验的方法
iSuppli:中国3G手机出货今年将达4297万部
加密货币经济生态系统的原生代币Binance的作用介绍
孙建宏:数字化驱动城市交通管理模式创新转变
国内光纤宽带产业发展三大要点
优信2019关键词预测:缺钱、债务、新危机
威盛起诉苹果侵犯其三项处理器专利
Uber无人车致死案结果公布 把问题归咎于Uber的安全文化
实现贴装智能控制的关键
情怀诺基亚6首战告捷 LGG6将亮相MWC
Comcast将为美国家庭宽带实施流量限制
紫光宏茂微电子宣布公司成功实现大容量企业级3D NAND芯片封测的规模量产
罗德与施瓦茨为CSA集团提供先进设备 ROHM确立150V GaN HEMT量产体制
openwrt路由器功能_openwrt路由器设置上网