Мысль в общем правильная, в верном направлении
Рассмотрим множество из N различных элементов M = {a(1),..,a(N)}. Любое его подмножество может быть представлено в виде сигнатуры (s(1),..,s(N)), где s(i) = 0|1 имеет смысл 'элемент a(i) входит в подмножество S'. Поскольку элементы M различны, любой комбинации из 0 и 1 длиной N однозначно соответствует некоторое подмножество исходного множества M, и наоборот, любому подмножеству M соответствует некоторая комбинация нулей и единиц. А теперь вспомним, что целые числа в двоичной системе представляются именно как последовательность нулей и единиц. Рассмотрим, например, множество неотрицательных целых чисел от 0 до 2^N-1 в двоичном представлении:
0 0000..0000 (N нулей)
1 0000..0001 (N-1 нуль, последняя 1)
2 0000..0010 (N-1 нуль, предпоследняя 1)
3 0000..0011 (N-2 нуля, 2 предпоследние 1)
и т.д.
2^N-1 1111..1111 (N единиц)
Очевидно, мы таким образом установили вз.-однозначное соответствие между множеством целых чисел от 0 до 2^N-1 и множеством всевозможных подмножеств M.
Теперь вопрос, как все это запрограммировать. Думаю, что для учебных целей достаточно будет смоделировать множество с небольшим числом элементов, например, N<=32. Тогда все элементарно: объявляешь переменную типа unsigned long (32 бита, как раз) и в последовательно ее инкрементируешь от 0 до 2^N-1. А в цикле просто выводишь ее двоичное представление и интерпретируешь как сигнатуру подмножества.