поведение strtoul между C и Arduino

У меня есть код C, который компилирует OK на моем ноутбуке

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

unsigned int crc32(const void *m, size_t len) {
  const unsigned char *message = m;
  size_t i;
  int j;
  unsigned int byte, crc, mask;

  i = 0;
  crc = 0xFFFFFFFF;
  while (i < len) {
    byte = message[i];
    crc = crc ^ byte;
    for (j = 7; j >= 0; j--) {
      mask = -(crc & 1);
      crc = (crc >> 1) ^ (0xEDB88320 & mask);
    }
    i = i + 1;
  }
  return ~crc;
}

int main() {
  char str[] =
      "620004";
  size_t len = strlen(str);
  unsigned int crc = crc32(str, len);

  size_t len2 = (len + 1) / 2;
  unsigned char arr2[len2];
  for (size_t i = 0; i < len; i += 2) {
    arr2[i / 2] = strtoul((char[3]) {str[i], str[i + 1], '\0'}, 0, 16);
  }
  crc = crc32(arr2, len2);
  printf("CRC: 0x%X\n", crc);

  return 0;
}

И я пытаюсь портировать его на Arduino

#include <CRC32.h>

void setup() {
  Serial.begin(115200);
  uint8_t byteBuffer[] = "620004";
  size_t numBytes = sizeof(byteBuffer) - 1;
  size_t len2 = (numBytes + 1) / 2;
  unsigned char arr2[len2];
  CRC32 crc;
  for (size_t i = 0; i < numBytes; i += 2) {
    arr2[i / 2] = strtoul((char[3]) {byteBuffer[i], byteBuffer[i + 1], '\0'}, 0, 16);
  }
  for (size_t i = 0; i < numBytes; i++){
    crc.update(byteBuffer[i]);
  }

  uint32_t checksum = crc.finalize();
  Serial.print("CRC: ");
  Serial

Но я получаю

Arduino: 1.8.9 (Linux), Board: "Arduino/Genuino Uno"

/home/nico/Arduino/crc32/crc32.ino: In function 'void setup()':
/home/nico/Arduino/crc32/crc32.ino:11:50: warning: narrowing conversion of 'byteBuffer[i]' from 'uint8_t {aka unsigned char}' to 'char' inside { } [-Wnarrowing]
     arr2[i / 2] = strtoul((char[3]) {byteBuffer[i], byteBuffer[i + 1], '\0'}, 0, 16);
                                                  ^
/home/nico/Arduino/crc32/crc32.ino:11:69: warning: narrowing conversion of 'byteBuffer[(i + 1u)]' from 'uint8_t {aka unsigned char}' to 'char' inside { } [-Wnarrowing]
     arr2[i / 2] = strtoul((char[3]) {byteBuffer[i], byteBuffer[i + 1], '\0'}, 0, 16);
                                                                     ^
crc32:11:84: error: taking address of temporary array
     arr2[i / 2] = strtoul((char[3]) {byteBuffer[i], byteBuffer[i + 1], '\0'}, 0, 16);
                                                                                    ^
exit status 1
taking address of temporary array

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

И я не совсем понимаю, в чем проблема с функцией strtoul и ее другим поведением на Arduino

, 👍0

Обсуждение

Все эти ошибки связаны с (char[3]) {byteBuffer[i], byteBuffer[i + 1], '\0'} и не имеют ничего общего с strtoul. Кто знает, сколько лет компилятору, где он работает., @KIIV

Кстати, первые два вызваны разницей между: char str[] = "620004"; и uint8_t byteBuffer[] = "620004";, @KIIV

Пожалуйста, сделайте ответ, не публикуйте ответ в качестве комментария. Комментарии предназначены для уточнения вопроса., @Nick Gammon


2 ответа


Лучший ответ:

0

В некоторых архитектурах "char" – это "байт без знака". В других это «байт со знаком». Спецификация AVR предписывает знаковый байт для char. Все, что вы компилируете, вероятно, требует беззнакового символа.

Вы не можете предполагать одно или другое. Если вы работаете с массивом символов ("строкой"), вы должны присвоить его типу данных char. Таким образом, обозначения типа данных и «строкового» содержимого будут совпадать.

Используя uint8_t, вы специально указываете использовать беззнаковый байт.

strtoul() ожидает символ. Так как на AVR char подписан, когда вы пытаетесь втиснуть 8-битное значение в 7-битное пространство + знак, он предупреждает вас. И это хорошо. Вы можете потерять восьмой бит данных.

Главная ошибка, однако, заключается в следующем:

error: taking address of temporary array

Это вызвано тем, что вы создали новый временный массив в списке параметров strtoul.

{byteBuffer[i], byteBuffer[i + 1], '\0'}

Затем вы берете адрес этого массива и передаете его функции. Это не хорошо. Представьте ситуацию, когда функция возвращает что-то, зависящее от этого адреса. Например, функция, которая возвращает указатель на все символы строки, кроме первого. Как только функция возвращает, этот возвращаемый указатель указывает на область памяти, которая больше не действительна — временный массив уже удален.

Я знаю, что в вашем коде это не так, но вы можете себе это представить.

Да, такие временные массивы прекрасно работают в некоторых версиях C++. Но это довольно свежие версии, и компилятор Arduino не такой уж новый (я не знаю, какая версия представила их как действительные сразу, но сейчас мы до GCC версии 8. Arduino все еще не работает на версии 5. что-то IIRC).

Достаточно сказать, что не используйте подобные временные массивы (что очень жаль, потому что это чертовски полезно). Вместо этого вы должны присвоить его новому массиву и вместо этого передать указатель на этот массив:

char tempArr[3] = {byteBuffer[i], byteBuffer[i + 1], '\0'};
 arr2[i / 2] = strtoul(tempArr, 0, 16);

Таким образом, адрес, который вы передаете функции, выделяется в стеке и гарантированно существует после времени существования вызова функции (вплоть до конца текущей области).

,

0

Не подвергать сомнению языковые проблемы, на которые было указано, а просто отвечать на то, что вы спросили,

"... в чем проблема... его поведение на Arduino отличается"

заключается в том, что это два разных компилятора, которые, возможно, написаны для двух разных стандартов языка (стандарты со временем развиваются) и, вероятно, работают с разными переключателями подавления ошибок и предупреждений.

И не для педантизма, а для уточнения, на самом деле он еще не работал на Arduino, не был успешно скомпилирован в этой среде. Различия, которые вы видите, полностью связаны с различиями между двумя компиляторами и параметрами ошибок, с которыми они были запущены.

,