Обычной методикой, применимой во многих случаях, является использование набора значений
#define, либо через перечисления. В данной главе API nftw() (описанный далее) также использует флаги. Для поля f_flag структуры struct statvfs есть только два флага:#define ST_RDONLY 1 /* файловая система только для чтения */
#define ST_NOSUID 2 /* setuid/setgid не разрешены */
Физически каждая именованная константа представляет различные позиции битов в значении f_flag
struct statvfs.Флаги устанавливаются, проверяются и очищаются с помощью побитовых операторов С. Например, statvfs()
int statvfs(const char *path, struct statvfs *vfs) {
/* заполнить большую часть *vfs */
vfs->f_flag = 0; /* Убедиться, что начинается с нуля */
if (
vfs->f_flag |= ST_RDONLY; /* Добавить флаг ST_RDONLY */
if (
vfs->f_flag |= ST_NOSUID; /* Добавить флаг ST_NOSUID */
/* оставшаяся часть процедуры */
}
Побитовый оператор И проверяет, установлен ли флаг, а сочетание побитовых операторов И и дополнения очищает флаг:
if ((vfs.f_flag & ST_RDONLY) != 0) /* True, если флаг ST_RDONLY */
vfs.f_flag &= ~(ST_RDONLY|ST_NOSUID); /* Очистить оба флага */
Побитовые операторы отпугивают, если вы не использовали их ранее. Однако, только что показанный код примера представляет обычный стиль С. Тщательно изучите каждую операцию; возможно, нарисуйте себе несколько картин, показывающих работу этих операторов. Однажды разобравшись с ними, вы можете тренировать себя, распознавая эти операторы как
Причина использования флагов кроется в том, что они обеспечивают значительную экономию пространства данных. Одно поле unsigned long
f_flag.[82] Если бы вы использовали для каждого флага отдельно поле char, это потребовало бы использования 11 байтов вместо четырех, используемых unsigned long. Если бы у вас было 32 флага, это были бы 32 байта вместо четырёх!