malloc() 和 memset() 行为

malloc() and memset() behavior

我写了一些代码来查看 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, '[=10=]', (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<<

首先,这是未定义的行为,所以任何事情都有可能发生;正如评论中所说,在我的机器上我得到了与禁用优化完全相同的行为,但是打开优化我在编译时 收到关于潜在缓冲区溢出的警告(令人印象深刻的 gcc 工作! ) 和运行时的大崩溃。更好的是,如果我在 printf 调用之前用 puts 打印它,我会用不同数量的 a.

打印它

不过,我运气不佳,与您有 完全 相同的行为,所以让我们调查一下。我在没有优化和调试信息的情况下编译了你的程序

[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
 = 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 '[=13=]0'
_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 调用(或多或少间接地),在紧邻它给你的内存块上做它的东西(可能标记为已使用) .

长话短说:你拿走了不属于你的记忆,现在正由它的合法所有者在他需要它的第一时间进行修改;没什么特别奇怪或有趣的。

I used malloc() to allocate 15 bytes of memory for a character array, and I wanted to see what would happen if I used memset() incorrectly to set 100 bytes of memory in the pointer I created.

就语言标准而言,发生的是未定义的行为,并且在特定情况下实际发生的任何事情都无法从源代码中预测并且可能不一致跨越不同的 C 实现,甚至是同一程序的不同运行。

I expected to see that memset() had set 15 bytes (and possibly trash some other memory).

这是一个看似合理的结果,但你有任何特定的期望是很危险的。不要假设你可以预测 UB 会采取什么表现形式,即使是根据过去的经验。既然你不应该那样做,也不能从中学到任何有用的东西,那么用 UB 做实验是不值得的。

What I'm seeing when I run the program is that it's setting 26 bytes of memory to the character that I coded.

Any idea why there are 26 bytes allocated for the pointer I created?

谁说你的实验证明是这样的?不仅memset()而且最后的printf()都展示了UB。输出只告诉你输出是什么。那一次。

现在一般来说,malloc 可能会保留比您请求的更大的块。许多实现在内部管理大于一个字节的内存块,例如 16 或 32 字节。但这与程序行为的定义无关,也不会影响您的输出。