Обновить
14

Пользователь

0,2
Рейтинг
8
Подписчики
Отправить сообщение
Как раз наоборот.

The ‘-j’ option is a special case (see Parallel Execution). If you set it to some numeric value ‘N’ and your operating system supports it (most any UNIX system will; others typically won’t), the parent make and all the sub-makes will communicate to ensure that there are only ‘N’ jobs running at the same time between them all. Note that any job that is marked recursive (see Instead of Executing Recipes) doesn’t count against the total jobs (otherwise we could get ‘N’ sub-makes running and have no slots left over for any real work!)
тыц
-j передается в sub-make автоматически (в отличие от некоторых других флагов), если внешний make понял, что это sub-make. Для этого стоит либо использовать $(MAKE) вместо make, либо (надежнее) ставить + перед соответствующей строкой. На самом деле в этом случае sub-make даже идет параллельно с «внешним», джобы общие и вообще все шикарно работает.

На самом деле я всеми руками за один единственный Makefile. Просто его чуть труднее правильно готовить, а многие разработчики, которых я видел, почему-то не хотят научиться это делать как следует.
Он зайдет в libsound, запустит там make, увидит, что делать ничего не надо, выйдет. Да, уйти в sub-make придется, но и все.

Есть вариант, который используется в некоторых проектах, поставить libsound.a в зависимость libsound/stamp, на который делается touch в конце сборки libsound.a, а при любой правке в каталоге libsound придется делать touch на этот stamp. Такое имеет смысл, если предполагается, что содержимое libsound будет меняться редко и вообще это сторонний код, которому мы верим.
Знает, если ему объяснить. В приведенном примере make ничего не объяснили о зависимостях.
В данном конкретном примере все вызовы P принимают строковые литералы со смещением 0 или 1, так что все вполне управляемо.

Да, это main, поэтому я опустил return.

Функции из libc не имеют отношения к инклюду — инклюд влияет только на подтягивание объявлений функций. Фокус в том, что printf (и поразительно большое число других функций) имеют прототип, эквивалентный прототипу по-умолчанию — возвращают int, принимают то, что в них передали.
Аналогично — думал, какие надо угадать значения R, C, и куда направить u, чтобы получилась вразумительная картинка. Остальное все было как-то почти очевидно (опыт кодгольфа дает о себе знать).

Вопрос к автору — я вместо функции P сделал так:
#define P(x) printf(x)
Это как-то карается? А еще у меня ни ретурнов, ни инклюдов — ворнинги сыплются, но все работает корректно (опять же, опыт кодгольфа).
С конкретно такими «подпроектами» сильно красиво не получается, поэтому через шелл циклы:

SUBDIRS = sound graphics engine
.PHONY: all build clean

all: build

build clean:
    for dir in $(SUBDIRS); do \
        make -C $$dir $@; \
    done


Или с макросами
SUBDIRS=sound graphics engine

define SUB
	# DO NOT REMOVE THIS LINE
	$(MAKE) -C $(1) $(2)
endef

.PHONY: all build clean

all: build

build clean:
	$(foreach dir,$(SUBDIRS),$(call SUB, $(dir),$@))


Вобщем все печально, если не поступить примерно так:
SUBDIRS=sound graphics engine
SOURCES=$(foreach dir, $(SUBDIRS), $(wildcard $(dir)/*.c))
OBJECTS=$(subst .c,.o,$(SOURCES))

.PHONY: all clean

all: program

program: $(OBJECTS)
	$(LD) -o $@ $^

clean:
	-rm $(OBJECTS)


Мы не можем использовать имена каталогов в качестве таргетов (то есть можем, но будем при этом страдать), потому что они с одной стороны существуют, а с другой стороны ни от чего не зависят. Можно было бы для каждого каталога определить связанную с ним статическую либу (sound -> libsound.a), которую бы и собирал соответствующий sub-make, но это не спасет от необходимости делать clean все равно по каталогам (find -name '*.o;' это тоже не самый правильный путь).

Я бы предпочел иметь всю сборку в одном мэйкфайле, чтобы там иметь и список всех исходников, и список всех объектников и все на свете. Но это лично мои предпочтения. Так-то можно и automake использовать и иметь в каждом каталоге совсем крошечные Makefile.am.
В make каждая строка выполняется в отдельном сабшелле, поэтому все сбрасывается.

В данном конкретном случае я бы использовал переменные (особенно когда количество директорий начинает расти), но это уже вопрос по make, а не по C.

Альтернативный вариант — исходники раскидать, а объектники свалить в одну кучу, используя VPATH. Но это опять вопрос сборки, не относящийся напрямую к языку.
Мержить чаще. В том числе и разбивать мерж на несколько — сначала подтянуть «от начала и до этого места», потом от «от этого места и до конца». Еще разумнее мержить по фичам — сначала вмержим к себе эту фичу, потом следующую, потом еще одну.

Чуть более сложный вариант это создать отдельную мерж-ветку — то есть в форке некая ветка, которая начинается от того места, где мастер апстрима. И на ней последовательно делается следующее:

— Попытаться смержить в свой мастер
— Если без конфликтов, то победа
— Если есть конфликты, мерж отменяется, выбирается какой-то один конфликтный кусок и человек, который в этом разбирается, вносит такие правки, чтобы последующий мерж этого куска прошел уже без конфликтов. Делается коммит.
— Повторить
Диффы видны и подсвечены, но, к сожалению, одной большой кучей — если в А и B строки одинаковы, а в C отличается, то вся строка будет подсвечена. Лечится тем, что подсветку можно выборочно включать/отключать командой :diffoff/:diffthis в нужном буфере — :diffoff в буфере А оставит подсвеченными только отличия между B и C. Тут уже вопрос привычки — для заядлых пользователей вима это норма, а для остальных людей не очень. Себя я отношу к первым.
Любой конфликт слияния требует внимательного изучения и ручного разрешения — я могу привести пример, когда все автоматически слилось, но не работает. Вопрос только в том, как сделать это изучение наиболее удобным, позволяя человеку сразу увидеть все нужные отличия.
Забавно, но у меня трехсторонний merge получается просто сразу из коробки через vimdiff (той же самой командой git mergetool). Правда там четыре окна — LOCAL, BASE, REMOTE сверху и результат внизу.
Если при этом выполняется условие «константа 0 является нулевым указателем» — вопросов нет. Но выглядит это странно.
В стандарте есть требование, что константа 0 это нулевой указатель. Про адрес тут речи нет. Кроме того, есть требование, что любые два нулевых указателя равны. Значит никаких других нулевых указателей быть не должно. 0xffffffff это не нулевой указатель. Просто особый невалидный.
А вот нашел C99. Там все написано:

An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.


Теперь я буду знать, что у нулевого адреса и нулевого указателя действительно особый статус, определенный стандартом.
Интересно, никогда раньше не слышал о такой «конвенции». Гугл ничего явного не подсказал, или я что-то не то искал. Зато я узнал, что в Линуксе нулевая страница адресного пространства просто всегда явно маппится без прав доступа, поэтому происходит segfault.

В «особых случаях» нулевой адрес является вполне себе валидной штукой — примеры опять же можно найти в гугле.

Под рукой нет Кернигана-Ритчи, но насколько я помню, разыменование указателя, который не содержит адрес объекта того типа, на который ссылается этот указатель, это Undefined Behavior. Ни о каком особом статусе нулевого адреса я не помню. Может просто невнимательно читал.

Все эти фокусы с указателями, разыменовываниями и прочим могут выглядеть как игра с огнем или чем-то более опасным, но если просто оперировать памятью, структурами и массивами, то оно все выглядит вполне естественным. Выделил память, передал в функцию адрес этой памяти, записал по нужному смещению нужное значение. Никакой магии, все на виду.
С тем же успехом можно разыменовать любой другой указатель (например мой любимый — 5). Указатель это число, некоторый адрес в памяти. Он может принимать очень много значений и очень немногие из них будут соответствовать расположенным в этой памяти объектам нужного типа. NULL это только одно из некорректных значений. Просто оно очень удобно для обозначения «на самом деле тут ничего нет». Все равно, это лучше, чем если бы был указатель на объект, который уже удален, но зато не NULL.
Никогда не понимал до конца вот это вот «в языке есть NULL, поэтому это плохо» — NULL это просто ноль. Или какое-то другое значение. Почему если есть возможность присводить какое-то значение в какую-то переменную, то сразу язык от этого портится?
Надо такую со всякими умными IDE скрестить, чтобы помогала в качестве виртуального «парного» программиста. Смотрела в код, в фоне прогоняла всякие там тесты и style-чеки, напоминала о том, что надо бы закоммиттиться и так далее.

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

Информация

В рейтинге
3 544-й
Зарегистрирован
Активность