Как делает кастинг функции, на самом деле работают в C?

Это не имеет никакого отношения к выделению памяти как таковой.

Проблема в этих строках

        int zerosneeded = length - tmp.size();

        while (zerosneeded != 0) {
            tmp.push_back(0);
            zerosneeded--;
        }

, не зная, почему он делает то, что делает, у меня возникает вопрос, глядя на код: «Может ли zerosneeded быть меньше нуля?». Тем более что zerosneeded определяется как int вместо unsigned int (применимо к остальной части кода).

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

int zerosneeded = length - tmp.size();
if(zerosneeded < 0)
{
    std::cout << "fatal, zerosneeded < 0 \n";
    throw std::runtime_exception("fatal, zerosneeded < 0");
}

Надеюсь, это поможет в вашей отладке.

Редактировать

Относительно того, почему нули нужны, является отрицательным:

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

int possibilities = std::pow(symbols, length);

при отправке значений в tmp мы в основном находим первый x такой, что длина ^ x> частное. Частное находится в диапазоне [0, символы ^ длина] и используется push-значениями в tmp.

int quotient = i;
while (quotient!=0) {
    tmp.push_back(quotient % length);
    quotient = quotient / length;
}

Если символы ^ длина> длина ^ длина, первый x такой, что длина ^ x> частное, дает x> длину, в результате чего int zerosneeded = length - tmp.size(); становится отрицательным.

Для примера мы имеем длину = 4, символы = 6, поэтому коэффициенты имеют диапазон [0,6 ^ 4] = [0, 1296]. Но уже для 256 мы имеем 4 ^ 4 = 256 => 4 ^ 5> 256, поэтому наш x = 5 => zerosneeded = 4 - 5 = -1.

Это ничего особенного для длины = 4 и символов = 6, и на самом деле должно происходить до тех пор, пока длина < символов.

6
задан Jonathan Leffler 7 August 2009 в 04:06
поделиться

5 ответов

Имя функции является указателем при использовании как таковое. Это несколько похоже на то, как имя массива является указателем на его первый элемент.

При этом вызов функции через указатель с типом, отличным от фактического прототипа функции (как в вашем примере), является неопределенным поведением. Не делайте этого.

Приложение

Если преобразованный указатель используется для вызова функции, тип которой не совместим с указанным типом, поведение не определено.

из раздел 6.3. 2.3 стандарта С.

5
ответ дан 8 December 2019 в 14:47
поделиться

Указатель в C - это адрес, то есть число, хранящееся в каком-то месте. Функция в C - это адрес некоторого кода. Эти двое - одно и то же.

2
ответ дан 8 December 2019 в 14:47
поделиться

Правильный термин - распад . Функция foo распадается на указатель на foo перед приведением. Само приведение будет неактивным на всех платформах, о которых я могу подумать.

Тем не менее, обратите внимание, что поведение программы, содержащей такое приведение, не определено стандартом C.

2
ответ дан 8 December 2019 в 14:47
поделиться

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

Я должен пояснить, что назначение копирует указатель функции. Но в C все указатели на функции являются просто указателями. Тип и приведение - все это клей компилятора.

Другое уточнение: Стандарт определяет (в 6.5.2.2), что поведение не определено, если вызывающая сторона использует несовместимые типы. Например, приведение функции, которая возвращает void, к функции, которая возвращает int, и затем вызов этой функции, возвращаемое значение не имеет смысла. Перед вызовом функции рекомендуется преобразовать функцию в совместимый тип, иначе вы можете получить неожиданные результаты.

5
ответ дан 8 December 2019 в 14:47
поделиться

Это будет комментарий к ответу Рика С. Петти, но он не умещается в 300 символов.

Стандарт C не очень ограничительный - указатели на объекты (и функции не являются объектами) могут быть преобразованы в указатель на void и обратно без проблем. POSIX требует, чтобы указатели на функции имели одинаковый размер и могли быть преобразованы в указатель на void.

POSIX 2008 - Общая информация - Среда компиляции

2.12.3 Типы указателей

Все типы указателей на функции должны иметь то же представление, что и указатель типа на void. Преобразование указателя функции в void * не должно изменять представление. Значение void *, полученное в результате такого преобразования, может быть преобразовано обратно в исходный тип указателя функции, используя явное приведение, без потери информации. Стандарт ISO C не требует этого, но это требуется для соответствия POSIX.

2
ответ дан 8 December 2019 в 14:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: