Существует простой прием для этой проблемы:
bool IsPowerOfTwo(ulong x)
{
return (x & (x - 1)) == 0;
}
Примечание, эта функция сообщит true
для [1 112], который не является питанием [1 113]. Если Вы хотите исключить это, вот то, как:
bool IsPowerOfTwo(ulong x)
{
return (x != 0) && ((x & (x - 1)) == 0);
}
Прежде всего поразрядный двоичный файл & оператор из определения MSDN:
Двоичный файл & операторы предопределены для целочисленных типов и bool. Для целочисленных типов, & вычисляет логическое поразрядное И его операндов. Для bool операндов, & вычисляет логическое И его операндов; то есть, результат верен, если и только если оба его операнда верны.
Теперь позволяют нам смотреть на то, как это все теряет значение:
функция возвращает булевскую переменную (верный / ложь) и принимает один входящий параметр типа, неподписанного длинный (x, в этом случае). Давайте ради простоты предположим, что кто-то передал значение 4 и вызвал функцию как так:
bool b = IsPowerOfTwo(4)
Теперь мы заменяем каждое возникновение x с 4:
return (4 != 0) && ((4 & (4-1)) == 0);
Хорошо мы уже знаем это 4! = 0 evals к истинному, пока неплохо. Но что относительно:
((4 & (4-1)) == 0)
Это переводит в это, конечно:
((4 & 3) == 0)
, Но что точно 4&3
?
двоичное представление 4 равняется 100, и двоичное представление 3 равняется 011 (помните & берет двоичное представление этих чисел). Таким образом, мы имеем:
100 = 4
011 = 3
Воображают эти значения сложенными во многом как элементарное дополнение. &
оператор говорит, что, если оба значения равны 1 тогда, результат равняется 1, иначе это 0. Так 1 & 1 = 1
, 1 & 0 = 0
, 0 & 0 = 0
, и 0 & 1 = 0
. Таким образом, мы делаем математику:
100
011
----
000
результат просто 0. Таким образом, мы возвращаемся и смотрим на то, во что теперь переводит наш оператор возврата:
return (4 != 0) && ((4 & 3) == 0);
, Который переводит теперь в:
return true && (0 == 0);
return true && true;
Все мы знаем, что true && true
просто true
, и это показывает, что для нашего примера, 4 питание 2.
Показанный вами синтаксис на языке GNU make называется шаблонным правилом, и он является краеугольным камнем вашего решения. Все, что вам нужно, это добавить способ динамического получения списка файлов .C. Другие показали решения, в которых для этого используется $ (shell)
, но это совершенно неэффективно. Я предлагаю вам вместо этого использовать $ (подстановочный знак), который является встроенной функцией GNU make, разработанной именно для этой цели:
SRCS = $(wildcard *.C)
OBJS = $(patsubst %.C,%.o,$(SRCS))
foo: $(OBJS)
$(CC) -o $@ $^
%.o: %.C
$(CC) $(CPFLAGS) -c $<
Если вы ищете что-то более сжатое, то подойдет и следующее:
foo: $(patsubst %.C,%.o,$(wildcard *.C))
Это просто устраняет переменных и использует тот факт, что GNU make предоставляет шаблонное правило %. o:% .C
по умолчанию, а также правило по умолчанию для связывания исполняемого файла из набора объектов. Лично я бы использовал более подробную версию, так как мне легче читать и поддерживать,
Это пример шаблонного правила , он должен нормально работать в gmake
. Для очень простых сборок, подобных этой, вы в основном можете полагаться на неявные правила . Главное, что вы должны указать в цели, которую вы хотите построить, и ее зависимостях:
CXXFLAGS:=-g -O -Iincludes
LDFLAGS:=-lm
SRCS:=$(shell ls *.C) # select all .C files as source
OBJS:=$(substr .C,.o,$(SRCS) # list the corresponding object files
foo : $(OBJS)
Обратите внимание на использование просто оператора присваивания расширенной переменной (: =
) вместо рекурсивного оператор присваивания ( =
), который может уменьшить объем повторной обработки make в более сложных сборках.