#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) определялось во время компиляции.
Является ли это возможным? если да, то как?
Вручную поместите что-то вроде (x)*((x-1)>0?(x-1):1)*((x-2)>0?(x-2):1)... в макрос. Вам нужно только приблизиться к паре итераций, так как факториалы растут так быстро, а самые большие поддерживаемые целые числа обычно имеют ширину всего 64 бита.
@PSkocik, но это не делается во время предварительной обработки
@0___________ Это будет целочисленное константное выражение, если X равно.
@PSkocik Не во время предварительной обработки компилятор может оптимизировать его для этого. Но препроцессор ничего не знает о выражениях и шагах Си.
@Someprogrammerdude Я хочу, чтобы массив был глобальным объектом, и, насколько мне известно, компилятор выдает ошибку, если размер этого глобального объекта не указан. Поэтому мне нужно факториальное значение во время компиляции
@ 0___________: вариант использования OP - это измерение массива, поэтому выражение должно быть постоянным во время компиляции, но не обязательно известно препроцессору. Что бы это ни стоило, кажется, что это работает и в условных директивах препроцессора, хотя, возможно, и не переносимо.
@0___________ OP не совсем нуждается в целочисленном константном выражении, распознаваемом препроцессором (просто целочисленное константное выражение), но, кстати, упомянутый мной подход генерирует выражения, которые распознаются как компилятором, так и препроцессором. Подробности смотрите в моем ответе.
Я снова открыл вопрос (возможно, слишком поспешно?), думая, что он отличается в силу требования времени компиляции. Возможно, это должен быть дубликат stackoverflow.com/questions/40733607/… как было предложено, я не знаю.
В отдельном файле .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
Вы можете вручную добавить что-то вроде (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++).
@JohnBollinger Можно было бы объединить описанный выше подход + _Generic + встроенную функцию (выражение оператора было бы лучше, ИМХО, если бы только компиляторы сформировались: gcc.gnu.org/bugzilla/show_bug.cgi?id=93239) для создания макрос, который одновременно сохраняет целочисленное константное выражение и предотвращает множественные вычисления, но мне не хочется раздувать ответ. Этот недостаток наивных макросов хорошо известен и в основном ожидаем, когда есть идентификатор только в верхнем регистре.
Обычно я пишу короткий сценарий оболочки для предварительного вычисления значений троичного выражения.
// $ 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];
C поддерживает массивы переменной длины, нет необходимости в макросах и вычислениях времени компиляции для размеров массивов. Разве что у вас есть очень специфические требования или ограничения для вашего задания (о которых вы нам ничего не сообщаете)?