Булевый массив с использованием "char" - PullRequest
3 голосов
/ 08 августа 2010

Я создал объект, который на самом деле представляет собой массив из 8 логических значений, хранящихся в char. Я сделал это, чтобы узнать больше о побитовых операторах и о создании ваших собственных объектов в C. Итак, у меня есть два вопроса:

  1. Могу ли я быть уверен, если код ниже всегда работает?
  2. Это хорошая реализация сделать объект, который не может потеряться в C, если вы не отпустите сами.

Код:

/*
 *  IEFBooleanArray.h
 *  IEFBooleanArray
 *
 *  Created by ief2 on 8/08/10.
 *  Copyright 2010 ief2. All rights reserved.
 *
 */

#ifndef IEFBOOLEANARRAY_H
#define IEFBOOLEANARRAY_H

#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef char * IEFBooleanArrayRef;

void IEFBooleanArrayCreate(IEFBooleanArrayRef *ref);
void IEFBooleanArrayRelease(IEFBooleanArrayRef ref);
int IEFBooleanArraySetBitAtIndex(IEFBooleanArrayRef ref, 
                                 unsigned index, 
                                 int flag);
int IEFBooleanArrayGetBitAtIndex(IEFBooleanArrayRef ref, 
                                 unsigned index);

#endif

/*
 *  IEFBooleanArray.c
 *  IEFBooleanArray
 *
 *  Created by ief2 on 8/08/10.
 *  Copyright 2010 ief2. All rights reserved.
 *
 */

#include "IEFBooleanArray.h"

void IEFBooleanArrayCreate(IEFBooleanArrayRef *ref) {
    IEFBooleanArrayRef newReference;

    newReference = malloc(sizeof(char));
    memset(newReference, 0, sizeof(char));
    *ref = newReference;
}

void IEFBooleanArrayRelease(IEFBooleanArrayRef ref) {
    free(ref);
}

int IEFBooleanArraySetBitAtIndex(IEFBooleanArrayRef ref, unsigned index, int flag) {
    int orignalStatus;

    if(index < 0 || index > 7)
        return -1;

    if(flag == 0)
        flag = 0;
    else
        flag = 1;

    orignalStatus = IEFBooleanArrayGetBitAtIndex(ref, index);
    if(orignalStatus == 0 && flag == 1)
        *ref = *ref + (int)pow(2, index);
    else if(orignalStatus == 1 && flag == 0)
        *ref = *ref - (int)pow(2, index);

    return 0;
}

int IEFBooleanArrayGetBitAtIndex(IEFBooleanArrayRef ref, unsigned index) {
    int result;
    int value;

    value = (int)pow(2, index);
    result = value & *ref;

    if(result == 0)
        return 0;
    else
        return 1;
}

Я скорее парень из Objective-C, но я действительно хочу больше изучать C. Кто-нибудь может попросить еще "домашнюю работу", с которой я могу улучшить себя?

Спасибо, ief2

Ответы [ 5 ]

10 голосов
/ 08 августа 2010
  1. Не проверяйте неподписанные типы с помощью < 0, это бессмысленно и вызывает предупреждения у некоторых компиляторов.
  2. Не используйте неподписанные типы без указания их размера (unsigned int, unsigned char и т. Д.).
  3. Если flag == 0, почему вы устанавливаете его на 0?
  4. Мне не нравится абстрагироваться от * в typedef, но это не является неправильным любыми способами.
  5. Вам не нужно вызывать memset(), чтобы установить для одного байта значение 0.
  6. Использование pow для вычисления смещения бита - это безумие. Проверьте операторы << и >> и используйте их вместо
  7. Полностью заключите в скобки условия if или будьте готовы к устранению боли в будущем.
  8. Если вы используете в своей функции SetBitAtIndex побитовые операторы & и | вместо арифметических + и -, вам все равно не понадобятся все эти сложные операторы if.
  9. Ваша GetBitAtIndex рутина не проверяет границы index.

Из этого списка # 9 - единственное, что означает, что ваша программа не будет работать во всех случаях, я думаю. Я не исчерпывающе проверил это - это просто проверка первого взгляда.

4 голосов
/ 08 августа 2010

pow(2,index) является одним из наиболее неэффективных способов создания битовой маски. Я могу себе представить, что использование функции Аккермана могло бы быть хуже, но pow() в значительной степени медленная сторона. Вы должны использовать (1<<index) вместо этого. Кроме того, C'ish способ установить / очистить немного значения выглядит иначе. Вот недавний вопрос об этом:


Если вы хотите разбирать биты в C эффективным и переносимым способом, то вы действительно должны взглянуть на страницу с битами, которую все здесь предложат вам, если вы упомянете "биты" как-то:


Следующая кодовая последовательность:

if(result == 0)
        return 0;
    else
        return 1;

можно записать как return (result != 0);, return result или return !!result (если результат должен быть равен 0 или 1). Хотя это всегда хорошая идея, чтобы прояснить намерение, большинство программистов на Си предпочтут «результат результата»; потому что в C это способ прояснить ваше намерение . «If» выглядит ненадежно, как предупреждающая наклейка с надписью «Первоначальный разработчик - парень из Java и мало что знает о битах» или что-то в этом роде.


newReference = malloc(sizeof(char));
memset(newReference, 0, sizeof(char));

malloc + memset(x,0,z) == calloc();


У вас есть способ сообщить об ошибке (недопустимый индекс) для IEFBooleanArraySetBitAtIndex, но не для IEFBooleanArrayGetBitAtIndex. Это противоречиво. Обеспечьте единообразие отчетов об ошибках, иначе пользователи вашей библиотеки будут проверять ошибки.

3 голосов
/ 08 августа 2010

Что касается доступа к биту #n в вашем объекте char, вместо использования функции pow () вы можете использовать сдвиг и маскирование:

Установить бит #n:

a = a | (1 << n);

Сбросить бит #n:

a = a & (~(1 << n));

Получить бит #n:

return ((a >> n) & 1);
1 голос
/ 08 августа 2010

В дополнение к очкам Карла Норума:

  1. Не сохраняйте место в символе таким образом, если это не требуется (т. Е. Вы храните много битовых значений). Это намного медленнее, поскольку вы должны выполнять побитовые операции и т. Д.
  2. На большинстве архитектур вы тратите память, теряя значение char. Один указатель занимает в 4-8 раз больше, чем char на большинстве современных архитектур, и, кроме того, у вас есть данные о выделенном фрагменте как таковом.
  3. Вероятно, статический размер - не лучший подход, поскольку он негибкий. Я не вижу никакой выгоды от использования специальных функций для этого.

По состоянию на 3-й пункт что-то вроде:

typedef struct {
    uint64_t size;
    uint64_t *array;
}bitarray;

bitarray bitarray_new(uint64_t size) {
    bitarray arr;
    arr.size = size;
    arr.array = calloc(size/8);
    return arr;
}

void bitarray_free(bitarray arr) {
    free(arr.array);
}

void bitarray_set(bitarray arr, uint64_t index, int bit) {
  assert (index <= arr.size)
  if (bit)
    array[index/8] |= 1 << (index % 8);
  else
    array[index/8] ^= ~(1 << (index % 8));
}

void bitarray_get(bitarray arr, uint64_t index, int bit) {
  assert (index <= arr.size)
  return array[index/8] & 1 << (index % 8);
}

Copyright 2010 ief2. All rights reserved.

На самом деле это не так. Вы добровольно опубликовали их под cc-by-sa лицензией и только некоторые права защищены. Кроме того, вы хотите, чтобы мы прочитали и изменили код, поэтому резервировать все права бессмысленно.

(PS. В любом случае, я бы советовал не публиковать тривиальные работы под ограничительными лицензиями - это не выглядит профессионально - если у вас нет юридических вопросов для этого)

Является ли это хорошей реализацией для создания объекта, который не может потеряться в C, если вы сами не отпустите его.

Извините

1 голос
/ 08 августа 2010

Никто, кажется, не упоминает об этом (я удивлен), но ... Вы не можете сказать мне, что вы серьезно делаете malloc(sizeof(char))?Это очень небольшое распределение.Не имеет смысла делать это объектом, выделенным кучей.Просто объявите его как char.

Если вы хотите иметь некоторую степень инкапсуляции, вы можете сделать: typedef char IEFBoolArray; и сделать функции доступа для манипулирования IEFBoolArray.Или даже typedef struct { char value; } IEFBoolArray; Но, учитывая размер данных, было бы просто безумием размещать их по одному в куче.Пусть потребители этого типа просто объявят его в строке и будут использовать средства доступа.

Далее ... Вы уверены, что хотите, чтобы оно было char?Вы можете получить немного более качественный код, если продвигаете его в нечто большее, например int.

...