Почему это работает, если размер буфера меньше, чем nbyte?

Коды такие:

#define BUFSIZ 5
#include <stdio.h>
#include <sys/syscall.h>

main()
{
    char buf[BUFSIZ];
    int n;
    n = read(0, buf, 10);
    printf("%d",n);
    printf("%s",buf);
    return 0;
}

Я вводabcdefg тогда и вывод:

8abcdefg

в read(0, buf, 10);, 10 больше чем 5, который является размером buf, Но, похоже, это не приведет к неправильному результату. У кого-нибудь есть идеи по этому поводу? Спасибо!

2 ответа

Решение

Это причуды того, как работает распределение в Си. В стеке выделен буфер, который на самом деле является просто частью непрерывной памяти, которую вы можете читать и записывать. Тот факт, что вам разрешено списывать конец этого массива, означает, что в этом случае он просто работает. Возможно, на вашей машине с вашим конкретным компилятором и компоновкой стека, вы не перезаписываете ничего важного:-)

Полагаться, что это поведение одинаково между версиями компилятора, не рекомендуется.

Вы можете, в принципе, 1 читать и писать по любому адресу, но доступ к данным безопасен и целесообразен только организованным, четко определенным образом.

Цель выделения памяти (явной или неявной) - привести порядок в хаос. Когда вы объявляете buf массив, небольшой блок памяти зарезервирован в стеке.
Обычно распределения имеют определенное выравнивание (и иногда определенный минимальный размер, также операционная система может обнаруживать неправильные обращения только на очень грубом уровне), поэтому между выделенными блоками памяти и небольшими областями, которые вы можете, часто будут небольшие промежутки. Пишите и читайте, по-видимому, без "ничего плохого", но вы должны сделать вид, что это не так, и вам даже не следует думать об использовании этих деталей реализации в ваших интересах.

Ваш пример кода "работает", потому что вам не повезло не попасть на нераспределенную или защищенную от записи страницу памяти, и вы не перезаписали другое жизненно важное значение стека, которое могло бы вызвать сбой приложения (например, адрес возврата функции).
Я намеренно говорю "не повезло", а не "повезло", так как тот факт, что это работает, не очень хорошая вещь. Это неверный код 2, и такой код должен рано падать, чтобы вы могли обнаружить и устранить проблему. В противном случае это может привести к очень сложной диагностике проблем, которые, по-видимому, возникают в совершенно несвязанном времени или месте. Даже если он работает сейчас, у вас нет никаких гарантий, что он будет работать завтра (или на другом компьютере, или с другим компилятором, или с немного другим кодом).

Распределение памяти обычно состоит из трех этапов. Это запрос на выделение операционной системы, выполняемый библиотекой C (который обычно не соответствует вашим запросам), за которым следует некоторая бухгалтерия, выполненная в библиотеке, и обещание, данное вами. На уровне операционной системы фактическое физическое распределение на уровне страницы происходит по требованию при первом обращении к памяти, предполагается, что библиотека C запросила выделение для доступного местоположения ранее.
В случае размещения в стеке процесс несколько проще на библиотечном уровне, поскольку на самом деле он должен уменьшать только один специальный регистр, но для вас это в основном не имеет значения. Концепция остается прежней.

Обещание, которое вы даете, состоит в том, что вы будете только когда-либо читать или писать в согласованную область, и это главное, что важно для вас.

Может случиться так, что вы нарушите свое обещание (умышленно или случайно), и оно все еще "работает", но это чистое совпадение.
В стеке вы рано или поздно перезапишете либо хранилище некоторых локальных переменных (которые могут остаться незамеченными, если они кэшируются в регистре), и, наконец, адреса возврата, что почти наверняка вызовет сбой (или подобное нежелательное поведение) когда функция возвращается. В куче вы можете перезаписать некоторые другие данные программы или получить доступ к странице, которая не была передана операционной системе как зарезервированная. В этом случае программа будет немедленно прекращена.


1 Давайте на минуту не будем рассматривать виртуальную память и защиту страниц.
2 Строго говоря, это не неправильный код, а код, который вызывает неопределенное поведение. Однако перезапись нераспределенной памяти, на мой взгляд, достаточно серьезна, чтобы заслужить ярлык "неверный".

Другие вопросы по тегам