我写了一些代码来查看malloc()
和memset()
的行为,发现一个不知道发生了什么的情况。
我使用malloc()
为一个字符数组分配了15个字节的内存,
想看看如果我错误地使用memset()
在我创建的指针中设置100字节的内存会发生什么。我希望看到memset()
设置了15个字节(并可能浪费了一些其他内存)。运行该程序时,我看到的是它为我编码的字符设置了26个字节的内存。
有人知道为什么为我创建的指针分配了26个字节吗?我正在使用gcc和glibc进行编译。这是代码:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define ARRLEN 14
int main(void) {
/* + 1 for the null terminator */
char *charptr = malloc((sizeof(*charptr) * ARRLEN) + 1);
if (!charptr)
exit(EXIT_FAILURE);
memset(charptr, '\0', (sizeof(*charptr) * ARRLEN) + 1);
/* here's the intentionally incorrect call to memset() */
memset(charptr, 'a', 100);
printf("sizeof(char) ------ %ld\n", sizeof(char));
printf("sizeof(charptr) --- %ld\n", sizeof(charptr));
printf("sizeof(*charptr) --- %ld\n", sizeof(*charptr));
printf("sizeof(&charptr) --- %ld\n", sizeof(&charptr));
printf("strlen(charptr) --- %ld\n", strlen(charptr));
printf("charptr string ---- >>%s<<\n", charptr);
free(charptr);
return 0;
}
这是我得到的输出:
sizeof(char) ------ 1
sizeof(charptr) --- 8
sizeof(*charptr) --- 1
sizeof(&charptr) --- 8
strlen(charptr) --- 26
charptr string ---- >>aaaaaaaaaaaaaaaaaaaaaaaa<<
答案 0 :(得分:5)
首先,这是未定义的行为,因此任何事情都可能发生;如评论中所述,在我的机器上,禁用优化后,我得到的行为与您完全相同,但是启用优化后,我收到了有关在编译时潜在的缓冲区溢出(令人印象深刻的作业gcc!)的警告。运行时发生严重崩溃。更好的是,如果我在puts
调用之前用printf
打印它,则会用不同数量的a
打印它。
不过,我有幸能拥有与您相同的 exact 行为,所以让我们进行调查。我在没有优化和调试信息的情况下编译了您的程序
[matteo@teokubuntu ~/scratch]$ gcc -g memset_test.c
然后我启动调试器,并在printf
之后的第一个memset
上添加一个断点。
Reading symbols from a.out...done.
(gdb) break 20
Breakpoint 1 at 0x87e: file memset_test.c, line 20.
(gdb) r
Starting program: /home/matteo/scratch/a.out
Breakpoint 1, main () at memset_test.c:20
20 printf("sizeof(char) ------ %ld\n", sizeof(char));
现在我们可以在charptr
指向的第26个内存位置上设置硬件写入断点了
(gdb) p charptr
$1 = 0x555555756260 'a' <repeats 100 times>
(gdb) watch charptr[26]
Hardware watchpoint 2: charptr[26]
...等等...
(gdb) c
Continuing.
Hardware watchpoint 2: charptr[26]
Old value = 97 'a'
New value = 0 '\000'
_int_malloc (av=av@entry=0x7ffff7dcfc40 <main_arena>, bytes=bytes@entry=1024) at malloc.c:4100
4100 malloc.c: File o directory non esistente.
(gdb) bt
#0 _int_malloc (av=av@entry=0x7ffff7dcfc40 <main_arena>, bytes=bytes@entry=1024) at malloc.c:4100
#1 0x00007ffff7a7b0fc in __GI___libc_malloc (bytes=1024) at malloc.c:3057
#2 0x00007ffff7a6218c in __GI__IO_file_doallocate (fp=0x7ffff7dd0760 <_IO_2_1_stdout_>) at filedoalloc.c:101
#3 0x00007ffff7a72379 in __GI__IO_doallocbuf (fp=fp@entry=0x7ffff7dd0760 <_IO_2_1_stdout_>) at genops.c:365
#4 0x00007ffff7a71498 in _IO_new_file_overflow (f=0x7ffff7dd0760 <_IO_2_1_stdout_>, ch=-1) at fileops.c:759
#5 0x00007ffff7a6f9ed in _IO_new_file_xsputn (f=0x7ffff7dd0760 <_IO_2_1_stdout_>, data=<optimized out>, n=23)
at fileops.c:1266
#6 0x00007ffff7a3f534 in _IO_vfprintf_internal (s=0x7ffff7dd0760 <_IO_2_1_stdout_>,
format=0x5555555549c8 "sizeof(char) ------ %ld\n", ap=ap@entry=0x7fffffffe330) at vfprintf.c:1328
#7 0x00007ffff7a48f26 in __printf (format=<optimized out>) at printf.c:33
#8 0x0000555555554894 in main () at memset_test.c:20
(gdb)
因此,malloc
只是({或间接地)由printf
在存储块中执行与它给您的存储块相邻的代码调用(可能将其标记为已使用)。 / p>
长话短说:您所拥有的记忆不是您的,现在它的合法所有者在第一次需要它时就对其进行了修改;没有什么特别奇怪或有趣的。
答案 1 :(得分:2)
我使用malloc()为字符数组分配了15个字节的内存, 我想看看如果我错误地使用了memset()会发生什么 在我创建的指针中设置100字节的内存。
就语言标准而言,发生的是未定义的行为,在特定情况下实际发生的任何事情都无法从源代码中预测出来,并且可能在不同的C实现中不一致,或者甚至同一程序的不同运行。
我希望看到 该memset()已设置15个字节(并可能浪费了一些其他内存)。
那将是一个合理的结果,但是您完全有任何特别的期望是危险的。不要以为即使在过去的经验基础上,也无法预测UB将采用的表现形式。而且,由于您不应该这样做,也无法从中学习任何有用的信息,因此尝试使用UB是不值得的。
运行程序时看到的是它设置了26个字节 记忆到我编码的字符。
有什么想法为什么要为我创建的指针分配26个字节?
谁说您的实验证明确实如此?不仅memset()
,而且最后一个printf()
都有UB。输出仅告诉您输出内容。那个时候。
现在,malloc
通常可以保留比您请求的块更大的块。许多实现在内部以大于一个字节的块(例如16或32字节)来管理内存。但这与程序行为的定义无关,它与您的输出无关。