C++: как бросить 2 байта в массиве к короткому целому без знака

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

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

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа перестает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

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

19
задан Jason Plank 1 November 2011 в 17:53
поделиться

10 ответов

Ну, Вы расширяете символ в короткое значение. То, что Вы хотите, должно интерпретировать два байта как короткое. static_cast не может бросить от unsigned char* до unsigned short*. Необходимо бросить к void*, затем к unsigned short*:

unsigned short *p = static_cast<unsigned short*>(static_cast<void*>(&packetBuffer[1]));

Теперь, можно разыменовать p и получить короткое значение. Но проблема с этим подходом состоит в том, что Вы бросаете от неподписанного символа*, для освобождения* и затем к некоторому другому типу. Стандарт не гарантирует, что адрес остается тем же (и кроме того, разыменовывание того указателя было бы неопределенным поведением). Лучший подход должен использовать смещение бита, которое будет всегда работать:

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];
22
ответ дан 30 November 2019 в 03:38
поделиться

Это, вероятно, значительно ниже того, о чем Вы заботитесь о, но имеете в виду, что могли легко получить невыровненный доступ, делающий это. x86 является прощающим и аварийное прекращение работы, что невыровненные причины доступа будут пойманы внутренне и закончатся с копией и возвратом значения, таким образом, Ваше приложение не будет знать несколько отличающийся (хотя это значительно медленнее, чем выровненный доступ). Если, однако, этот код будет работать на non-x86 (Вы не упоминаете целевую платформу, таким образом, я принимаю x86 настольный Windows), то выполнение этого вызовет аварийное прекращение работы данных процессора, и необходимо будет вручную скопировать данные в выровненный адрес прежде, чем попытаться бросить его.

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

4
ответ дан 30 November 2019 в 03:38
поделиться

Сдвиг разряда выше имеет ошибку:

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];

, если packetBuffer находится в байтах (8 битов шириной) затем, вышеупомянутый сдвиг может и поворачиваться packetBuffer в нуль, оставляя Вас только с packetBuffer[2];

, Несмотря на который это все еще предпочтено указателям. Для предотвращения вышеупомянутой проблемы я трачу впустую несколько строк кода (кроме quite-literal-zero-optimization), это приводит к тому же машинному коду:

unsigned short p;
p = packetBuffer[1]; p <<= 8; p |= packetBuffer[2];

Или сохранить некоторые такты и не сместить биты от конца:

unsigned short p;
p = (((unsigned short)packetBuffer[1])<<8) | packetBuffer[2];

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

Говорят, что Вы были ленивы и требовались, чтобы сделать математику приблизительно на 16 битов на 8 битовых массивах. (прямой порядок байтов)

unsigned short *s;
unsigned char b[10];

s=(unsigned short *)&b[0];

if(b[0]&7)
{
   *s = *s+8;
   *s &= ~7;
}

do_something_With(b);

*s=*s+8;

do_something_With(b);

*s=*s+8;

do_something_With(b);

нет никакой гарантии, что отлично ошибка бесплатный компилятор создаст код, который Вы ожидаете. Массив байтов b отправленный в эти do_something_with() функция никогда не может изменяться эти *s операции. Ничто в коде выше не говорит, что он должен. Если Вы не оптимизируете свой код затем, Вы никогда не можете видеть эту проблему (пока кто-то действительно не оптимизирует или изменяет компиляторы или версии компилятора). При использовании отладчика, Вы никогда не можете видеть эту проблему (пока не слишком поздно).

компилятор не видит соединение между s и b, они - два абсолютно отдельных объекта. Оптимизатор может принять решение не записать *s назад в память, потому что это видит, что *s начинает много операций, таким образом, это может сохранить то значение в регистре и только сохранить его к памяти в конце (если когда-либо).

существует три основных способа решить проблему указателя выше:

  1. Объявляют s как энергозависимый.
  2. Использование объединение.
  3. Использование функция или функции, изменяя типы.
3
ответ дан 30 November 2019 в 03:38
поделиться

у статического броска есть другой синтаксис, плюс Вы должен работать с указателями, что Вы хотите сделать:

unsigned short *myShort = static_cast<unsigned short*>(&packetBuffer[1]);
1
ответ дан 30 November 2019 в 03:38
поделиться
unsigned short myShort = *(unsigned short *)&packetBuffer[1];
2
ответ дан 30 November 2019 в 03:38
поделиться

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

Несомненно, можно скорректировать параметры компилятора для обхождения этого ограничения, но это - очень тонкая вещь, которая прервет будущее, если код будет роздан и снова использует.

2
ответ дан 30 November 2019 в 03:38
поделиться

Сделал никто не видит, что вход был строкой!

/* If it is a string as explicitly stated in the question.
 */
int byte1 = packetBuffer[1] - '0'; // convert 1st byte from char to number.
int byte2 = packetBuffer[2] - '0';

unsigned short result = (byte1 * 256) + byte2;

/* Alternatively if is an array of bytes.
 */
int byte1 = packetBuffer[1];
int byte2 = packetBuffer[2];

unsigned short result = (byte1 * 256) + byte2;

Это также избегает проблем с выравниванием, которое большинство других решений может иметь на определенных платформах. Обратите внимание, что короткими составляют по крайней мере два байта. Большинство систем даст Вам ошибку памяти, при попытке разыменовать короткий указатель, который не составляет выровненные 2 байта (или независимо от того, что sizeof (короткий) в Вашей системе)!

0
ответ дан 30 November 2019 в 03:38
поделиться
char packetBuffer[] = {1, 2, 3};
unsigned short myShort = * reinterpret_cast<unsigned short*>(&packetBuffer[1]);

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

можно хотеть записать тестовый бросок и утверждение, чтобы видеть, читает ли это правильно. Таким образом, когда работал на машине с обратным порядком байтов или что еще более важно машине, которой не нравятся неправильно выровненные чтения, утверждать ошибка произойдет вместо странного трудно для трассировки 'ошибки' ;)

0
ответ дан 30 November 2019 в 03:38
поделиться

На окнах можно использовать:

unsigned short i = MAKEWORD(lowbyte,hibyte);
0
ответ дан 30 November 2019 в 03:38
поделиться
- 4532914-

Я понимаю, что это старая нить, и я не могу сказать, что попробовал каждое предложение здесь. Я просто делаю себя комфортно с MFC, и я искал способ преобразовать UINT на два байта, а затем снова на другом конце розетки.

Есть много битовых измерений, которые вы можете найти в сети, но ни один из них, казалось, на самом деле не работает. Много примеров кажутся чрезмерно сложными; Я имею в виду, что мы просто говорим о захвате 2 байта из UINT, отправляя их через проволоку и подключив их обратно в UINT на другом конце, верно?

Это решение, которое я наконец придумал:

class ByteConverter
{
public:
 static void uIntToBytes(unsigned int theUint, char* bytes)
  {
   unsigned int tInt = theUint;

   void *uintConverter = &tInt;
   char *theBytes = (char*)uintConverter;

   bytes[0] = theBytes[0];
   bytes[1] = theBytes[1];
  }
 static unsigned int bytesToUint(char *bytes)
  {
   unsigned theUint = 0;

   void *uintConverter = &theUint;
   char *thebytes = (char*)uintConverter;

   thebytes[0] = bytes[0];
   thebytes[1] = bytes[1];

   return theUint;
  }
};

Используется так:

unsigned int theUint;
char bytes[2];
CString msg;
ByteConverter::uIntToBytes(65000,bytes); theUint = ByteConverter::bytesToUint(bytes);
msg.Format(_T("theUint = %d"), theUint); AfxMessageBox(msg, MB_ICONINFORMATION | MB_OK);

Надеюсь, это поможет кому-то.

0
ответ дан 30 November 2019 в 03:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: