Чаще всего побитовое отрицание используется для того, чтобы сохранить установленные биты нетронутыми при побитовом умножении.
Например:
У тебя есть переменная с битовыми флажками
0101 0011
Здесь 8 бит и в каждом записан флажок: 0 - флажка нет, 1 - флажок есть.
Вот тебе надо справа снять самый крайний флажок, потому что это что-то означает в программе. (Типа был какой-то файл на диске, а потом его стёрли и тебе надо это пометить, чтобы программа знала.)
Чтобы снять самый правый флажок, нужно
0101 0011
превратить в
0101 0010
А как это сделать?
Нужно взять значение переменной, поменять его и результат сохранить обратно.
Чтобы его поменять, мы делаем маску и побитово умножаем её на первоначальное значение (операция &).
То есть нам нужно выполнить
0101 0011
&
1111 1110
Тогда самый правый флажок станет нулём, а остальные флажки останутся незатронутыми, так как побитовое умножение на 1 даёт то же самое, что там и было.
И вот тут-то возникает главный вопрос: как сделать эту маску?
Мы можем её записать напрямую или получить с помощью побитовых операций.
Мы берём число 1, оно выглядит как
0000 0001
применяем к нему операцию побитового отрицания (операция ~)
1111 1110
и видим свою маску.