【问题标题】:convert char* to float* using union or memcpy使用 union 或 memcpy 将 char* 转换为 float*
【发布时间】:2018-07-18 19:48:34
【问题描述】:

我正在尝试将字符流转换为 float *,但不知何故无法获得正确的结果。

char  *char_data_  = static_cast<char *>("abcdefghijklmnopqrstuvwx");
float *float_data_ = reinterpret_cast<float *>(malloc(strlen(char_data_)/sizeof(float)));

printf("%ld\n", strlen(char_data_));

memcpy(float_data_, reinterpret_cast<float *>(char_data_), strlen(char_data_)/sizeof(float));

for ( auto n = 0 ; n < strlen(char_data_)/sizeof(float); n++) {
    printf("%f\n", *(float_data_ + n));
}

以下是我的结果,但它显然是不正确的。有人可以看看吗?

16777999408082104352768.000000
0.000000
0.000000
0.000000
0.000000
0.000000

是否可以使用 union 来解决这个问题?

我希望每个字符流有 4 个字节的块并将其保存到浮点数组中。

【问题讨论】:

  • @dbush - 我正在尝试从字符流中提取 4 个字节并将其分配给浮点数组。
  • 我正在尝试从字符流中提取 4 个字节并将其分配给一个浮点数组。 然后你的 我期待你的部分问题是完全错误的。您应该期待一些浮点数。
  • A float 需要以 IEEE-754 单精度浮点格式排序的数据。我从未见过 IEEE-754 格式的拉丁字母。
  • " 我正在尝试从字符流中分别提取 4 个字节......" 这根本不可行。你希望从“hijk”得到什么浮动
  • @infoclogged 因为您没有复制足够的字节。你需要strlen(char_data_),而不是strlen(char_data_)/sizeof(float)。此外,真正的浮点数流可能包含值为 0 的字节,在这种情况下使用字符串函数是无用的。

标签: c++


【解决方案1】:

如何使用 union 或 memcpy 将 char* 转换为 float*?

使用 memcpy 和 reinterpretcast:

#include <cstdio>
#include <string>
#include <cstring>
#include <cassert>
#include <cstddef>

void using_pointer(const char *s, size_t slen) {
    const float *f = reinterpret_cast<const float*>(s);
    for (size_t i = 0; i < slen/sizeof(float); ++i) {
        printf("%zu = %f\n", i, f[i]);
    }
}

void using_memcpy(const char* s, size_t slen) {
    float* f = new float[slen/sizeof(float)];
    memcpy(f, s, slen/sizeof(float)*sizeof(float));
    for (size_t i = 0; i < slen/sizeof(float); ++i) {
        printf("%zu = %f\n", i, f[i]);
    }
    delete f;
}

int main() {
    static_assert(sizeof(float) == 4, "");
    std::string stdstr(
        "\x00\x00\x80\x3f" // float 1.0
        "\x00\x00\x81\x3f" // just randomly changed 0x80 to 0x81 
        "\x00\x00\x82\x3f"
    , 4 * 3);

    printf("using_pointer:\n");
    using_pointer(stdstr.c_str(), stdstr.size());
    printf("using_memcpy:\n");
    using_memcpy(stdstr.c_str(), stdstr.size());

    std::string s2("abcdefghijklmnopqrstuvwx");
    printf("Last:\n");
    using_memcpy(s2.c_str(), s2.size());

    return 0;
}

将在http://www.onlinegdb.com 使用的机器上输出:

using_pointer:                                                                                                         
0 = 1.000000                                                                                                           
1 = 1.007812                                                                                                           
2 = 1.015625                                                                                                           
using_memcpy:                                                                                                          
0 = 1.000000                                                                                                           
1 = 1.007812                                                                                                           
2 = 1.015625                                                                                                           
Last:                                                                                                                  
0 = 16777999408082104352768.000000                                                                                     
1 = 4371022013021616997400576.000000                                                                                   
2 = 1138400301458999111806091264.000000                                                                                
3 = 296401655701622853703074578432.000000                                                                              
4 = 77151445562813935304650187079680.000000                                                                            
5 = 20076561220099179535696200212676608.000000      

@edit:正如@drescherjm 在 cmets 中指出的那样,除非基于未定义的行为,否则在 C++ 中使用 union 在 char 和 float 表示之间进行转换是不可能的。在 C++ 中,联合不用于更改字节的表示。联合用于最多存储多个对象之一。您可以将 char 数组或 float 数组存储在联合中,而不是同时存储,并且您不能(至少不应该)使用 C++ 中的联合在浮点和字符表示之间进行转换。

你的代码发生了什么?

// static_cast<char*> from string is forbidden in iso C++. It's better to use  should use at least std::string.c_str()
// or use char char_data_[sizeof("abcdefghijklmnopqrstuvwx"]; memcpy(char_data_, "abcdefghijklmnopqrstuvwx", sizeof(char_data_));
char  *char_data_  = static_cast<char *>("abcdefghijklmnopqrstuvwx");
// you are allocating strlen("abcdefghijklmnopqrstuvwx")/4 = 24/4 = 6 bytes of memory. That's memory for 1 and a half float numbers.
float *float_data_ = reinterpret_cast<float *>(malloc(strlen(char_data_)/sizeof(float)));

// this will print '24\n'
printf("%ld\n", strlen(char_data_));

// you are copying 6 bytes of data from char_data_ to float_data_
// now float_data_ contains "abcdef" without '\0'
memcpy(float_data_, reinterpret_cast<float *>(char_data_), strlen(char_data_)/sizeof(float));

// this will print  16777999408082104352768.000000 on the first loop, which is "abcd" in hex in ascii
// then this invokes undefined behaviour cause of out of bound access
// you are trying to access elements number 2, 3, 4, 5 while float_data_ points to only 6 bytes, which is 1,5 float numbers, so even float_data_[1] is out of bound and undefined behaviour
for ( auto n = 0 ; n < strlen(char_data_)/sizeof(float); n++) {
    printf("%f\n", *(float_data_ + n));
}              

【讨论】:

  • 非常感谢! malloc 是您正确识别的问题。让我看看工会,然后会回来。
  • 这两种方法不都是未定义的行为吗?我知道工会绝对是 c++ 中的 UB。 stackoverflow.com/questions/11373203/…
  • 我明白了。使用联合是未定义的行为。为什么要memcpy UB?我确信在我的机器下(!)存储在字符串中的字节代表浮点数,所以我可以转换表示,因为我知道底层字节代表浮点对象。
  • “使用指针”版本导致未定义的行为(严格的别名冲突)
  • 但是底层内存包含一个与float类型兼容的对象,所以它不是严格的别名违规。我知道这个内存在我的机器上包含浮动对象,所以它不是严格的别名违规。是吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-30
  • 2012-05-25
  • 1970-01-01
  • 2012-12-22
  • 2020-08-22
  • 2015-08-30
  • 2010-10-12
相关资源
最近更新 更多