Можно ли вычислить факториал значения пропроцессора во время компиляции в C?

#define num 7  \\ user can change this
#define size ????  \\I want this value (factorial of num) to be computed during compile time

int array[size][num];

Я хочу определить array глобально, но его размер зависит от значения препроцессора num. Поэтому я хочу, чтобы значение (факториал num) определялось во время компиляции.

Является ли это возможным? если да, то как?

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

Some programmer dude 13.02.2023 16:36

Вручную поместите что-то вроде (x)*((x-1)>0?(x-1):1)*((x-2)>0?(x-2):1)... в макрос. Вам нужно только приблизиться к паре итераций, так как факториалы растут так быстро, а самые большие поддерживаемые целые числа обычно имеют ширину всего 64 бита.

PSkocik 13.02.2023 16:44

@PSkocik, но это не делается во время предварительной обработки

0___________ 13.02.2023 16:56

@0___________ Это будет целочисленное константное выражение, если X равно.

PSkocik 13.02.2023 17:01

@PSkocik Не во время предварительной обработки компилятор может оптимизировать его для этого. Но препроцессор ничего не знает о выражениях и шагах Си.

0___________ 13.02.2023 17:02

@Someprogrammerdude Я хочу, чтобы массив был глобальным объектом, и, насколько мне известно, компилятор выдает ошибку, если размер этого глобального объекта не указан. Поэтому мне нужно факториальное значение во время компиляции

Cinverse 13.02.2023 17:09

@ 0___________: вариант использования OP - это измерение массива, поэтому выражение должно быть постоянным во время компиляции, но не обязательно известно препроцессору. Что бы это ни стоило, кажется, что это работает и в условных директивах препроцессора, хотя, возможно, и не переносимо.

M Oehm 13.02.2023 17:36

@0___________ OP не совсем нуждается в целочисленном константном выражении, распознаваемом препроцессором (просто целочисленное константное выражение), но, кстати, упомянутый мной подход генерирует выражения, которые распознаются как компилятором, так и препроцессором. Подробности смотрите в моем ответе.

PSkocik 13.02.2023 19:42

Я снова открыл вопрос (возможно, слишком поспешно?), думая, что он отличается в силу требования времени компиляции. Возможно, это должен быть дубликат stackoverflow.com/questions/40733607/… как было предложено, я не знаю.

PSkocik 13.02.2023 19:50
Ускорьте разработку веб-приложений Laravel с помощью этих бесплатных стартовых наборов
Ускорьте разработку веб-приложений Laravel с помощью этих бесплатных стартовых наборов
Laravel - это мощный PHP-фреймворк, используемый для создания масштабируемых и надежных веб-приложений. Одним из преимуществ Laravel является его...
Что такое двойные вопросительные знаки (??) в JavaScript?
Что такое двойные вопросительные знаки (??) в JavaScript?
Как безопасно обрабатывать неопределенные и нулевые значения в коде с помощью Nullish Coalescing
Создание ресурсов API Laravel: Советы по производительности и масштабируемости
Создание ресурсов API Laravel: Советы по производительности и масштабируемости
Создание API-ресурса Laravel может быть непростой задачей. Она требует глубокого понимания возможностей Laravel и лучших практик, чтобы обеспечить...
Как сделать компонент справочного центра с помощью TailwindCSS
Как сделать компонент справочного центра с помощью TailwindCSS
Справочный центр - это веб-сайт, где клиенты могут найти ответы на свои вопросы и решения своих проблем. Созданный для решения многих распространенных...
Асинхронная передача данных с помощью sendBeacon в JavaScript
Асинхронная передача данных с помощью sendBeacon в JavaScript
В современных веб-приложениях отправка данных из JavaScript на стороне клиента на сервер является распространенной задачей. Одним из популярных...
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Отказ от ответственности: Эта статья предназначена только для демонстрации и не должна использоваться в качестве инвестиционного совета.
1
9
138
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

В отдельном файле .h (например, fc.h):

#if num == 1
#define sum 1
#elif num == 2
#define sum 2
#elif num == 3
#define sum 6
#elif num == 4
#define sum 24
#elif num == 5
#define sum 120
#elif num == 6
#define sum 720
#elif num == 7
#define sum 5040
#elif num == 8
#define sum 40320
#elif num == 9
#define sum 362880
#else
#error wrong number
#endif

Применение

#define num 7
#include "fc.h"

int array[sum][num];

Одна опечатка: 8! = 40320, а не 40230

Ingo Leonhardt 13.02.2023 16:52

Вы можете вручную добавить что-то вроде (x) * (((x)-1)>0?((x)-1):1) * (((x)-2)>0?((x)-2):1) ... в макрос. Вам нужно только приблизиться к паре итераций, так как факториалы растут так быстро, а самые большие поддерживаемые целые числа обычно имеют ширину всего 64 бита.

Хотя выражение, подобное показанному выше, может показаться сложным, для x, которое является целочисленным константным выражением (например, 1, 1+2, sizeof(0)*3), гарантированно будет сгенерировано целочисленное константное выражение, т. е. что-то подходит для инициализации размеров статических массивов, размеров битовых полей и меток case). Более того, для аргументов, которые являются целым числом, распознаваемым препроцессором (например, 1, 42u, 0x1000ull), выражение также распознается препроцессором, то есть подходит в качестве аргумента для условного выражения препроцессора #if.

Итак, как вы можете получить такой макрос?

Сначала вам нужна верхняя граница для аргумента факториала, который не будет переполнять unsigned long long (наибольшая гарантированная величина поддерживается препроцессором и C-компилятором, обычно 64-битная ширина).

Что вы можете получить с чем-то вроде

#include <stdio.h>
unsigned long long factorial(unsigned long long X){ if (X<=1) return 1; return X*factorial(X-1); }
int main(){
    int i=2;
    for(; i<100 && factorial(i-1)<factorial(i); i++){ if (0) printf("%016llx \n", factorial(i)); }
    printf("%d\n", i-1); //22
}

И это 22 для 64-битных беззнаковых длинных длин.

Зная, что это 22, вы можете сгенерировать макрос:

 printf("#define FACTORIAL(X) ((X)>22 || (X)<0 ? 0 : (1 ");
 for(int i=0; i<22; i++) printf(" * ((int)+(X)-%d > 0 ? (X)-%dULL : 1)", i, i);
 printf("))\n");

Вышеуказанные отпечатки

#define FACTORIAL(X) ((X)>22 ? 0 : (1  * ((int)+(X)-0 > 0 ? (X)-0ULL : 1) * ((int)+(X)-1 > 0 ? (X)-1ULL : 1) * ((int)+(X)-2 > 0 ? (X)-2ULL : 1) * ((int)+(X)-3 > 0 ? (X)-3ULL : 1) * ((int)+(X)-4 > 0 ? (X)-4ULL : 1) * ((int)+(X)-5 > 0 ? (X)-5ULL : 1) * ((int)+(X)-6 > 0 ? (X)-6ULL : 1) * ((int)+(X)-7 > 0 ? (X)-7ULL : 1) * ((int)+(X)-8 > 0 ? (X)-8ULL : 1) * ((int)+(X)-9 > 0 ? (X)-9ULL : 1) * ((int)+(X)-10 > 0 ? (X)-10ULL : 1) * ((int)+(X)-11 > 0 ? (X)-11ULL : 1) * ((int)+(X)-12 > 0 ? (X)-12ULL : 1) * ((int)+(X)-13 > 0 ? (X)-13ULL : 1) * ((int)+(X)-14 > 0 ? (X)-14ULL : 1) * ((int)+(X)-15 > 0 ? (X)-15ULL : 1) * ((int)+(X)-16 > 0 ? (X)-16ULL : 1) * ((int)+(X)-17 > 0 ? (X)-17ULL : 1) * ((int)+(X)-18 > 0 ? (X)-18ULL : 1) * ((int)+(X)-19 > 0 ? (X)-19ULL : 1) * ((int)+(X)-20 > 0 ? (X)-20ULL : 1) * ((int)+(X)-21 > 0 ? (X)-21ULL : 1)))

И вы можете протестировать этот макрос для целых чисел, распознаваемых препроцессором:

#if FACTORIAL(1)!=1 || FACTORIAL(6)!=720 || FACTORIAL(22) != 0xeea4c2b3e0d80000
   #error ""
#endif

И для целочисленных константных выражений, которые не распознаются препроцессором:

_Static_assert(FACTORIAL(6*sizeof(char))==720,"");

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

John Bollinger 13.02.2023 20:14

@JohnBollinger Можно было бы объединить описанный выше подход + _Generic + встроенную функцию (выражение оператора было бы лучше, ИМХО, если бы только компиляторы сформировались: gcc.gnu.org/bugzilla/show_bug.cgi?id=93239) для создания макрос, который одновременно сохраняет целочисленное константное выражение и предотвращает множественные вычисления, но мне не хочется раздувать ответ. Этот недостаток наивных макросов хорошо известен и в основном ожидаем, когда есть идентификатор только в верхнем регистре.

PSkocik 13.02.2023 20:38

Обычно я пишу короткий сценарий оболочки для предварительного вычисления значений троичного выражения.

// $ fac() { r=1; for ((i=$1;i!=0;--i)); do ((r*=i)); done; echo $r; }; for ((i=1;i<10;++i)); do echo "i == $i ? $(fac $i) : \\"; done
#define FACTORIAL(x) \
i == 1 ? 1 : \
i == 2 ? 2 : \
i == 3 ? 6 : \
i == 4 ? 24 : \
i == 5 ? 120 : \
i == 6 ? 720 : \
i == 7 ? 5040 : \
i == 8 ? 40320 : \
i == 9 ? 362880 : \
-1

int array[FACTORIAL(3)][3];

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