malloc()和memset()行为

时间:2019-01-18 22:59:13

标签: c gcc malloc glibc memset

我写了一些代码来查看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<<

2 个答案:

答案 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字节)来管理内存。但这与程序行为的定义无关,它与您的输出无关。