c - Действия, которые должны быть неопределенными, по-видимому, действуют очень «определенно». Почему? - PullRequest
0 голосов
/ 03 февраля 2020

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

В частности, я записывал целое число за пределы mallo c (1), как таковое.

int *x = malloc(1);
*x = 123456789;

Это достаточно мало чтобы поместиться в 4 байта (мой компилятор предупреждает меня, что он переполнится, если он слишком большой, что имеет смысл), но все же явно больше, чем один байт, однако все равно как-то работает. Я не смог запустить ни одного теста, который бы не работал ни в очень «определенном» виде, ни в segfault сразу. Такие тесты включают в себя многократную перекомпиляцию и запуск программы, а также вывод значения x, попытки записи поверх него с помощью гигантского массива и попытки записи поверх него с массивом длины 0, выходящим за его границы.

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

Может кто-нибудь порекомендовать тест, который я могу использовать для демонстрации неопределенного поведения? Мой компилятор (Mingw64 на Windows 10) как-то делает что-то, чтобы компенсировать мою кажущуюся глупость? Где носовые демоны?

Ответы [ 2 ]

2 голосов
/ 06 февраля 2020

Одним из товарных знаков неопределенного поведения является то, что один и тот же код может вести себя по-разному на разных компиляторах или с разными настройками компилятора.

Учитывая этот код:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *x = malloc(1);
    x[100000] = 123456789;
    return 0;
}

Если я скомпилирую это на моей локальной машине с -O0 и запустите его, код segfaults. Если я компилирую с -O3, это не так.

[dbush@centos72 ~]$ gcc -O0 -Wall -Wextra -o x1 x1.c 
[dbush@centos72 ~]$ ./x1
Segmentation fault (core dumped)
[dbush@centos72 ~]$ gcc -O3 -Wall -Wextra -o x1 x1.c 
[dbush@centos72 ~]$ ./x1
[dbush@centos72 ~]$ 

Конечно, это только на моей машине. Ваш может сделать что-то совсем другое.

2 голосов
/ 06 февраля 2020

Термин «неопределенное поведение» воплощает два разных понятия: действия, поведение которых ничем не определено, и действия, поведение которых не определено стандартом C, но определено многими реализациями. В то время как некоторые люди, включая сопровождающих некоторых компиляторов, отказываются признавать существование второй категории, авторы Стандарта прямо описали это:

Неопределенное поведение дает разработчику лицензию не перехватывать определенную программу ошибки, которые трудно диагностировать. Он также определяет области возможного соответствующего расширения языка: разработчик может расширить язык, предоставив определение официально неопределенного поведения.

В большинстве реализаций ваша программа будет примером первого рода. Реализации, как правило, для собственного удобства заполняют небольшие запросы на выделение до определенного минимального размера, а также дополняют большие запросы на выделение при необходимости, чтобы сделать их кратными определенному размеру. Однако они обычно не документируют это поведение. Ваш код должен * ожидать, что будет вести себя осмысленно в реализации, которая документирует поведение malloc достаточно подробно, чтобы гарантировать наличие необходимого количества места; в такой реализации ваш код будет вызывать UB второго типа.

Многие виды задач были бы невозможны или нецелесообразны без использования второго типа UB, но такая эксплуатация обычно требует отключения определенных оптимизаций компилятора и диагностики c особенности. Я не могу придумать ни одной причины, по которой код, который хотел бы получить пространство для 4 байтов, мог бы только malloc один, если только он не был разработан для проверки поведения распределителя, который использовал бы хранилище сразу после окончания выделения для конкретного документированного цель.

...