如何将字符数组转换为单个整数?

8
我正在尝试读取PNG文件的内容。
正如您所知,png文件中所有数据都是以4字节的方式编写的,包括文本和数字。因此,如果我们有数字35234,它保存在这样的方式: [1000][1001][1010][0010]。
但有时数字会更短,所以前面的字节为零,当我从char *转换为integer并读取数组时,我得到了错误的数字。例如 [0000] [0000] [0001] [1011] 有时数字被解释为负数,有时为零!
让我给你一个直观的例子:
char s_num[4] = {120, 80, 40, 1};

int  t_num = 0;

t_num = int(s_num);

我希望我能清晰地解释我的问题!
如何将这样的数组转换为一个整数值?
好的,让我修改一下我的代码来更好地解释它:
char s_num[4] = {0, 0, 0, 13};
int  t_num;


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

这里我们需要得到13作为结果,好吗? 但是使用这个新解决方案后,答案是错误的,你可以在你的电脑上测试一下! 我得到了这个数字:218103808,显然是错误的!


1
你的机器是大端序还是小端序?...阅读这个:http://zh.wikipedia.org/wiki/%E5%AD%97%E8%8A%82%E5%BA%8F#%E5%A4%A7%E7%AB%AF%E5%BA%8F - Nawaz
1
你顺便怎么转换类型?发一下代码吧。 - Nawaz
1
[1000][1001][1010][0010] 是两个字节,而不是四个。 - TonyK
1
我认为这个问题非常模糊。他是想将所有字节相加,还是想将char*转换为int*?或者是什么意思? - Nawaz
1
@SepiDevi:如果你不想求和,那么为什么期望241是你“直觉”示例的结果呢? - Nawaz
显示剩余6条评论
7个回答

11

你将(char*)转换为(int)。你应该将它转换为指向整数的指针,即

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

但是,你真的应该将代码提取到自己的函数中,并确保:

  1. 大小端是正确的
  2. sizeof(int) == 4
  3. 使用C++类型转换(即 static, dynamic, const, reinterpret

“同样的结果”是什么意思?也许你应该解释一下,为什么你期望这会给出241... - Axel
噢...你只是想要将四个字符的字节值相加。这样将得到241。Macieks的解答做到了这一点。但真的是你想要做的吗?对我来说看起来很奇怪(不是代码,我在努力理解你的问题)。 - Axel
@iammilind:你有读完你链接的答案吗?char*是少数几种情况之一,这种情况下不会破坏别名。 - AndyG
1
@iammilind:为了支持我的说法,请参考C++标准§3.10/10 Lvalues and rvalues [basic.lval],其中对于charunsigned char做出了特殊规定。 - AndyG
这个违反了严格别名规则 - xskxzr

8
假设使用一个32位整数的小端机器,您可以执行以下操作:
char s_num[4] = {0xAF, 0x50, 0x28, 0x1};
int t_num = *((int*)s_num);

将其分解为以下步骤:

  1. s_num是一个数组,可以理解为指向其第一个元素(此处为char*)的指针。
  2. 由于(1),将s_num转换为int* - 转换指针是可以的。
  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风格的强制转换。


你能解释一下为什么是反向顺序吗?我认为应该是af50281。 - Summer Sun
谢谢你,Eli。我现在明白了。 - Summer Sun

2
我发现使用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
Axel的回答违反了严格别名规则,至少自C++14以来如此。因此我为未来用户发布此答案。
除了字节序和大小问题外,一种安全的方法是使用std::memcpy,即:
   unsigned char s_num[4] = {13, 0, 0, 0}; 
// ^^^^^^^^               // ^^ fix endianness issue
// use unsigned char to avoid potential issues caused by sign bit

int t_num;

std::memcpy(&t_num, s_num, 4);

0

转换做得很好,因为您不是将这些值相加,而是将它们分配为一个值。如果您想对它们进行求和,必须手动执行:

int i;
for (i = 0; i<4; ++i)
    t_num += s_num[i];

0

编辑:看起来你并不想求和。我保留这个答案以供后人参考,但它可能无法回答你想要问的问题。

您想要对值求和,所以使用 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

我不确定这是否是他所询问的。 - Eli Bendersky
@Eli 我不认为清楚他在问什么。但这是他的例子所要求的结果。 - Philip Potter
当我使用了你的解决方案时,发生了同样的情况。 - sepisoad

0
你知道在C++中,int类型在32767的值之后会溢出吗?这就可以解释为什么35234会变成负数。
解决方法是使用可以处理更大值的数据类型。详见《整数溢出》文章获取更多信息:

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

更新:我写这篇文章的时候没有考虑到我们都生活在现代社会,32位和64位的计算机存在且蓬勃发展!整型数据类型的溢出实际上比我最初的说法要大得多。


3
你有哪种机器会在32767时溢出int? - Eli Bendersky
不一定如此。许多C++实现具有32位甚至64位的“int”,在32767之后不会溢出。 - Philip Potter
@Philip: “Many…” 这个说法有些保守了。除了嵌入式领域,现在很难找到一个比32位还小的处理器了。 - Eli Bendersky
我改正了。我可能是有阅读障碍,看错了数据类型。话虽如此,我们需要删除这个答案吗? - jamesmortensen
哈哈,所以还有人被困在16位编译器上。嵌入式系统? - Axel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接