【问题标题】:Why is memset() incorrectly initializing int?为什么 memset() 错误地初始化 int?
【发布时间】:2011-10-29 08:31:46
【问题描述】:

为什么下面程序的输出是84215045

int grid[110];
int main()
{
    memset(grid, 5, 100 * sizeof(int));
    printf("%d", grid[0]);
    return 0;
}

【问题讨论】:

  • 我不认为这会导致您看到的问题,但是您是否故意将 grid 设为 110 个数组,然后只分配其中的 100 个?
  • 如果您将该数字打印为十六进制值,您可能会看到问题。
  • @Alex : 它不会做出改变,因为我正在打印网格 [0]
  • 对,它只是看起来错字,我想确保你知道它

标签: c++ c memset


【解决方案1】:
memset(grid, 5, 100 * sizeof(int));

您正在设置 400 个字节,从 (char*)grid 开始到 (char*)grid + (100 * sizeof(int)) 结束,到值 5(这里需要强制转换,因为 memset 处理 字节,而指针算术交易在objects

84215045 十六进制是0x05050505;因为int(在您的平台/编译器/等)由四个字节表示,所以当您打印它时,您会得到“四个五”。

【讨论】:

  • 设置 (100 * 4) 字节,从 &grid[0] 开始,确定吗?
  • 现在我可以理解你的答案了 :D ,但还有解决方案吗?!
【解决方案2】:

memset 将目标缓冲区的每个 byte 设置为指定值。在您的系统上,int 是四个字节,每个字节在调用 memset 之后是 5。因此,grid[0] 的值为 0x05050505(十六进制),即十进制的 84215045

一些平台为memset 提供替代API,将更宽的模式写入目标缓冲区;例如,在 OS X 或 iOS 上,您可以使用:

int pattern = 5;
memset_pattern4(grid, &pattern, sizeof grid);

获得您似乎期望的行为。你的目标是什么平台?

在 C++ 中,您应该只使用 std::fill_n:

std::fill_n(grid, 100, 5);

【讨论】:

  • Windows 7 32 位 // gnu++ 编译器
  • @Ahmad:我不确定 Windows 是否提供这样的 API;如果是这样,我相信其他人可以指出。
  • @Ahmad:没有注意到你标记了这个 C++;只需使用std::fill
  • 通过 memset 分配指针是否安全?如果 int 不等于 uintptr_t 怎么办
  • @Dmitry:您的要求并不完全清楚,但这里的示例并不假定 intuintptr_t 相同(第一个假定 int 是四个字节,但这是一个有效的假设,memset_pattern4 API 可用)。
【解决方案3】:

好吧,memset 使用所选值写入字节。因此 int 看起来像这样:

00000101 00000101 00000101 00000101

然后解释为 84215045。

【讨论】:

  • 所以...有什么解决方案吗?! :D
【解决方案4】:

不要使用 memset。

您将内存的每个字节 [] 设置为值 5。每个 int 长度为 4 个字节 [5][5][5][5],编译器将其正确解释为 5*256*256*256 + 5*256*256 + 5 *256 + 5 = 84215045。相反,使用 for 循环,它也不需要 sizeof()。一般来说, sizeof() 意味着你正在做一些艰难的事情。

for(int i=0; i<110; ++i)
    grid[i] = 5;

【讨论】:

  • 我只是觉得 memset 更快 :D
  • memset的后端基本上就是这么干的。
  • @Mooing Duck:memset 究竟是如何让你的程序崩溃的?
  • 实际上,对于数组初始化的具体情况,我认为memset 更容易正确,因为您可以简单地使用sizeof array。这样,如果您将来更改数组的大小,初始化会自动保持同步。
  • 当然,如果你在写C++,这应该是一个容器和std::fill
【解决方案5】:

memset 是关于设置字节,而不是值。在 C++ 中设置数组值的众多方法之一是 std::fill_n

std::fill_n(grid, 100, 5);

【讨论】:

  • 这个函数比循环所有元素更快??
  • @Ahmad:这个函数可能会遍历所有元素。
  • 一个好的编译器会将 for 循环和 std::fill_n 转换为使用宽对齐存储(可能是向量)的优化代码序列或已知在目标平台上快速的库调用.
【解决方案6】:

你实际上并没有说你想让你的程序做什么。

假设您要将 grid 的前 100 个元素中的每一个都设置为 5(并忽略 100110 的差异),只需执行以下操作:

for (int i = 0; i < 100; i ++) {
    grid[i] = 5;
}

我了解您担心速度,但您的担心可能是错误的。一方面,memset() 可能会被优化,因此比简单的循环更快。另一方面,优化可能包括一次写入多个字节,这就是这个循环所做的。另一方面,memset() 无论如何都是一个循环;显式编写循环而不是将其埋在函数调用中并不会改变这一点。另一方面,即使循环很慢,也可能无关紧要。专注于编写清晰的代码,并考虑对其进行优化如果实际测量表明存在严重的性能问题。

你写问题所花费的时间比你的计算机设置grid所花费的时间要多很多。

最后,在我手忙脚乱(为时已晚!)之前,如果 memset() 没有按照您的意愿行事,那么它的速度有多快都没有关系。 (完全不设置grid会更快!)

【讨论】:

    【解决方案7】:

    由于 memset 写入字节,我通常使用它将一个 int 数组设置为零,例如:

    int a[100];
    memset(a,0,sizeof(a));
    

    或者你可以用它来设置一个字符数组,因为一个字符就是一个字节:

    char a[100];
    memset(a,'*',sizeof(a));
    

    另外,一个int数组也可以通过memset设置为-1:

    memset(a,-1,sizeof(a));
    

    这是因为 -1 在 int 中是 0xffffffff,在 char(一个字节)中是 0xff。

    【讨论】:

      【解决方案8】:

      此代码已经过测试。这是一种将“整数”数组 memset 为 0 到 255 之间的值的方法。

      MinColCost=new unsigned char[(Len+1) * sizeof(int)];
      
      memset(MinColCost,0x5,(Len+1)*sizeof(int));
      
      memset(MinColCost,0xff,(Len+1)*sizeof(int));
      

      【讨论】:

        【解决方案9】:

        如果你在 shell 上输入 man memset,它会告诉你

        void * memset(void *b, int c, size_t len)

        对此的简单英文解释是,它填充一个长度为len 的字节字符串b,每个字节都有一个值c

        对于你的情况,

        memset(grid, 5, 100 * sizeof(int));
        

        由于sizeof(int)==4,因此上面的代码片段看起来像:

        for (int i=0; i<100; i++)
            grid[i]=0x05050505;
        

        char *grid2 = (char*)grid;
        for (int i=0; i<100*sizeof(int); i++)
            grid2[i]=0x05;
        

        它会打印出 84215045

        但在大多数 C 代码中,我们希望将一块内存块初始化为零。

        • char 类型 --> \0NUL
        • int 类型 --> 0
        • float 类型 --> 0.0f
        • double 类型 --> 0.0
        • 指针类型 --> nullptr

        gccclang 等现代编译器可以自动为您处理好这个问题。

        // variadic length array (VLA) introduced in C99
        int len = 20;
        char carr[len];
        int iarr[len];
        float farr[len];
        double darr[len];
        memset(carr, 0, sizeof(char)*len);
        memset(iarr, 0, sizeof(int)*len);
        memset(farr, 0, sizeof(float)*len);
        memset(darr, 0, sizeof(double)*len);
        for (int i=0; i<len; i++)
        {
            printf("%2d: %c\n", i, carr[i]);
            printf("%2d: %i\n", i, iarr[i]);
            printf("%2d: %f\n", i, farr[i]);
            printf("%2d: %lf\n", i, darr[i]);
        }
        

        但请注意,C ISO 委员会并没有强加这样的定义,它是特定于编译器的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-05-18
          • 1970-01-01
          • 1970-01-01
          • 2013-03-12
          • 1970-01-01
          • 1970-01-01
          • 2023-03-15
          相关资源
          最近更新 更多