2015-05-05 8 views
6

Что было бы эффективным способом инвертировать все биты структуры, имеющие различные элементы разных размеров?Инвертировать все биты в структуре в C

Образец структура:

typedef struct { 
    uint16_t mem1; 
    uint8_t mem2; 
    uint32_t mem3; 
    uint8_t mem4[4]; 
} sample_struct; 
+1

В ролях к INT [], где размер массива определяется SizeOf (sample_struct)/SizeOf (INT), затем цикл над ним и использовать ~ operator? Может быть, вам нужно будет более конкретно говорить об «эффективном». – BadZen

+0

Можете ли вы привести пример: какие значения до и после инверсии? –

+0

Значение понятное - все биты в памяти, соответствующие полям структуры, должны быть перевернуты. – BadZen

ответ

11

простой и универсальный способ будет использовать такую ​​функцию:

void *memxor(void *p, int val, size_t size) { 
    unsigned char *pb = p; 
    while (size-- > 0) *pb++ ^= (unsigned char)val; 
    return p; 
} 

и использовать его таким образом:

sample_struct s; 
... 
memxor(&s, ~0, sizeof s); 

Если для по какой-то причине вы хотите оптимизировать больше, попробуйте это:

void meminvert(void *ptr, size_t size) { 
    if (((uinptr_t)ptr | size) & (sizeof(unsigned int) - 1)) { 
     unsigned char *p = ptr, *pe = pb + size; 
     while (p < pe) *p++ ^= ~0U; 
    } else { 
     unsigned int *p = ptr, *pe = p + size/sizeof *p; 
     while (p < pe) *p++ ^= ~0U; 
    } 
} 

Вы можете попробовать и сравнить, если на оптимизированная версия на самом деле имеет значение. Это потребует как огромной структуры, так и необоснованного количества вызовов. Подобная оптимизация редко необходима и часто ошибочна.

+0

Зачем использовать параметр 'int' (val), если вы используете его как' unsigned char'? – davmac

+0

@ davmac: для согласованности с 'void * memset (void * s, int c, size_t n);' – chqrlie

+0

+1, это разумная общая реализация. (Мне жаль, что 'memfrob' не принял фактический аргумент сейчас, а не просто 0x42 ...) – nneonneo

2

Простой, портативный, но не обязательно оптимальный способ:

char *ptr = (char *)&the_struct; 
size_t sz = sizeof(struct sample_struct); 
size_t i; 
for(i=0; i<sz; i++) { 
    ptr[i] = ~ptr[i]; 
} 

Это законно, потому что вы можете бросить указатель на любой записываемый объект для char * свободно.

Для большей эффективности вы должны использовать более крупный указатель, например unsigned long *, но тогда вам нужно беспокоиться о проблемах выравнивания (от начала и до конца). (И, обратите внимание, что он больше не будет строго легальным C, но он будет быстрее). Пример:

unsigned long *ptr = (unsigned long *)&the_struct; 
size_t sz = sizeof(struct sample_struct)/sizeof(unsigned long); 
while(sz-->0) { 
    *ptr = ~*ptr; 
    ptr++; 
} 
+0

Технически вы должны использовать' unsigned char44 для реальной мобильности – chqrlie

+0

«И, обратите внимание, что это больше не будет строго легальным C, но оно будет быстрее» - и на самом деле это может не работать на компиляторах реального мира, которые выполняют оптимизацию и переупорядочивание на основе типа- основанный на анализе псевдонимов. Пусть компилятор беспокоится об оптимизации портативной версии. – davmac

+0

@ davmac: Есть причина, по которой я наложил на нее большое предостережение. Нарушение спецификации C - это то, что я не часто рекомендую, но в сложных ситуациях (например, известный компилятор или конкретная встроенная платформа). Такие методы используют их. – nneonneo

1

как показано here

вы могли бы сделать: (псевдокод)

#define X_FIELDS \ 
    X(uint16_t, field1,) \ 
    X(uint8_t, field2,) \ 
    X(uint32_t, field3,) \ 
    X(uint8_t *, field4, [4]) 
//define the structure, the X macro will be expanded once per field 
typedef struct { 
#define X(type, name, num) type##num name; 
    X_FIELDS 
#undef X 
} mystruct;  
void flip(mystruct *aStruct) 
{ 
//--- "iterate" over all the fields of the structure 
#define X(type, name, num) \ 
     aStruct->name ~= aStruct->name; 
X_FIELDS 
#undef X 
} 
//--- demonstrate 
int main(int ac, char**av) 
{ 
    mystruct a = { 0, 1, 2, {1,2,3}}; 
    flip(&a); 
    return 0; 
} 
-1

Позаботьтесь о выравнивании данных.

Структуры в С, если вы не указываете на начале программы

#pragma pack(1) 

выровнены.

Это означает, что происходит что-то, называемое «заполнением данных».

И вы с удивлением заметите, что вызов sizeof на вашей структуре не возвращает 11, а возвращает 12 :).

+1

Хорошо, но это не отвечает на вопрос. (обратите внимание, что перелистывание битов прокладки структуры - это операция, которая обычно не видна программе). – nneonneo

+0

Он фактически ответил, использовать метод, предложенный выше людей и использовать #pragma пакет (толчок, 1) // определить структуру #pragma упаковка (поп) – mihaitzateo

+1

хорошая точка - но это должно не важно, поскольку тип данных не влияет на результат. Если вы переходите к uint8 вместо одного uint16, это не имеет значения –

2

Чтобы инвертировать все биты, логический оператор ~ - ваш друг. С 96 бит в структуре лучше всего отменить (принять один комплимент) каждого элемента структуры. Вот краткий пример:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <limits.h> 

typedef struct { 
    uint16_t mem1; 
    uint8_t mem2; 
    uint32_t mem3; 
    uint8_t mem4[4]; 
} sample_struct; 

/* toggle all bits in type sample_struct */ 
void toggle_sample (sample_struct *a) 
{ 
    register unsigned char i = 0; 

    a->mem1 = ~a->mem1; 
    a->mem2 = ~a->mem2; 
    a->mem3 = ~a->mem3; 

    for (i = 0; i < 4; i++) 
     a->mem4[i] = ~a->mem4[i]; 
} 

int main (void) { 

    sample_struct a = {0,0,0,{0}}; 
    unsigned i = 0; 

    printf (" \nThe struct values with all bits '0':\n\n"); 
    printf (" mem1 : %d\n mem2 : %d\n mem3 : %d\n", (int)a.mem1, (int)a.mem2, (int)a.mem3); 
    for (i = 0; i < 4; i++) 
     printf (" mem4[%d] : %u\n", i, a.mem4[i]); 

    toggle_sample (&a);  /* toggle all bits in a */ 

    printf (" \nThe struct values with all bits '1':\n\n"); 
    printf (" mem1 : %hu\n mem2 : %hhu\n mem3 : %u\n", (int)a.mem1, (int)a.mem2, (int)a.mem3); 
    for (i = 0; i < 4; i++) 
     printf (" mem4[%d] : %u\n", i, a.mem4[i]); 

    printf ("\n"); 

    return 0; 
} 

Выходной

$ ./bin/struct_invert 

The struct values with all bits '0': 

mem1 : 0 
mem2 : 0 
mem3 : 0 
    mem4[0] : 0 
    mem4[1] : 0 
    mem4[2] : 0 
    mem4[3] : 0 

The struct values with all bits '1': 

mem1 : 65535 
mem2 : 255 
mem3 : 4294967295 
    mem4[0] : 255 
    mem4[1] : 255 
    mem4[2] : 255 
    mem4[3] : 255 
Смежные вопросы