Перейти к содержанию
    

Char в uint8_t

Работаю с примером Speex AN2812 Keil STM32 (есть у меня в профиле в темах). Возник вопрос когда массиву, объявленному как:

char input_bytes[ENCODED_FRAME_SIZE];

начали присваивать поэлементно значения массива, объявленного как:

const uint8_t male_voice[]

Следующим образом:

for(uint8_t i=0;i<ENCODED_FRAME_SIZE-1; i++)
{
  input_bytes[i] = male_voice[sample_index++];
}

Почти все ссылки в инете (кроме вики) пишут, что char знаковый, то есть тоже самое что и signed char или int8_t. А так как uint8_t это переопределение типа unsigned char, то при присвоении переменной типа char значения типа unsigned char в переменной char должно получиться значение равное (unsigned char) mod 2^n. Но этого не происходит, присваивается нормально, без потери точности (перепроверил ниже в приведенном коде). И вроде вопросов быть не должно, но если написать условие типа:

  char char_var = 0;
  unsigned char uschar_var = 0;
  signed char schar_var = 0;

  uschar_var = 251;
  char_var = uschar_var;
  schar_var = uschar_var;

  if (char_var == 245)
  {
    char_var = uschar_var;
  }

То компилятор выдаст предупреждение, что условие всегда ложно: "warning: comparison of constant 245 with expression of type 'char' is always false". Значит нельзя так просто присваивать знаковому типу значение беззнакового. А как правильно или как это работает? Просто это все же пример от ST, уровень как никак. 

Изменено пользователем Turgenev

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 минуты назад, Turgenev сказал:

Почти все ссылки в инете (кроме вики) пишут, что char знаковый

Эти ссылки пишут неправильно. char - это отдельный тип, который может быть как знаковым, так и беззнаковым в зависимости от ключей командной строки при вызове компилятора. Поэтому в правильно написанной программе он должен использоваться исключительно для хранения символов. Для чисел должен использоваться signed char или unsigned char (а лучше - их псевдонимы из stdint.h - int8_t и uint8_t).

Поэтому ваш пример написан криворуким программистом и массив input_bytes должен быть объявлен как

uint8_t input_bytes[ENCODED_FRAME_SIZE]

.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, Сергей Борщ сказал:

Поэтому ваш пример написан криворуким программистом и массив input_bytes должен быть объявлен как

Думал так избавиться от неопределенности у себя в голове, но там функции чтения/записи кодированных/декодированных данных тоже ждут данные типа char в параметрах, а это уже так просто не заменишь

EXPORT int speex_bits_write(SpeexBits *bits, char *chars, int max_nbytes);
EXPORT void speex_bits_read_from(SpeexBits *bits, char *chars, int len);

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

для компилятора в данном случае '245' - int и в char физически не влазит, для сравнения.

если физически битики 11110101 поместить в uint8_t это будет 245, а если в int8_t, то -11, что совсем не равно 245, о чем компилятор и сообщает.

и если привести сравнение к одному соответствующему типу, (int8_t)245, сделав из неё -11, или наоборот (uint8_t)char_var, компилятор спокойно сравнит одинаковые наборы битов 11110101, без разницы "знаковые" они или нет.

 

2 minutes ago, Сергей Борщ said:

char - это отдельный тип, который может быть как знаковым, так и беззнаковым

+1, он к тому же ещё на некоторых платформах не всегда 8 бит бывает.

11 minutes ago, Turgenev said:

а это уже так просто не заменишь

указатель это лишь всего лишь адрес,

(void*) chars

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

41 минуту назад, Turgenev сказал:

Почти все ссылки в инете

Это примерно то же самое, что ОБС (одна бабка сказала).  :smile:

41 минуту назад, Turgenev сказал:

А как правильно или как это работает?

Про тип char выше уже сказал Сергей Борщ.

Кроме того, вот это:

41 минуту назад, Turgenev сказал:
for(uint8_t i=0;i<ENCODED_FRAME_SIZE-1; i++)
{
  input_bytes[i] = male_voice[sample_index++];
}

Написано или для 8-битника или криворуким программером также потому, что для переменной цикла на 32-битном МК используется почему-то 8-разрядный тип данных.

Грамотный программер для 32-битной системы для переменной цикла в данном случае использует тип int или unsigned или auto (если компилятор его поддерживает).

Да и вообще - присвоение скорее всего начнёт с конца. Так как - оптимальнее.

23 минуты назад, Turgenev сказал:

Думал так избавиться от неопределенности у себя в голове, но там функции чтения/записи кодированных/декодированных данных тоже ждут данные типа char в параметрах, а это уже так просто не заменишь

EXPORT int speex_bits_write(SpeexBits *bits, char *chars, int max_nbytes);
EXPORT void speex_bits_read_from(SpeexBits *bits, char *chars, int len);

Лучше так:

EXPORT int speex_bits_write(SpeexBits *bits, void *chars, int max_nbytes);
EXPORT void speex_bits_read_from(SpeexBits *bits, void *chars, int len);

И скорее всего в первой функции ещё и void const *chars (там же *chars используется явно только на чтение?). С приведением типа указателя в теле функции.

 

Имхо: Входные аргументы-указатели на массивы данных, которые функцией используются не как конкретно данные данного типа, а как массивы байт, лучше всегда делать типа void * (или void const *).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

23 минуты назад, jcxz сказал:

И скорее всего в первой функции ещё и void const *chars (там же *chars используется явно только на чтение?). С приведением типа указателя в теле функции.

Да, вроде только на чтение. Реализацию функций из оригинала прилагаю:

Спойлер
EXPORT void speex_bits_read_from(SpeexBits *bits, char *chars, int len)
{
   int i;
   int nchars = len / BYTES_PER_CHAR;
   if (nchars > bits->buf_size)
   {
      speex_notify("Packet is larger than allocated buffer");
      if (bits->owner)
      {
         char *tmp = (char*)speex_realloc(bits->chars, nchars);
         if (tmp)
         {
            bits->buf_size=nchars;
            bits->chars=tmp;
         } else {
            nchars=bits->buf_size;
            speex_warning("Could not resize input buffer: truncating input");
         }
      } else {
         speex_warning("Do not own input buffer: truncating oversize input");
         nchars=bits->buf_size;
      }
   }
#if (BYTES_PER_CHAR==2)
/* Swap bytes to proper endian order (could be done externally) */
#define HTOLS(A) ((((A) >> 8)&0xff)|(((A) & 0xff)<<8))
#else
#define HTOLS(A) (A)
#endif
   for (i=0;i<nchars;i++)
      bits->chars[i]=HTOLS(chars[i]);

   bits->nbBits=nchars<<LOG2_BITS_PER_CHAR;
   bits->charPtr=0;
   bits->bitPtr=0;
   bits->overflow=0;
}

 

EXPORT int speex_bits_write(SpeexBits *bits, char *chars, int max_nbytes)
{
   int i;
   int max_nchars = max_nbytes/BYTES_PER_CHAR;
   int charPtr, bitPtr, nbBits;

   /* Insert terminator, but save the data so we can put it back after */
   bitPtr=bits->bitPtr;
   charPtr=bits->charPtr;
   nbBits=bits->nbBits;
   speex_bits_insert_terminator(bits);
   bits->bitPtr=bitPtr;
   bits->charPtr=charPtr;
   bits->nbBits=nbBits;

   if (max_nchars > ((bits->nbBits+BITS_PER_CHAR-1)>>LOG2_BITS_PER_CHAR))
      max_nchars = ((bits->nbBits+BITS_PER_CHAR-1)>>LOG2_BITS_PER_CHAR);

   for (i=0;i<max_nchars;i++)
      chars[i]=HTOLS(bits->chars[i]);
   return max_nchars*BYTES_PER_CHAR;
}

 

26 минут назад, jcxz сказал:

Имхо: Входные аргументы-указатели на массивы данных, которые функцией используются не как конкретно данные данного типа, а как массивы байт, лучше всегда делать типа void * (или void const *).

А вот, кстати, по поводу типа void в параметрах функции. Есть функция:

Спойлер

RINGBUF_STATUS RingBuf_DataWatch(void *data, u16_t len, RINGBUF_t *rb);

И ее реализация:

Спойлер
/**
 * @brief Watch current data in the buf
 * @note Reads data without shifting in the buffer
 *
 * @param[out] data Data from buffer
 * @param[in] len Length of data to be read [bytes]
 * @param[in] rb #RINGBUF_t structure instance
 * @return #RINGBUF_STATUS enum
 */
RINGBUF_STATUS RingBuf_DataWatch(void *data, u16_t len, RINGBUF_t *rb) {
    if (data == NULL)
        return RINGBUF_PARAM_ERR;
    if (len > rb->size)
        return RINGBUF_OVERFLOW;
    // OUTPUT data index start address
    u16_t s_addr = 0;
    // available space in the end of buffer
    u16_t space = rb->size - rb->tail;
    u16_t loc_tail = rb->tail;
    if (len > space) { // if len > available space
        // recast pointer to u8_t
        // copy data from available space
          memcpy((&data[s_addr * rb->cell_size]), &rb->buf[loc_tail * rb->cell_size], space * rb->cell_size);
        // next reading will start from 0
        loc_tail = 0;
        // new start address - space length
        s_addr = space;
        // new length - len-space
        len -= space;
    }
    // copy all the data from the buf storage
    memcpy(&data[s_addr * rb->cell_size], &rb->buf[loc_tail * rb->cell_size], len * rb->cell_size);
    return RINGBUF_OK;
}

 

Тип параметра как вы и пишите- void. Но на строчках:

...
memcpy((&data[s_addr * rb->cell_size]), &rb->buf[loc_tail * rb->cell_size], space * rb->cell_size);
...
memcpy(&data[s_addr * rb->cell_size], &rb->buf[loc_tail * rb->cell_size], len * rb->cell_size);
...

компилятор выдаст ошибку: error:  #852: expression must be a pointer to a complete object type. Потому что компилятор не знает по сколько ему прибавлять ячеек памяти. Я пролечил указанием в параметрах функции типа uint8_t вместо void, тем самым ограничив работу функции (а в целом и всей библиотеки) только значениями типа uint8_t. А как такое лечить, чтобы не ограничивать возможности функции? Функция взята из гитхаба

 

 

41 минуту назад, jcxz сказал:

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

Но тут тоже есть нюанс. Например, функция из библиотеки с гитхаба:

Спойлер

RINGBUF_STATUS RingBuf_DataWatch(void *data, u16_t len, RINGBUF_t *rb);

И ее реализация:

Спойлер
/**
 * @brief Watch current data in the buf
 * @note Reads data without shifting in the buffer
 *
 * @param[out] data Data from buffer
 * @param[in] len Length of data to be read [bytes]
 * @param[in] rb #RINGBUF_t structure instance
 * @return #RINGBUF_STATUS enum
 */
RINGBUF_STATUS RingBuf_DataWatch(void *data, u16_t len, RINGBUF_t *rb) {
    if (data == NULL)
        return RINGBUF_PARAM_ERR;
    if (len > rb->size)
        return RINGBUF_OVERFLOW;
    // OUTPUT data index start address
    u16_t s_addr = 0;
    // available space in the end of buffer
    u16_t space = rb->size - rb->tail;
    u16_t loc_tail = rb->tail;
    if (len > space) { // if len > available space
        // recast pointer to u8_t
        // copy data from available space
          memcpy((&data[s_addr * rb->cell_size]), &rb->buf[loc_tail * rb->cell_size], space * rb->cell_size);
        // next reading will start from 0
        loc_tail = 0;
        // new start address - space length
        s_addr = space;
        // new length - len-space
        len -= space;
    }
    // copy all the data from the buf storage
    memcpy(&data[s_addr * rb->cell_size], &rb->buf[loc_tail * rb->cell_size], len * rb->cell_size);
    return RINGBUF_OK;
}

В параметрах функции тип принимаемых данных void *, как вы и указали. Но на строчках:

...
memcpy((&data[s_addr * rb->cell_size]), &rb->buf[loc_tail * rb->cell_size], space * rb->cell_size);
...
memcpy(&data[s_addr * rb->cell_size], &rb->buf[loc_tail * rb->cell_size], len * rb->cell_size);
...

Компилятор выдаст ошибку: error:  #852: expression must be a pointer to a complete object type. Потому что компилятор не знает по сколько ячеек памяти прибавлять, так как тип данных не известен. Я пролечил проблему заменой типа данных с void на Uint8_t, но и возможности функции тем самым ограничил. А как тут решался бы конфликт из-за типа void и указателя на него без ограничения возможностей функции?

 

В параметрах функции тип принимаемых данных void *, как вы и указали. Но на строчках:

...
memcpy((&data[s_addr * rb->cell_size]), &rb->buf[loc_tail * rb->cell_size], space * rb->cell_size);
...
memcpy(&data[s_addr * rb->cell_size], &rb->buf[loc_tail * rb->cell_size], len * rb->cell_size);
...

Компилятор выдаст ошибку: error:  #852: expression must be a pointer to a complete object type. Потому что компилятор не знает по сколько ячеек памяти прибавлять, так как тип данных не известен. Я пролечил проблему заменой типа данных с void на Uint8_t, но и возможности функции тем самым ограничил. А как тут решался бы конфликт из-за типа void и указателя на него без ограничения возможностей функции?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Turgenev сказал:

В параметрах функции тип принимаемых данных void *, как вы и указали. Но на строчках:

...
memcpy((&data[s_addr * rb->cell_size]), &rb->buf[loc_tail * rb->cell_size], space * rb->cell_size);
...

Написано опять криворуким. void * - указатель на безразмерный тип, а значит к нему нельзя применять операции индексирования ([]), разъименования (*) и подобные. Сперва его нужно привести к нужному размерному типу. О чём я выше писал:

1 час назад, jcxz сказал:

С приведением типа указателя в теле функции.

1 час назад, Turgenev сказал:

А как тут решался бы конфликт из-за типа void и указателя на него без ограничения возможностей функции?

Что мешает в нужном конкретном месте сделать приведение типа data?

memcpy((u8 *)data + s_addr * rb->cell_size, &rb->buf[loc_tail * rb->cell_size], space * rb->cell_size);

Здесь вы приказываете компилятору рассматривать data - как указатель на байты (u8 *).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...