поведение malloc () и memset () - PullRequest
       34

поведение malloc () и memset ()

0 голосов
/ 19 января 2019

Я написал код, чтобы посмотреть, как ведут себя 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 голосов
/ 19 января 2019

Я использовал malloc (), чтобы выделить 15 байтов памяти для массива символов, и я хотел посмотреть, что произойдет, если я неправильно использовал memset () установить 100 байт памяти в указателе, который я создал.

Что происходит, это неопределенное поведение в том, что касается языкового стандарта, и то, что действительно происходит в конкретном случае, не предсказуемо из исходного кода и может быть непоследовательным в разных реализациях C или даже различные запуски одной и той же программы.

Я ожидал увидеть этот memset () установил 15 байтов (и, возможно, очистил какую-то другую память).

Это был бы один правдоподобный результат, но то, что у вас были какие-то особые ожидания, вообще опасно. Не думайте, что вы можете предсказать, что проявит UB, даже не основываясь на прошлом опыте. А поскольку вы не должны этого делать и не можете извлечь из этого ничего полезного, экспериментировать с UB не стоит.

Когда я запускаю программу, я вижу, что она устанавливает 26 байт памяти персонажу, которого я закодировал.

Есть идеи, почему для созданного указателя выделено 26 байтов?

Кто сказал, что ваш эксперимент показывает, что это так? Не только memset(), но и последние printf() экспонаты UB. Выходные данные не говорят вам ничего, кроме того, что был выход. На этот раз.

В общем, malloc может зарезервировать блок большего размера, чем вы запрашиваете. Многие реализации внутренне управляют памятью порциями размером более одного байта, например, 16 или 32 байта. Но это никак не связано с определенностью поведения вашей программы и не влияет на ваш вывод.

0 голосов
/ 19 января 2019

Прежде всего, это неопределенное поведение, поэтому может произойти все что угодно;Как сказано в комментарии, на моей машине я получаю точно такое же поведение с отключенной оптимизацией, но, включив оптимизацию, я получаю предупреждение о потенциальном переполнении буфера во время компиляции (впечатляющая работа gcc!) и большойсбой во время выполнения.Еще лучше, если я напечатаю его с puts перед printf вызовами, я напечатаю его с другим номером 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));

теперь мы можем установить аппаратную точку останова записи в 26-й ячейке памяти, на которую указывает charptr

(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, выполняющего свою работу в блоке памяти, непосредственно смежном с тем, который он вам дал (возможно, пометив его как использованный).

Короче говоря: вы взялипамять, которая была не вашей, и теперь она в первый раз модифицируется ее законным владельцем, когда ему это нужно;ничего особенно странного или интересного.

...