How to interpret complex C/C++ declarations

1:40:00 PM 0 Comments

ntroduction

Ever came across a declaration like int * (* (*fp1) (int) ) [10]; or something similar that you couldn't fathom? This article will teach you to interpret C/C++ declarations, starting from mundane ones (please bear with me here) and moving on to very complex ones. We shall see examples of declarations that we come across in everyday life, then move on to the troublesome const modifier and typedef, conquer function pointers, and finally see the right-left rule, which will allow you to interpret any C/C++ declaration accurately. I would like to emphasize that it is not considered good practice to write messy code like this; I'm merely teaching you how to understand such declarations. Note: This article is best viewed with a minimum resolution of 1024x768, in order to ensure the comments don't run off into the next line.

The basics

Let me start with a very simple example. Consider the declaration:
int n;
This should be interpreted as "declare n as an int".
Coming to the declaration of a pointer variable, it would be declared as something like:
int *p;
This is to be interpreted as "declare p as an int * i.e., as a pointer to an int". I'll need to make a small note here - it is always better to write a pointer (or reference) declaration with the * (or &) preceding the variable rather than following the base type. This is to ensure there are no slip-ups when making declarations like:
int* p,q;
At first sight, it looks like p and q have been declared to be of type int *, but actually, it is only p that is a pointer, qis a simple int.
We can have a pointer to a pointer, which can be declared as:
char **argv;
In principle, there is no limit to this, which means you can have a pointer to a pointer to a pointer to a pointer to afloat, and so on.
Consider the declarations:
int RollNum[30][4];
int (*p)[4]=RollNum;
int *q[5];
Here, p is declared as a pointer to an array of 4 ints, while q is declared as an array of 5 pointers to integers.
We can have a mixed bag of *s and &s in a single declaration, as explained below:
int **p1;  //  p1 is a pointer   to a pointer   to an int.
int *&p2;  //  p2 is a reference to a pointer   to an int.
int &*p3;  //  ERROR: Pointer    to a reference is illegal.
int &&p4;  //  ERROR: Reference  to a reference is illegal.

The const modifier

The const keyword is used when you want to prevent a variable (oops, that's an oxymoron) from being modified. When you declare a const variable, you need to initialize it, because you can't give it a value at any other time.
const int n=5;
int const m=10;
The two variables n and m above are both of the same type - constant integers. This is because the C++ standard states that the const keyword can be placed before the type or the variable name. Personally, I prefer using the former style, since it makes the const modifier stand out more clearly.
const is a bit more confusing when it comes to dealing with pointers. For instance, consider the two variables p and qin the declaration below:
const int *p;
int const *q;
Which of them is a pointer to a const int, and which is a const pointer to an int? Actually, they're both pointers to const ints. A const pointer to an int would be declared as:
int * const r= &n; // n has been declared as an int
Here, p and q are pointers to a const int, which means that you can't change the value of *pr is a const pointer, which means that once declared as above, an assignment like r=&m; would be illegal (where m is another int) but the value of *r can be changed.
To combine these two declarations to declare a const pointer to a const int, you would have to declare it as:
const int * const p=&n // n has been declared as const int
The following declarations should clear up any doubts over how const is to be interpreted. Please note that some of the declarations will NOT compile as such unless they are assigned values during declaration itself. I have omitted them for clarity, and besides, adding that will require another two lines of code for each example.
char ** p1;                    //        pointer to       pointer to       char
const char **p2;               //        pointer to       pointer to const char
char * const * p3;             //        pointer to const pointer to       char
const char * const * p4;       //        pointer to const pointer to const char
char ** const p5;              //  const pointer to       pointer to       char
const char ** const p6;        //  const pointer to       pointer to const char
char * const * const p7;       //  const pointer to const pointer to       char
const char * const * const p8; //  const pointer to const pointer to const char

The subtleties of typedef

typedef allows you a way to overcome the *-applies-to-variable-not-type rule. If you use a typedef like:
typedef char * PCHAR;
PCHAR p,q;
both p and q become pointers. If the typedef had not been used, q would be a char, which is counter-intuitive.
Here are a few declarations made using typedef, along with the explanation:
typedef char * a;  // a is a pointer to a char

typedef a b();     // b is a function that returns
                   // a pointer to a char

typedef b *c;      // c is a pointer to a function
                   // that returns a pointer to a char

typedef c d();     // d is a function returning
                   // a pointer to a function
                   // that returns a pointer to a char

typedef d *e;      // e is a pointer to a function 
                   // returning  a pointer to a 
                   // function that returns a 
                   // pointer to a char

e var[10];         // var is an array of 10 pointers to 
                   // functions returning pointers to 
                   // functions returning pointers to chars.
typedefs are usually used with structure declarations as shown below. The following structure declaration allows you to omit the struct keyword when you create structure variables even in C, as is normally done in C++.
typedef struct tagPOINT
{
    int x;
    int y;
}POINT;

POINT p; /* Valid C code */

Function pointers

Function pointers are probably the greatest source of confusion when it comes to interpreting declarations. Function pointers were used in the old DOS days for writing TSRs; in the Win32 world and X-Windows, they are used in callback functions. There are lots of other places where function pointers are used: virtual function tables, some templates in STL, and Win NT/2K/XP system services. Let's see a simple example of a function pointer:
int (*p)(char);
This declares p as a pointer to a function that takes a char argument and returns an int.
A pointer to a function that takes two floats and returns a pointer to a pointer to a char would be declared as:
char ** (*p)(float, float);
How about an array of 5 pointers to functions that receive two const pointers to chars and return a void pointer?
void * (*a[5])(char * const, char * const);

The right-left rule [Important]

This is a simple rule that allows you to interpret any declaration. It runs as follows:
Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.

One small change to the right-left rule: When you start reading the declaration for the first time, you have to start from the identifier, and not the innermost parentheses.
Take the example given in the introduction:
int * (* (*fp1) (int) ) [10];
This can be interpreted as follows:
  1. Start from the variable name -------------------------- fp1
  2. Nothing to right but ) so go left to find * -------------- is a pointer
  3. Jump out of parentheses and encounter (int) --------- to a function that takes an int as argument
  4. Go left, find * ---------------------------------------- and returns a pointer
  5. Jump put of parentheses, go right and hit [10] -------- to an array of 10
  6. Go left find * ----------------------------------------- pointers to
  7. Go left again, find int -------------------------------- ints.
Here's another example:
int *( *( *arr[5])())();
  1. Start from the variable name --------------------- arr
  2. Go right, find array subscript --------------------- is an array of 5
  3. Go left, find * ----------------------------------- pointers
  4. Jump out of parentheses, go right to find () ------ to functions
  5. Go left, encounter * ----------------------------- that return pointers
  6. Jump out, go right, find () ----------------------- to functions
  7. Go left, find * ----------------------------------- that return pointers
  8. Continue left, find int ----------------------------- to ints.

Further examples

The following examples should make it clear:
float ( * ( *b()) [] )();              // b is a function that returns a 
                                       // pointer to an array of pointers
                                       // to functions returning floats.

void * ( *c) ( char, int (*)());       // c is a pointer to a function that takes
                                       // two parameters:
                                       //     a char and a pointer to a
                                       //     function that takes no
                                       //     parameters and returns
                                       //     an int
                                       // and returns a pointer to void.

void ** (*d) (int &, 
  char **(*)(char *, char **));        // d is a pointer to a function that takes
                                       // two parameters:
                                       //     a reference to an int and a pointer
                                       //     to a function that takes two parameters:
                                       //        a pointer to a char and a pointer
                                       //        to a pointer to a char
                                       //     and returns a pointer to a pointer 
                                       //     to a char
                                       // and returns a pointer to a pointer to void

float ( * ( * e[10]) 
    (int &) ) [5];                    // e is an array of 10 pointers to 
                                       // functions that take a single
                                       // reference to an int as an argument 
                                       // and return pointers to
                                       // an array of 5 floats.

Suggested reading

  • A Prelude to pointers by Nitron.
  • cdecl is an excellent utility that explains variable declarations and does much more. You can download the Windows port of cdecl from here.

Credits

I got the idea for this article after reading a thread posted by Jörgen Sigvardsson about a pointer declaration that he got in a mail, which has been reproduced in the introduction. Some of the examples were taken from the book "Test your C skills" by Yashvant Kanetkar. Some examples of function pointers were given by my cousin Madhukar M Rao. The idea of adding examples with mixed *s and &s and typedef with structs was given by my cousin Rajesh Ramachandran. Chris Hills came up with modifications to the right-left rule and the way in which some examples were interpreted.

onLoad and onDOMContentLoaded

1:05:00 PM 0 Comments

  1. Load
  2. DOMContentLoaded
    1. What it awaits, in detail
  3. The alternative to DOMContentLoaded
  4. Hacks for IE<9 code="">
    1. IE<9 a="" document="" for="" frame="" hack="" inside="" not="">
    2. IE<9 a="" frame="" hack="" in="">
    3. The last resort: window.onload
  5. The crossbrowser DOMContentLoaded handling code
  6. Multiple handlers
The “document is loaded” event is a nice hook for page initialization. All elements are at place and we can build interfaces on them.
There are two main events to track it: load and DOMContentLoaded.

Load

The load event is a general “loading complete” signal. It is supported by many elements. For example, external SCRIPTand IMGIFRAME trigger it when downloading of their content finishes.
The handler window.onload and iframe.onload triggers when the page is fully loaded with all dependent resources including images and styles.
The example with IFRAME:
1<iframe src="/"></iframe>
2
To execute the code when the current page finishes loading, use window.onload.
For the bage below, the alert shows up when it is completely loaded, including the BODY and all resources:
01<html>
02<head>
03
08</head>
09<body>
10  ... page content ...
11</body>
12</html>
window.onload is rarely used, because no one wants to wait until all resources load, especially for large pictures.
Normally, we need the DOM and scripts to build interfaces. That’s exactly what DOMContentLoaded is for.

DOMContentLoaded

The DOMContentLoaded event triggers on document when the page is ready. It waits for the full HTML and scripts, and then triggers.
All browsers except IE<9 br="" it.="" support="">
document.addEventListener( "DOMContentLoaded", ready, false )
The ready function is a handler which usually performs interface initialization.
About IE hacks - we’ll get to them later.
Firefox doesn't autofill forms before `DOMContentLoaded`
For example, you have a login/password form, and the values are remembered by Firefox.
The browser will autofill them only after DOMContentLoaded. If it takes too long, the user may have to wait.

What it awaits, in detail

Generally, the DOMContentLoaded awaits only for HTML and scripts. But there are many peculiar details about that.
Not taking them into account may make DOMContentLoaded trigger later than needed, forcing the visitors to wait. Or, it may trigger earlier and skip strictly required resouces.
DOMContentLoaded doesn’t wait for any script.
There are ways to add a script to the document, so that DOMContentLoaded won’t wait for them.
You may use them smartly, to make it trigger early. Or hit that feature occasionaly, and try to initialize interfaces without a script.
DOMContentLoaded won’t wait for a script, created by document.createElement (called dynamicscript) in all browsers except Opera.
var script = document.createElement('script')
script.src = "..."
document.getElementsByTagName('head')[0].appendChild(script)
This feature is used to add ads and counters which don’t block the page from initialization. Usually ascript.async = true is added to make the script don’t wait for other scripts.
DOMContentLoaded will wait for a script:
  • In all browsers - external scripts in HTML.
  • In Opera - all scripts.
  • In Safari/Chrome - scripts with defer attribute.
And of course, DOMContentLoaded triggers after all inline scripts are executed. The browser can’t render a page without it.

The alternative to DOMContentLoaded

The most obvious way to execute JavaScript after the page load is to put it before the 
:
1<body>
2
3... bla-bla-bla my cool interface...
4
5  
8
9</body>
That’s simple. And the simplicity rules. But the drawbacks are:
  • You must put JS inside HTML. That makes integration more difficult for third-party plugins and components.
  • The BODY is not complete, so document.body.appendChild appends to current end of BODY, right after the SCRIPT.
    IE6 can’t do such body.appendChild at all.
The real document.DOMContentLoaded event ensures that the DOM is ready including BODY and everything. It can be used in JS without modifying HTML.

Hacks for IE<9 code="">

IE<9 a="" document="" for="" frame="" h3="" hack="" inside="" not="">
For IE, there is a hack which works if a window is not inside a frame.
The document is being scrolled using document.documentElement.doScroll call. The browser throws exception until the DOM is complete. So basically the scoll is called every 10 ms or so until no exception is thrown. Then the DOM ready handler is activated.
01try {
02  var isFrame = window.frameElement != null
03catch(e) {}
04
05if (document.documentElement.doScroll && !isFrame) {
06  function tryScroll(){
07    if (called) return
08    try {
09      document.documentElement.doScroll("left")
10      ready()
11    catch(e) {
12      setTimeout(tryScroll, 10)
13    }
14  }
15  tryScroll()
16}
The function tryScroll is repeatedly called until there is no exception on doScroll("left").

IE<9 a="" frame="" h3="" hack="" in="">
For a document inside a frame or iframe, the doScroll trick doesn’t work, so we use a special IE event named document.onreadystatechange:
1document.attachEvent("onreadystatechange"function(){
2  if ( document.readyState === "complete" ) {
3    ready()
4  }
5})
The event triggers many times in the process of document loading. It is very buggy and unreliable, but if it triggers with readyState=="complete", it means that the document is really complete.
Unfortunately, this also requires all resources to be loaded: images, styles etc.

The last resort: window.onload

There are non-IE browsers which do not support DOMContentLoaded, just because they are old. Still we need to support them somehow.
The window.onload is an ancient event which triggers on full page load. So we can bind to it as well.
1if (window.addEventListener)
2  window.addEventListener('load', ready, false)
3else if (window.attachEvent)
4  window.attachEvent('onload', ready)
Actually, the onreadystatechange with complete state for IE triggers just before onload.

The crossbrowser DOMContentLoaded handling code

The following code joins the methods described above into a single bindReady(handler) method.
The ready handler is bound in multiple ways, but ensures that only the first trigger will work.
01function bindReady(handler){
02
03    var called = false
04
05    function ready() {
06        if (called) return
07        called = true
08        handler()
09    }
10
11    if ( document.addEventListener ) { // native event
12        document.addEventListener( "DOMContentLoaded", ready, false )
13    else if ( document.attachEvent ) {  // IE
14
15        try {
16            var isFrame = window.frameElement != null
17        catch(e) {}
18
19        // IE, the document is not inside a frame
20        if ( document.documentElement.doScroll && !isFrame ) {
21            function tryScroll(){
22                if (called) return
23                try {
24                    document.documentElement.doScroll("left")
25                    ready()
26                catch(e) {
27                    setTimeout(tryScroll, 10)
28                }
29            }
30            tryScroll()
31        }
32
33        // IE, the document is inside a frame
34        document.attachEvent("onreadystatechange"function(){
35            if ( document.readyState === "complete" ) {
36                ready()
37            }
38        })
39    }
40
41    // Old browsers
42    if (window.addEventListener)
43        window.addEventListener('load', ready, false)
44    else if (window.attachEvent)
45        window.attachEvent('onload', ready)
46    else {
47        var fn = window.onload // very old browser, copy old onload
48        window.onload = function() { // replace by new onload and call the old one
49            fn && fn()
50            ready()
51        }
52    }
53}

Multiple handlers

The bindReady allows to hook a single handler only. To attach multiple handlers, we need an external wrapper:
01var readyList = []
02
03function onReady(handler) {
04     
05    function executeHandlers() {
06        for(var i=0; i
07            readyList[i]()
08        }
09    }
10
11    if (!readyList.length) { // set handler on first run
12        bindReady(executeHandlers)
13    }
14
15    readyList.push(handler)
16}
An example of use:
01
02<html>
03<body>
04
05<p>Large image will load after initialization</p>
06
07
08
09
10
19
20
24
25</body>
26</html>
You can view the live example and export all sources here:tutorial/browser/events/domcontentloaded/index.html.
Most modern frameworks support DOMContentLoaded using methods described above.