Sizeof Array

Sizeof Array


So, you want to know how many elements there are in your array? This is a tricky subject.

Before we go any further, you should be familiar with the concepts discussed in  c-strings and pointers . It is also worth your time to read through the  tutorial on arrays .

A beginner will often try something along the lines of  size = sizeof( myarray )  (which is incorrect). The more experienced programmers will often go for the  C approach  (discussed below), because they do not know the  C++ approach . Here we will explore both C and C++ approaches, and why the  C++ approach  is superior.

But first:

Rationale 

There are two broad classes of reasons to want to have some kind of method to return information about your array:
  • You think the One Definition Rule should apply to the dimensions of each rank of an array.
    The traditional, C way to do this is to declare the size of each rank (or dimension) using a named constant or macro:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    #include <iostream>
    
    int main()
    {
      const size_t ROWS = 3;
      const size_t COLS = 4;
      double M[ ROWS ][ COLS ];
    
      std::cout << "M is a " << ROWS << " by " << COLS << " matrix.\n";
    
      return 0;
    }

    This is actually a very simple, smart way to keep this information. Unless you tell it not to, the compiler will optimize ROWS and COLS out of existence. It does, however, seem to use up a lot of space for a single array’s definition.
     
  • You want to pass a multi-dimensional array with variable rank dimensions (the size of each dimension) to a function, and use it in the function normally.
    C and C++ require you to specify the size of each dimension of an array except the first, or major, one. You can use this information when you pass such an array to a function:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    #include <iostream>
    using namespace std;
    
    void print( int a[][ 4 ], size_t n )
    {
      for (size_t i = 0; i < n; i++)
      {
        for (size_t j = 0; j < 4; j++)
        {
          cout << a[ i ][ j ] << " ";
        }
        cout << endl;
      }
    }
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    
    int main()
    {
      int M[ 3 ][ 4 ] =                 
      {
        { 1, 0, 0, 0 },
        { 0, 1, 0, 0 },
        { 0, 0, 1, 0 }
      };
    
      print( M, 3 );
    
      return 0;
    }

    The problem, of course, is that this limits the function considerably. While it does not matter how long the major dimension is, the exact size of the minor dimensions do matter. I cannot, for example, pass a 3 by 3 array to the print() function.

    It is more desirable to be able to simply say that the array has two ranks (or dimensions), regardless of the size of each dimension, and still be able to use it like an array inside the function.
This FAQ deals primarily with handling the first situation without having to explicitly name constant variables for each rank’s size. We want, essentially, to be able to say:

1
2
  int primes7[] = { 2, 3, 5, 7, 11, 13, 17 };
  cout << "There are " << SizeOfArray( primes7 ) << " primes in 'primes7[]'.\n";

We will deal with the second situation in the next couple of FAQs. So, with that in mind, let’s dig-in!

The Basics 

Firstly, the sizeof() operator does not give you the number of elements in an array, it gives you the number of bytes a thing occupies in memory. Hence:

千万注意了!!!!!!

不过,可以sizeof(xs) / sizeof(int)

注意xs的类型和除数中的变量类型一致。


1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main()
{
  char s[] = { 1, 2, 3, 4, 5, 0 };
  int xs[] = { 1, 2, 3, 4, 5, 0 };

  printf( "sizeof( s ) = %d\n",  sizeof( s  ) );
  printf( "sizeof( xs ) = %d\n", sizeof( xs ) );

  return 0;
}
sizeof( s ) = 6
sizeof( xs ) = 24

This causes some confusion, because the first time many beginners try to use  sizeof()  is often with a character string, and a  char  is  always  one byte long. An  int , however, will be something different. (The actual size depends on your computer. On mine, an  int  is four bytes long, and six four-byte elements is 24 bytes total.)

Secondly, an array type will degenerate into a pointer type at  every  opportunity. The difference is that an array type knows all about the array, including its dimensions, whereas a pointer type only knows the address of the one thing it is pointing at. Here is an example to explain (you may have seen this, right?):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

void f( const char s[] )
{
  printf( "The size of f()'s     (s) is %d\n", sizeof( s ) );  
}

int main()
{
  const char s[] = "Hello world!";
  printf( "s = \"%s\"\n", s );
  printf( "The size of main()'s  (s) is %d\n", sizeof( s ) );
  f( s );
  printf( "The size of main()'s (&s) is %d\n", sizeof( &s ) );
  return 0;
}
s = "Hello world!"
The size of main()'s  (s) is 13
The size of f()'s     (s) is 4
The size of main()'s (&s) is 4

So, what happened?

In  main() , the variable ‘s’ is an  array , meaning it has all that useful information attached to it, like its size in memory. The example string is thirteen characters long (and hence, thirteen bytes long).

But when we pass it to the function  f() , it is no longer an array, it is a  pointer . Likewise, you get the same result by directly converting the array to a pointer (line 14). On my 32-bit operating system, a pointer is 4 bytes long. Your results might vary.

This is often surprising to beginners because the syntax  const char s[]  only means ‘an array’ when it is  not  a function argument. When it is, it is the same as saying  const char* s even if you do specify a dimension :

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

void f( const char s[ 3 ] )
{
  printf( "s[ 3 ] = \"%s\"\n", s );
}

int main()
{
  f( "Hello world!" );
  return 0;
}
s[ 3 ] = "Hello world!"

How we handle this differs between C and C++.

C++ 

The first thing to do is not let the array degenerate into a pointer, if possible. The second is to get the  length  of the array instead of its  size in memory .

C++ makes this simple using the power of templates. Here is a function that returns the length of the argument array.

1
2
3
4
5
6
template <typename T, size_t N>
inline
size_t SizeOfArray( const T(&)[ N ] )
{
  return N;
}
Yes, I know that the argument syntax here is confusingly wonky. What it says is that the (unnamed) argument is a reference (&) to an array of N elements [ N ] of type const T.

Some compilers (like the GCC) still choke on any effort to make the reference itself const, but it really doesn’t matter here, since the array is referenced as immutable anyway...
 

Now, as long as I am still handling an array (and not a pointer), I can get its length by simply using the SizeOfArray()  function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

template <typename T, size_t N>
inline
size_t SizeOfArray( const T(&)[ N ] )
{
  return N;
}

int main()
{
  const char s[] = "Hello world!";

  cout << "s[] is " << SizeOfArray( s )
       << " characters long.\n";
  cout << "(That includes the null at the end.)\n";

  return 0;
}
s[] is 13 characters long.
(That includes the null at the end.)

The nice thing about this approach is that it  will not compile  if you try to use a pointer:

1
2
3
  const char* s = "Hello world!";
  cout << "s is " << SizeOfArray( s )  // foo, 's' is a pointer
       << " characters long.\n";

The other nice thing is that, if your compiler does its job properly, the compiler will optimize that function out of existence and just plug the actual length of the array in your code where you call it. Neat, huh?

variable-length arrays 
Immediately after authoring this page, someone on the forum tried to use  SizeOfArray()  like this:

1
2
3
4
5
  int   ii[ 42 ];
  float ff[ SizeOfArray( ii ) ];

  cout << "ii.length = " << SizeOfArray( ii ) << endl;
  cout << "ff.length = " << SizeOfArray( ff ) << endl;  /* ERROR! */

<-- Line 2 is NON-STANDARD

Not surprisingly, it didn’t work.  Variable length arrays are not legal in C++.  Compilers that allow it, like the GCC, do so with various hacks. But, since you already know the length of the array, why do you need to ask for it another way? Just use what you already know.

VLAs  are  legal in C99, and the  C method  works just fine with them.

C++11 

The  SizeOfArray()  function we have written so far works just fine. However, there is another way to get that information in C++11.

The  <type_traits>  header provides a number of classes for inspecting array types. Let’s use them:

1
2
3
4
5
6
7
8
9
#include <cstddef>
#include <type_traits>

template <typename A>
typename std::enable_if <std::is_array <A> ::value, size_t> ::type
SizeOfArray( const A& a )
{
  return std::extent <A> ::value;
}

This does the same thing as what we already have, only it produces an unfamiliar error message because we use the fancy new  std::enable_if  type to force the return type of the function to  exist only if  the template argument ‘A’ is an array type. The  std::extent  type returns the dimension of a given rank of an array (in this case, it defaults to the first rank).

You can also use  std::extent  directly and skip the SizeOfArray() function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <type_traits>
using namespace std;

int main()
{
  int five_primes[] = { 2, 3, 5, 7, 11 };
  cout << "five_primes[ "
       << extent <decltype( five_primes )> ::value
       << " ] = { ";
  for (int x: five_primes) cout << x << ", ";
  cout << "\b\b };\n";
  return 0;
}
five_primes[ 5 ] = { 2, 3, 5, 7, 11 };

The  std::extent  template returns zero if the object given it is not an array.

std::array 

The new C++11  std::array  class provides a lot of STL sequence container information about your array, including its size() .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <array>
#include <iostream>
using namespace std;

int main()
{
  array <char, 13> s = {"Hello world!"};

  cout << "s[] = \"" << s.data() << "\"\n";
  cout << "s[] is " << s.size() << " characters long.\n";
  cout << "(That includes the null at the end.)\n";

  return 0;
}
s[] = "Hello world!"
s[] is 13 characters long.
(That includes the null at the end.)

The  std::array  class has several important characteristics, including the fact that it  will not  automatically degenerate to a pointer. (We had to use the  data()  member function to do that.)

However, as the dimension of the array (the size of its rank) is encoded into the type (as a template argument), we are stuck when we wish to pass  variable -length arrays to a function.

In C, there is no way to have a compile-time safe way to do this. There is, however, a common macro to do it.

 
#define SIZEOF_ARRAY( a ) (sizeof( a ) / sizeof( a[ 0 ] )) 

It simply uses a little math and takes the size of the array in memory and divides that by the size of a single element in the array, which equals the number of elements in the array.

You use it very much the same: make sure you have an array (and not a pointer) and use the macro:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

#define SIZEOF_ARRAY( a ) (sizeof( a ) / sizeof( a[ 0 ] ))

int main()
{
  const char s[] = "Hello world!";
  printf( "s[] is %d characters long\n", SIZEOF_ARRAY( s ) );
  printf( "(That includes the null at the end.)\n" );
  return 0;
}
s[] is 13 characters long
(That includes the null at the end.)

The C++ way is superior simply because it catches any errors you make – you  must  pass an array to the function, whereas the existing C method does not. Some people will also complain that the C way uses a macro, but it really isn’t an issue here (because you  must  pass an array as argument, and nothing else). There is no compile-time check to enforce this, though.

Multiple Dimensions 

Thus far we have only dealt with arrays with  one  dimension. It’s time to dig a little deeper.

The first observation we need to make is that the above methods already work on multidimensional arrays! The trick, of course, is that it will only return the length of the outermost dimension.

   C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

template <typename T, size_t N> inline
size_t SizeOfArray( const T(&)[ N ] )
{
  return N;
}

int main()
{
  int M[ 3 ][ 4 ] =
  {
    { 1, 0, 0, 0 },
    { 0, 1, 0, 0 },
    { 0, 0, 1, 0 }
  };
  std::cout << "M[ m ][ n ], m = "
    << SizeOfArray( M ) << "\n";


  return 0;
}
   C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

#define SIZEOF_ARRAY( a ) \
  (sizeof( a ) / sizeof( a[ 0 ] ))




int main()
{
  int M[ 3 ][ 4 ] =
  {
    { 1, 0, 0, 0 },
    { 0, 1, 0, 0 },
    { 0, 0, 1, 0 }
  };
  printf( "M[ m ][ n ], m = %d\n",
    SIZEOF_ARRAY( M ) );


  return 0;
}
Output
M[ m ][ n ], m = 3






















The next observation to make is that you can apply the same trick to the next dimension. Go ahead and add the following lines to the code above:

19
20
  std::cout << "M[ m ][ n ], n = "
    << SizeOfArray( M[ 0 ] ) << "\n"; 
19
20
  printf( "M[ m ][ n ], m = %d\n",
    SIZEOF_ARRAY( M[ 0 ] ) );
M[ m ][ n ], m = 3
M[ m ][ n ], n = 4

The final observation to make is that a single element of any array has a size of  one element . This is a given. So, if you try to use  SIZEOF_ARRAY()  on an element (which is technically not an array), the answer you get will be  one . The SizeOfArray()  template function will not compile when given an element (but we can work this out with a little function overloading).

So, with that knowledge in hand, let us move on to the next couple of FAQs:  Pass my (N-dimensional) array to a function?  and  Pass any (N-dimensional) array to a single function? .
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值