Gaith
Gaith

Reputation: 828

How to zero a new array?

1:

int a[100] = {};

2:

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

3:

int a[100];
fill(a, a + 100, 0);

What is the best way to zero a new array from the methods shown above and what is the difference between them?

Upvotes: 4

Views: 357

Answers (3)

Adam Burry
Adam Burry

Reputation: 1902

Using Keith's sample code. The difference under GCC are as follows:

GCC 4.7.3: g++ -Wall -Wextra -std=c++0x -O3 -c array-fill.cpp

#include <algorithm>
#include <cstring>

int  fref() {
    int a[1024];
    return a[512] - a[256]; }

int f1() {
    int a[1024] = {};
    return a[512] - a[256]; }

int  f2() {
    int a[1024];
    std::memset(a, 0, sizeof(a));
    return a[512] - a[256]; }

int f3() {
    int a[1024];
    std::fill(a, a + 100, 0);
    return a[512] - a[256]; }

Disassemble

objdump -d array-fill.o | c++filt

00000000 <fref()>:
   0:   b8 00 10 00 00          mov    $0x1000,%eax
   5:   e8 00 00 00 00          call   a <fref()+0xa>
   a:   29 c4                   sub    %eax,%esp
   c:   8b 84 24 00 08 00 00    mov    0x800(%esp),%eax
  13:   2b 84 24 00 04 00 00    sub    0x400(%esp),%eax
  1a:   81 c4 00 10 00 00       add    $0x1000,%esp
  20:   c3                      ret
  21:   eb 0d                   jmp    30 <f1()>

00000030 <f1()>:
  30:   31 c0                   xor    %eax,%eax
  32:   c3                      ret
  33:   8d b6 00 00 00 00       lea    0x0(%esi),%esi
  39:   8d bc 27 00 00 00 00    lea    0x0(%edi,%eiz,1),%edi

00000040 <f2()>:
  40:   b8 00 10 00 00          mov    $0x1000,%eax
  45:   e8 00 00 00 00          call   4a <f2()+0xa>
  4a:   29 c4                   sub    %eax,%esp
  4c:   31 c0                   xor    %eax,%eax
  4e:   81 c4 00 10 00 00       add    $0x1000,%esp
  54:   c3                      ret
  55:   8d 74 26 00             lea    0x0(%esi,%eiz,1),%esi
  59:   8d bc 27 00 00 00 00    lea    0x0(%edi,%eiz,1),%edi

00000060 <f3()>:
  60:   b8 00 10 00 00          mov    $0x1000,%eax
  65:   e8 00 00 00 00          call   6a <f3()+0xa>
  6a:   29 c4                   sub    %eax,%esp
  6c:   89 e0                   mov    %esp,%eax
  6e:   8d 94 24 90 01 00 00    lea    0x190(%esp),%edx
  75:   c7 00 00 00 00 00       movl   $0x0,(%eax)
  7b:   83 c0 04                add    $0x4,%eax
  7e:   39 d0                   cmp    %edx,%eax
  80:   75 f3                   jne    75 <f3()+0x15>
  82:   8b 84 24 00 08 00 00    mov    0x800(%esp),%eax
  89:   2b 84 24 00 04 00 00    sub    0x400(%esp),%eax
  90:   81 c4 00 10 00 00       add    $0x1000,%esp
  96:   c3                      ret

The C style initialization (f1) certainly allowed optimization in this case!

Upvotes: 1

Keith
Keith

Reputation: 6834

Decided to investigate the performance question using VS2010, full optimization.

Interesting result:

1: 13105

2: 13044

3: 4546

No initialization case: 906.

So it looks highly like VS2010 uses memset for case 1, but that fill is better optimized.

#include "stdafx.h"
#include <Windows.h>
#include <algorithm>
#include <iostream>

int  fref()
{
    int a[1024];
    return a[512] - a[256];
}

int f1()
{
    int a[1024] = {};

    return a[512] - a[256];
}

int  f2()
{
    int a[1024];
    memset(a, 0, sizeof(a));
    return a[512] - a[256];
}



int f3()
{
    int a[1024];
    std::fill(a, a + 100, 0);
    return a[512] - a[256];
}

typedef int (*Function)();

LONGLONG time(Function function)
{
    const unsigned numLoops = 50000;
    LARGE_INTEGER start;
    QueryPerformanceCounter(&start);
    for(unsigned j = 1; j != numLoops; ++j)
    function();
    LARGE_INTEGER end;
    QueryPerformanceCounter(&end);
    return end.QuadPart-start.QuadPart;
}

Function tests[]= 
{
    &fref, &f1, &f2, &f3
};

const unsigned numTests = sizeof(tests)/sizeof(tests[0]);

LONGLONG results[numTests] = {};


int _tmain(int argc, _TCHAR* argv[])
{
    for(unsigned i = 0; i != numTests; ++i)
    {
        results[i] = time(tests[i]);
    }
    for(unsigned i = 0; i != numTests; ++i)
        std::cout << results[i] << std::endl;
    getchar();
    return 0;
}

Upvotes: 2

Cramer
Cramer

Reputation: 1795

1: The best. It sets all the values to their default value which for most is 0.

2: This is dangerous, it copies the pattern 0 through the whole array. For example if the array is of floats, there is no guarantee that it is represented as zeros. Also memset copies byte wise NOT word wise which can cause all sorts of issues if you pass it something other than zero. For example memset(a, 1, ...) will cause it to be filled with 16843009. Memset should not be used unless you are using C-strings.

3: Legal, and legible. Easy to extend to non-zero values while (1) will not. Although more verbose.

Upvotes: 4

Related Questions