How to Understand the _ countof () macro in stdlib. h

Source: Internet
Author: User

There is a macro _ countof in stdlib. H, as follows:

Extern "C ++"
{
Template <typename _ countoftype, size_t _ sizeofarray>
Char (* _ countof_helper (unaligned _ countoftype (& _ array) [_ sizeofarray]) [_ sizeofarray];

# DEFINE _ countof (_ array) sizeof (* _ countof_helper (_ array ))
}

This macro is used to obtain the size of an array element.

Use:

# Include <stdlib. h>

Int main ()

{

Int A [5];

......

Int arraysize = _ countof (a); // obtain the size of array.

}

//////////////////////////////////////// //////////////////////////////////////// ////////////////

This macro uses some less commonly used syntax. So I don't quite understand it all at once.

So let's talk about how to understand it.

For ease of instruction, rewrite as follows:
Template <typename T, size_t n>
Char (* _ countof_helper (T (& _ array) [N]) [N];

This is a function template declaration with no implementation body defined.
_ Countof_helper is a function (in red );
Its parameter is an "array reference" t (&) [N] (blue part );
The returned value is an "array Pointer" (pointer to an array) Char (*) [N] (green );

First, you need to understand the three C ++ syntaxes:

1) array reference:

T (&) [N] (Note: There is a bracket ). For example:
Int A [5] = {0 };
INT (& RA) [5] = A; here Ra is a reference to array A, and its array size is also 5;

The array information includes the type of the array element and the size of the array. When declaring "array reference", the type and size must match; otherwise, it is incorrect. For example:
Int A [5] = {0 };
INT (& RA) [4] = A; // compilation error: Error c2440: 'initializing': cannot convert from 'int [5] 'to 'int (&) [4]'

Note: The Syntax "reference array" is not found in C ++. The syntax "Int & RA [5]" cannot be compiled.

2) array pointer:

T (*) [N] (Note: There is a bracket ). For example:
Int A [5];
INT (* P) [5] = & A; // a pointer pointing to an array with five elements"

Similarly, the declared type and size must match, and the following error occurs.
Int A [4];
INT (* P) [5] = & A; // error c2440: '=': cannot convert from 'int (*) [4] 'to' int (*) [5]'

As mentioned above, C ++ does not support "reference array", but "pointer array" is absolutely supported and is often used. For example:
Char * Names [] = {
"Mike ",
"John ",
"Tom "};
Names is a pointer array with three elements. Each element is a char pointer pointing to a string.
From the example above, we can see that the data size in the memory area pointed by the pointer in the pointer array can be different.
Because the pointer information only knows the Data Type of the memory and the size information. For example:
Int A [4]; int B [5];
Int * p = 0;
P = A; // OK
P = B; // OK

3) The function returns the Declaration Syntax of "array Pointer:

T (* Fun (param_list) [N];
Fun is a function whose return value type is T (*) [N];

Why can't I declare it as follows?
T (*) [N] Fun (param_list); How nice to understand this declaration. But this is not the case.
The C ++ syntax is complex. The language designer has special requirements on the locations where (), [], *, &, & and these symbols are placed.
Chapter 3rd of <thinking in C ++> contains "aside: complicated declarations & definitions", which provides examples of complex declaration syntax.

Second, you need to know that sizeof is completed during the compilation period.
Sizeof is the keyword in C/C ++, not a function. The expressions in its parameters are not executed during the execution period, but the final type information of the entire expression is estimated during the compilation period.
For example: int A = 1; sizeof (A ++); cout <A; // A or 1. It won't be 2;
Because the compilation is completed, the compiler only needs the type information and does not need the function implementation body. Therefore, you do not need to provide a definition body.
The following example may better illustrate this problem.
Int fun (); // only declare it without definition
Sizeof (fun (); // here, the fun () function will not be executed at runtime. What the compiler needs to know when compiling is: Fun () what is the type of the function return value.

Third, we need to understand "template deduction )"
Template derivation is completed by the compiler during the compilation period (compile time), rather than during the execution period (Run time.

This is one of the key points of understanding. Memory layout allocation is not involved. During compilation, the compiler only cares about the declaration information (that is, the type information included in the Declaration ). Template derivation automatically derives various information about template parameters (types, passed values, etc ). The following is an example:
Template <int X, int y>
Struct sum _
{
Enum {value = x + y };
};
Int sum = sum _ <3, 4 >:: value; // The value of sum has been determined during compilation, not by the CPU operation during execution.
The template in this example only involves the transfer of values, and does not contain types. This is rarely seen. However, templates are supported.

Example 2:
Template <typename T, size_t n>
Void fun (T (&) [N]);

In this template, there are both types of T and numbers of n. The template is derived based on the real parameters of fun. For example:
Int A [5];
Fun (a); // the compiler will know After derivation, t = int, n = 5. Note that N can get 5 because it is determined by the parameter declaration of fun. Here, the fun parameter is: array reference

. For "array reference", as mentioned above, the compiler can know the array type from the real parameters and the array size.

At the same time, the compiler will deduce all the conditions that the "real parameters" and "form Parameters" can match during template derivation, and cannot match after all attempts, the compilation failure information is reported. For example, in this example, we pass a pointer as a real parameter to it:
Int * P = NULL;
Fun (p); // error c2784: 'void fun (T (&) [N]) ': cocould not deduce template argument for't (&) [N] 'From 'int *'
Because a pointer cannot be assigned to an array for reference. Therefore, an error occurs during compilation.

Therefore, for the _ countof () in stdlib. H, if a pointer is passed to it, the compilation will fail.

At the same time, it should be noted that vc6 does not support templates very well and some complicated template syntaxes cannot be compiled. According to my tests, the _ countof () mentioned here cannot be compiled in vc6. Vc2005/2008 is already supported. Therefore, this _ countof () is provided in vc2005. The _ countof () macro is not found in stdlib. h of vc6.

[Digression: templates are the basis of generic programming. With the ability of automatic template derivation, you can build a very skillful code implementation. For example, boost library. What's more: the Loki library <modern c ++ design> (Chinese name: C ++ new design thinking) is included in this book. For more information about the template syntax and automatic derivation, see <C ++ templates

Complete Guide>, Chinese translation compilation is not good. The "C ++ templates overview" Translated by Hou Jie is good. There are electronic pdf files available on the Internet, but only

It can be viewed on a computer, and cannot be printed or copied .]

Fourth: Considerations
Template <typename _ countoftype, size_t _ sizeofarray>
Char (* _ countof_helper (unaligned _ countoftype (& _ array) [_ sizeofarray]) [_ sizeofarray];
# DEFINE _ countof (_ array) sizeof (* _ countof_helper (_ array ))

A) # The sizeof () parameter in define is "* _ countof_helper (_ array)" (Note: There is a *), rather than "_ countof_helper (_ array) ",
If it is "_ countof_helper (_ array)", the value returned by _ countof () is always the size of a pointer (4 in 32-bit systems ).
Because _ countof_helper returns an "array Pointer", plus *, it gets the array pointed to by the pointer.

Sizeof (a pointer) and sizeof (an array) are very different. See the following example:

Int A [5];
Int * P =;
INT (* pA) [5] = &;

// Both P and PA are pointers, but their types are different: P is int *; PA is int (*) [5].

Sizeof (p) // = 4;
Sizeof (a) // = 20 = 5 * sizeof (INT)
Sizeof (* P) // = sizeof (INT) = 4;
Sizeof (PA) // = 4;
Sizeof (* pA) // = sizeof (A) = 20;

C/C ++ clearly distinguishes array and pointer types. The array information includes (Type + size), while the pointer has no size information.
Here, the array size information is lost after the array is passed through function parameters. For example:
Void fun (int A [5])
{
Int n = sizeof (a); // n is 4, relative to sizeof (int *). Because the compiler only transmits the address of the array, the size information is gone.
}

Therefore, the specific number of the array in the parameter declaration of fun is ignored. The equivalent statement can be:
Void fun (int A [10]);
Void fun (int A []);
Void fun (int * );

In order to pass the size information to the function, an additional parameter is required to indicate the size information. The size information needs to be provided by the caller, and a caller can ensure the correctness of the size information.
Void fun (int A [], int size );
Or use the Vector Template Class in STL to replace void fun (vector <int> & V); Because vector is a class, it has the size () method to obtain the size information.

B) The type of the _ countof_helper function is Char rather than _ countoftype. Because sizeof (* (char (*) [N]) [Note: this is only for demonstration purposes,

The Code cannot be written like this, but the compiled code is equal to N, because sizeof (char) is equal to 1 on any operating system;

C) This template function only has a Declaration and is not defined (Definition ). The reason for no definition body is that sizeof is completed during the compilation period and does not need to be specific.

(As mentioned in the second point above ). At the same time, template derivation is completed during the compilation period. So the _ countof () macro is completed in the compilation phase. Letter not required

The specific implementation body of the number.
If the implementation body is defined, _ countof can still work, but those implementation bodies will not be used, and these implementations will be loaded to the memory after the program runs.

. In this way, memory is wasted.
Because _ countof_helper () does not have a defined body, you can only use the _ countof macro to call _ countof_helper () as follows ().
Int A [5];
_ Countof_helper (a); // It can be compiled. However, when linking, the linker will tell you that the implementation body cannot be found.

D) there is a similar macro in the source code of Google Chrome:
Template <typename T, size_t n>
Char (& arraysizehelper (T (& array) [N]) [N];
# Define arraysize (array) (sizeof (arraysizehelper (array )))
The difference with _ countof is:
1) The arraysizehelper function returns char (&) [N] instead of char (*) [N];
2) # There is no * In define. Because sizeof (char (&) [N]) = N;

Why does the _ countof macro in stdlib. h need to be used instead *?
In winnt. h, there is also a macro rtl_number_of_v2 with the same function [Note: vc6 is not available, which is provided by vc2005 ].

A comment is as follows:
"We cocould return a reference instead of a pointer but older compilers do not accept that .":)
That's it.

Notes in winnt. h are well described and worth looking. The excerpt is as follows:
//
// Rtlpnumberof is a function that takes a reference to an array of n ts.
//
// Typedef t array_of_t [N];
// Typedef array_of_t & reference_to_array_of_t;
//
// Rtlpnumberof returns a pointer to an array of N chars.
// We coshould return a reference instead of a pointer but older compilers do not accept that.
//
// Typedef char array_of_char [N];
// Typedef array_of_char * pointer_to_array_of_char;
//
// Sizeof (array_of_char) = N
// Sizeof (* pointer_to_array_of_char) = N
//
// Pointer_to_array_of_char rtlpnumberof (reference_to_array_of_t );
//
// We never even call rtlpnumberof, we just take the size of dereferencing its return type.
// We do not even implement rtlpnumberof, we just decare it.
//
// Attempts to pass pointers instead of arrays to this macro result in compile time errors.
// That is the point.
//
Extern "C ++" // templates cannot be declared to have 'C' Linkage
Template <typename T, size_t n>
Char (* rtlpnumberof (unaligned T (&) [N]) [N];

# Define rtl_number_of_v2 (A) (sizeof (* rtlpnumberof ()))

P.s. the following link is worth noting. The explanation is quite good.
Http://blogs.msdn.com/the1/archive/2004/05/07/128242.aspx

 

Diagram: Some syntaxes

 

 
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/yuanmanzheng/archive/2010/04/12/5472967.aspx

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.