Turgenev 1 6 часов назад Опубликовано 6 часов назад (изменено) · Жалоба Работаю с примером 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, уровень как никак. Изменено 6 часов назад пользователем Turgenev Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 130 6 часов назад Опубликовано 6 часов назад · Жалоба 3 минуты назад, Turgenev сказал: Почти все ссылки в инете (кроме вики) пишут, что char знаковый Эти ссылки пишут неправильно. char - это отдельный тип, который может быть как знаковым, так и беззнаковым в зависимости от ключей командной строки при вызове компилятора. Поэтому в правильно написанной программе он должен использоваться исключительно для хранения символов. Для чисел должен использоваться signed char или unsigned char (а лучше - их псевдонимы из stdint.h - int8_t и uint8_t). Поэтому ваш пример написан криворуким программистом и массив input_bytes должен быть объявлен как uint8_t input_bytes[ENCODED_FRAME_SIZE] . 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Turgenev 1 6 часов назад Опубликовано 6 часов назад · Жалоба 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); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_pv 61 6 часов назад Опубликовано 6 часов назад · Жалоба для компилятора в данном случае '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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 216 5 часов назад Опубликовано 5 часов назад · Жалоба 41 минуту назад, Turgenev сказал: Почти все ссылки в инете Это примерно то же самое, что ОБС (одна бабка сказала). 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 *). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Turgenev 1 5 часов назад Опубликовано 5 часов назад · Жалоба 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 и указателя на него без ограничения возможностей функции? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 216 4 часа назад Опубликовано 4 часа назад · Жалоба 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 *). 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться