1

我有一个代码,其中字符数组由整数填充(转换为 char 数组),并由另一个函数读取,该函数将其重新转换回整数。我使用以下函数将转换为 char 数组:

char data[64];
int a = 10;
std::string str = boost::lexical_cast<std::string>(a);
memcpy(data + 8*k,str.c_str(),sizeof(str.c_str()));   //k varies from 0 to 7

并使用以下方法重新转换回字符:

char temp[8];
memcpy(temp,data+8*k,8);
int a = atoi(temp);

这通常工作得很好,但是当我尝试将它作为涉及 qt(版本 4.7)的项目的一部分时,它编译得很好,并在尝试使用 memcpy() 读取时给我分段错误。请注意,分段错误仅在读取循环中发生,而不是在写入数据时发生。我不知道为什么会发生这种情况,但我想通过任何方法完成它。

那么,是否还有其他我可以使用的函数可以接收字符数组、第一位和最后一位并将其转换为整数。然后我根本不必使用 memcpy() 。我想做的是这样的:

new_atoi(data,8*k,8*(k+1)); // k varies from 0 to 7

提前致谢。

4

2 回答 2

9

您只复制 4 个字符(取决于系统的指针宽度)。这将使超过 4 个字符的数字非空终止,从而导致 atoi 输入中的字符串失控

 sizeof(str.c_str()) //i.e. sizeof(char*) = 4 (32 bit systems)

应该

 str.length() + 1

或者字符不会被空终止

仅限 STL:

make_testdata(): 一路向下看

你为什么不使用流...?

#include <sstream>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>

int main()
{
    std::vector<int> data = make_testdata();

    std::ostringstream oss;
    std::copy(data.begin(), data.end(), std::ostream_iterator<int>(oss, "\t"));

    std::stringstream iss(oss.str());

    std::vector<int> clone;
    std::copy(std::istream_iterator<int>(iss), std::istream_iterator<int>(),
              std::back_inserter(clone));

    //verify that clone now contains the original random data:
    //bool ok = std::equal(data.begin(), data.end(), clone.begin());

    return 0;
}

您可以使用 atoi/itoa 和一些调整在普通 C 中更快地完成它,但我认为如果您需要速度,您应该使用二进制传输(请参阅Boost Spirit Karmaprotobuf以获得良好的库)。

提升业力/气:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>

namespace qi=::boost::spirit::qi;
namespace karma=::boost::spirit::karma;

static const char delimiter = '\0';

int main()
{
    std::vector<int> data = make_testdata();

    std::string astext;
//  astext.reserve(3 * sizeof(data[0]) * data.size()); // heuristic pre-alloc
    std::back_insert_iterator<std::string> out(astext);

    {
        using namespace karma;
        generate(out, delimit(delimiter) [ *int_ ], data);
    //  generate_delimited(out, *int_, delimiter, data); // equivalent
    //  generate(out, int_ % delimiter, data); // somehow much slower!
    }

    std::string::const_iterator begin(astext.begin()), end(astext.end());
    std::vector<int> clone;
    qi::parse(begin, end, qi::int_ % delimiter, clone);

    //verify that clone now contains the original random data:
    //bool ok = std::equal(data.begin(), data.end(), clone.begin());

    return 0;
}

如果您想改为进行体系结构独立的二进制序列化,您可以使用这种微小的适应使事情变得更快请参阅下面的基准...):

karma::generate(out, *karma::big_dword, data);
// ...
qi::parse(begin, end, *qi::big_dword, clone);

提升序列化

在二进制模式下使用 Boost Serialization 可以达到最佳性能:

#include <sstream>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp>

int main()
{
    std::vector<int> data = make_testdata();

    std::stringstream ss;
    {
        boost::archive::binary_oarchive oa(ss);
        oa << data;
    }

    std::vector<int> clone;
    {
        boost::archive::binary_iarchive ia(ss);
        ia >> clone;
    }

    //verify that clone now contains the original random data:
    //bool ok = std::equal(data.begin(), data.end(), clone.begin());

    return 0;
}

测试数据

以上所有版本通用

#include <boost/random.hpp>

// generates a deterministic pseudo-random vector of 32Mio ints
std::vector<int> make_testdata()
{
    std::vector<int> testdata;

    testdata.resize(2 << 24);
    std::generate(testdata.begin(), testdata.end(), boost::mt19937(0));

    return testdata;
}

基准

我对它进行了基准测试

  • 使用2<<24(33554432) 随机整数的输入数据
  • 不显示输出(我们不想测量终端的滚动性能)
  • 大致的时间是
    • 仅 STL 的版本实际上在 12.6 秒时还不错
    • Karma/Qi 文本版本 在 18s 5.1s中运行,感谢 Arlen 的提示generate_delimited:)
    • Karma/Qi 二进制版本 (big_dword) 仅需 1.4 秒(大约12倍 3-4倍的速度
    • Boost Serialization 大约需要 0.8 秒(或者当替换文本存档而不是二进制文件时,大约需要 13 秒)
于 2011-06-11T19:13:56.983 回答
2

Karma/Qi 文本版本绝对没有理由比 STL 版本慢。我改进了 Karma/Qi 文本版本的 @sehe 实现以反映该声明。

以下Boost Karma/Qi文本版本的速度是 STL 版本的两倍多:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/random.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>

namespace ascii = boost::spirit::ascii;
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phoenix = boost::phoenix;


template <typename OutputIterator>
void generate_numbers(OutputIterator& sink, const std::vector<int>& v){

  using karma::int_;
  using karma::generate_delimited;
  using ascii::space;

  generate_delimited(sink, *int_, space, v);
}

template <typename Iterator>
void parse_numbers(Iterator first, Iterator last, std::vector<int>& v){

  using qi::int_;
  using qi::phrase_parse;
  using ascii::space;
  using qi::_1;
  using phoenix::push_back;
  using phoenix::ref;

  phrase_parse(first, last, *int_[push_back(ref(v), _1)], space);
}

int main(int argc, char* argv[]){

  static boost::mt19937 rng(0); // make test deterministic
  std::vector<int> data;
  data.resize(2 << 24);
  std::generate(data.begin(), data.end(), rng);

  std::string astext;
  std::back_insert_iterator<std::string> out(astext);
  generate_numbers(out, data);

  //std::cout << astext << std::endl;

  std::string::const_iterator begin(astext.begin()), end(astext.end());
  std::vector<int> clone;
  parse_numbers(begin, end, clone);

  //verify that clone now contains the original random data:
  //std::copy(clone.begin(), clone.end(), std::ostream_iterator<int>(std::cout, ","));

  return 0;
}
于 2011-06-13T13:49:37.413 回答