циркуляр # определяет в C - PullRequest
       21

циркуляр # определяет в C

2 голосов
/ 19 апреля 2019

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

Один из моих заголовочных файлов содержит следующие строки:

/* macros to seed the random number generator */
/* This is needed on Windows which throws an error due to 'random' 
not being defined on MingW.  I'll clean it up later. */
#define srandom srand
#define random rand

/* New random seed function. */
#define srand srandom
#define rand random

#define rnd(x)  ((int)(rand() % (x)) + 1)
#define rund(x) ((int)(rand() % (x)))

Кроме того, у меня есть справочные страницы для всех четырех функций rand, srand, random, srandom, поэтому я хотел бы предположить, что это все действительные функции языка Си.

Можете ли вы помочь мне понять / выяснить, что происходит, когда я звоню rnd(10)? Это неопределенное поведение? Одно определение как-то "переопределяет" другое?

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


Просто быстрое замечание, что я почти уверен, что «много младших чисел», которое я видел, было, вероятно, ошибкой в ​​приоритетах операторов и скобках, из-за чего длинное логическое значение всегда оценивалось как «true».

Ответы [ 5 ]

3 голосов
/ 19 апреля 2019

Эффект рекурсивных макросов препроцессора четко определен: при расширении определения макроса foo, если имя foo найдено снова, оно остается одним.Так что в вашем случае rand расширяется до random, который расширяется до rand, который не расширяется далее.

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

Функции rand и srand определены основным языком C и доступны, когда вы включаете stdlib.h (кроменекоторые встроенные реализации, которые не предоставляют полную библиотеку C).random и srandom являются дополнениями Unix / POSIX, и чтобы использовать их, вам нужно определить символ, такой как #define _XOPEN_SOURCE 500 перед #include <stdlib.h>.

Я озадачен тем, как эти определения могут исправить что-либона MinGW.Если MinGW вообще не определяет random, то вызов random() расширяется препроцессором до random() и все еще пытается вызвать функцию, которая не определена.Похоже, что либо серия последовательных взломов привела к безвредному остатку, либо кто-то нащупал решение проблемы, в итоге решил ее по-другому, но совершил эту часть, хотя это не способствовало решению.

2 голосов
/ 19 апреля 2019

Препроцессор C не будет оценивать одно и то же расширение макроса более одного раза для данного расширения.То есть он разорвет цикл, как только идентифицирует его.Следовательно, в вашем случае произойдут следующие расширения:

rnd(10) // list of available macros: [srandom, random, srand, rand, rnd(x), rund(x)]
((int)(rand() % (10)) + 1) // list of available macros: [srandom, random, srand, rand, rund(x)]
((int)(random() % (10)) + 1) // list of available macros: [srandom, random, srand, rund(x)]
((int)(rand() % (10)) + 1) // list of available macros: [srandom, srand, rund(x)]

На последнем шаге препроцессор не найдет больше доступных расширений (рандовое уже было сделано)и сдастся.

Это не имеет ничего общего с тем, почему вы видите больше маленьких чисел, чем больших.Это может быть связано с различными определениями RAND_MAX.В очень надуманном примере, если RAND_MAX = 10, то 0 имеет 18% вероятности обнаружения, в то время как другие числа имеют 9% шанс.Мало того, что типичные реализации rand имеют низкую энтропию для младших битов.Если для вас важно иметь равномерное распределение, вы можете заглянуть за пределы стандартной библиотеки.

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

В алгоритме blue paint символ оценивается не более одного раза. rand было окрашено в синий цвет после того, как оно было возвращено random, поэтому rand больше не будет расширяться до random.

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

У вашего компилятора есть магическая опция -E, чтобы увидеть расширения.

https://godbolt.org/z/_PIvet

int foo(int x)
{
    return rand(x);
}

int foo(int x)
{
    return rnd(x);
}

расширяется до

int foo(int x)
{
    return rand(x);
}

int foo(int x)
{
    return ((int)(rand() % (x)) + 1);
}

Кстати ИМО лучшевместо этого использовать встроенные функции.

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

Похоже, что первый препроцессор обновит источник и заменит srand на srandom, затем снова обновит источник и заменит srandom на srand.Я проверил с cpp, и это выглядит нормально.Программа работает также.У меня были мысли о макросе srand, поскольку в stdlib.h есть функции с одинаковым именем, но ничего плохого не произошло.

...