Мне нравятся оба решения. Вот еще один способ, который приходит мне в голову (вероятно, не лучше).
((~((unsigned int)0) << k) >> (k + n)) << n
EDIT:
В моей предыдущей версии была ошибка (она была без неподписанного типа int). Проблема заключалась в том, что ~0 >> n
добавляет 1 с перед, а не 0.
И да, у этого подхода есть один большой недостаток; он предполагает, что вы знаете число бит целочисленного типа по умолчанию, или другими словами, он предполагает, что вы действительно знаете k, тогда как другие решения не зависят от k. Это делает мою версию менее переносимой или, по крайней мере, труднее переносить. (Также используются 3 смены, оператор сложения и побитового отрицания, что является двумя дополнительными операциями.)
Так что вам лучше использовать один из других примеров.
Вот небольшое тестовое приложение, созданное Джонатаном Леффлером для сравнения и проверки результатов различных решений:
#include <stdio.h>
#include <limits.h>
enum { ULONG_BITS = (sizeof(unsigned long) * CHAR_BIT) };
static unsigned long set_mask_1(int k, int m, int n)
{
return ~(~0 << m) << n;
}
static unsigned long set_mask_2(int k, int m, int n)
{
return ((1 << m) - 1) << n;
}
static unsigned long set_mask_3(int k, int m, int n)
{
return ((~((unsigned long)0) << k) >> (k + n)) << n;
}
static int test_cases[][2] =
{
{ 1, 0 },
{ 1, 1 },
{ 1, 2 },
{ 1, 3 },
{ 2, 1 },
{ 2, 2 },
{ 2, 3 },
{ 3, 4 },
{ 3, 5 },
};
int main(void)
{
size_t i;
for (i = 0; i < 9; i++)
{
int m = test_cases[i][0];
int n = test_cases[i][1];
int k = ULONG_BITS - (m + n);
printf("%d/%d/%d = 0x%08lX = 0x%08lX = 0x%08lX\n", k, m, n,
set_mask_1(k, m, n),
set_mask_2(k, m, n),
set_mask_3(k, m, n));
}
return 0;
}