как привести массив char в одно целое число? - PullRequest
8 голосов
/ 08 января 2011

Я пытаюсь прочитать содержимое файла PNG.

Как вы, возможно, знаете, все данные записываются 4-байтовым способом в png-файлах, как текстовых, так и числовых.поэтому, если у нас есть номер 35234, он сохраняется следующим образом: [1000] [1001] [1010] [0010].

, но иногда числа короче, поэтому первые байты равны нулю, и когда я читаюмассив и приведение его от char * к целому числу Я получаю неправильное число.например [0000] [0000] [0001] [1011] иногда числа неверно интерпретируются как отрицательные числа, а время от времени - как ноль!

Позвольте мне привести интуитивный пример:

char s_num[4] = {120, 80, 40, 1};

int  t_num = 0;

t_num = int(s_num);

IЕсли бы я мог хорошо объяснить свою проблему!

как я могу привести такие массивы в одно целое значение?

хорошо, хорошо, хорошо, позвольте мне изменить мой код, чтобы объяснить его лучше:

char s_num[4] = {0, 0, 0, 13};
int  t_num;


t_num = *((int*) s_num);
cout << "t_num: " << t_num << endl;

здесь мы должны получить 13 в результате, хорошо?но опять же с этим новым решением ответ неверен, вы можете проверить на своих компьютерах!я получаю этот номер: 218103808, что, безусловно, неправильно!

Ответы [ 7 ]

12 голосов
/ 08 января 2011

Вы разыгрываете (char *) в (int).Что вам нужно сделать, это привести к целочисленному указателю, то есть

t_num = *((int*) s_num));

Но на самом деле вы должны извлечь свой код в его собственную функцию и убедиться, что:

  1. порядок байтов правильный1007 *
  2. sizeof(int) == 4
  3. Использовать приведения C ++ (т.е. static, dynamic, const, reinterpret)
7 голосов
/ 08 января 2011

Предполагается, что машина с прямым порядком байтов с 32-разрядным целым числом может выполнить:

char s_num[4] = {0xAF, 0x50, 0x28, 0x1};
int t_num = *((int*)s_num);

Чтобы разбить его на шаги:

  1. s_num - это массив, который можно интерпретировать как указатель на его первый элемент (char* здесь)
  2. Приведение s_num к int* из-за (1) - можно использовать указатели
  3. Доступ к целому числу, на которое указывает указатель приведения (разыменование)

Чтобы иметь 0xAF в качестве младшего байта целого числа. Более полный пример (код C):

#include <stdio.h>

int main()
{
    char s_num[4] = {0xAF, 0x50, 0x28, 0x1};
    int t_num = *((int*)s_num);

    printf("%x\n", t_num);
    return 0;
} 

Печать:

12850af

Как и ожидалось.

Обратите внимание, что этот метод не слишком переносим, ​​поскольку он предполагает порядковый номер и целочисленный размер. Если вам нужно выполнить простую задачу на одной машине, вам это может сойти с рук, но для чего-то качественного производства вам придется учитывать мобильность.

Кроме того, в коде C ++ было бы лучше использовать reinterpret_cast вместо приведения в стиле C.

2 голосов
/ 08 января 2011

Я считаю использование std bitset наиболее явным способом выполнения преобразований (в частности, отладку.)

Возможно, следующее не то, что вам нужно в вашем конечном коде (возможно, слишком многословно) - но я нахожу егоотлично подходит для попытки точно понять, что происходит.

http://www.cplusplus.com/reference/stl/bitset/

#include <bitset>
#include <iostream>
#include <string>

int
main  (int ac, char **av)
{

  char s_num[4] = {120, 80, 40, 1};
  std::bitset<8> zeroth   = s_num[0];
  std::bitset<8> first    = s_num[1];
  std::bitset<8> second   = s_num[2];
  std::bitset<8> third    = s_num[3];

  std::bitset<32> combo;
  for(size_t i=0;i<8;++i){
    combo[i]     = zeroth[i];
    combo[i+8]   = first[i];
    combo[i+16]  = second[i];
    combo[i+24]  = third[i];
  }
  for(size_t i = 0; i<32; ++i)
    {
      std::cout<<"bits ["<<i<<"] ="<<combo.test(i)<<std::endl;
    }
  std::cout<<"int = "<<combo.to_ulong()<<std::endl;
}
1 голос
/ 06 февраля 2018

Ответ Акселя нарушает строгое правило псевдонимов , по крайней мере, с C ++ 14.Поэтому я публикую этот ответ для будущих пользователей.


Помимо проблем с порядком байтов и размером, безопасным способом является использование std::memcpy, т.е.

0 голосов
/ 08 января 2011

РЕДАКТИРОВАТЬ: кажется, что вы не хотите суммировать числа в конце концов. Оставьте этот ответ здесь для потомков, но, скорее всего, он не отвечает на вопрос, который вы хотите задать.

Вы хотите суммировать значения, поэтому используйте std::accumulate:

#include <numeric>
#include <iostream>

int main(void) {
    char s_num[4] = {120,80,40,1};
    std::cout << std::accumulate(s_num, s_num+4,0) << std::endl;
    return 0;
}

Производит продукцию:

pgp@axel02:~/tmp$ g++ -ansi -pedantic -W -Wall foo.cpp -ofoo
pgp@axel02:~/tmp$ ./foo
241
0 голосов
/ 08 января 2011

Преобразование выполнено хорошо, потому что вы не суммируете эти значения, а назначаете их как одно значение. Если вы хотите суммировать их, вы должны сделать это вручную:

int i;
for (i = 0; i<4; ++i)
    t_num += s_num[i];
0 голосов
/ 08 января 2011

Знаете ли вы, что int в C ++ переполняется после 32767-го значения? Это объяснило бы ваше отрицательное число для 35234.

Решение состоит в том, чтобы использовать тип данных, который может обрабатывать большие значения. См. Статью переполнения целых чисел для получения дополнительной информации:

http://en.wikipedia.org/wiki/Integer_overflow

ОБНОВЛЕНИЕ : Я написал это, не думая, что мы все живем в современном мире, где существуют и процветают 32- и 64-разрядные машины !! Переполнение для int на самом деле намного больше, чем мое первоначальное утверждение.

...