how do you define an array without providing a size declarator?

In short you do not.

However it can be left off for the first dimension if you provide array initialisation data:

   int dataSet[] = { 23, 34, 45, 56, 67, 78, 89, 90 };
   char    str[] = "ABCDEFG";

As the compiler can infer the size from the number of elements used to initialise the array with. This size is 8 for both examples above (remember C-style strings have an extra terminating zero character).

If you are not defining an array but just declaring one then you do not need to provide the size of the first dimension. This can occur for external global array declarations and function parameters:

   int GlobalData[100];   // Global array definition

   extern GlobalData[];   // Global array declaration

Note that global data are defined and declared outside of any function.

// Function declaration for function taking a 2D array parameter
// and a 1D array parameter:

   void WorkOnArrays( int TwoD[][10], int OneD[] );

The difference between an object declaration and its definition is that a definition allocates storage for the object, whereas a declaration just states that there exists such an object with the given name and type.

C and C++ have a one definition rule: objects, functions, class types, enumeration types and templates can be defined only once within one translation unit (roughly one source file plus all the included headers). They can be declared as many times as you like however. In addition a program must only contain one definition for entities with external linkage - those that are seen by the linker - such as non-inline functions and global variables.

Now if you do not know the size of the array at compile time then you can create it at runtime using dynamic memory allocation by using new [] to allocate the require storage and create the elements and delete [] to destroy the elements and release the storage. However you still need to provide a size, although it can be a variable. You can only dynamically allocate single dimension arrays because new [] returns a pointer to the start of the array and the compiler has no way of knowing details of additional dimensionality:

   size_t array_size(0);
   std::cout << "How many elements?  ";
   std::cin >> array_size;

   // Check array_size has a sensible value...
   int * array = new int[array_size];

   // Use array...

   delete [] array;

What all this amounts to is that the quantity of memory used by your program from the various storage areas - stack based storage, static (global) storage, and dynamic storage - has to be carefully managed. This means that the size required for each object is known either at compile time (i.e. the compiler works it out for you, but you have to provide enough information to allow it to do so) or by requests from the dynamic storage area at runtime.

The array you define is almost certainly not the only item that needs to be allocated in the given storage area. For non-dynamically allocated storage the compiler and linker will produce memory layouts such that everything fits together in a harmonious way. They can only do this if they know the sizes of the items involved.

For dynamic memory requests at runtime you have to ask for a specific amount of memory for each request. This allows the dynamic storage manager to keep track of storage usage and availability.

The amount of storage required for items is calculated from the size of the object or objects being allocated and the number of objects being allocated. C++ has a static type system and so knows the size of all objects from their type. For user defined types this implies that array members have to have their size defined so the overall size of the class or struct can be calculated by the compiler.

Finally I should explain why the compiler needs sizes for all dimensions except the first.

Memory is arranged like a single dimension array of words, which are typically 8-bit bytes on modern computers, and would map to an array of char in C++. To give the impression of additional dimensions we can arrange the memory in a particular way that places adjacent cells in all but one dimension so they are not adjacent in memory. We then use some arithmetic to calculate how to move in these dimensions.

Let us take as an example a 2D table of char on a machine where memory is addressed in the same units (e.g. char is an 8-bit byte, and the machine is 8-bit byte addressed). This will mean that the size of the elements in the table is 1 and this equates to a single address slot in memory:

   char table[3][4]; // 3 rows of 4 columns

This will require 12 char elements arranged like so (best viewed with a mono-spaced front such as Courier):

        0      1      2      3
   0 [0][0] [0][1] [0][2] [0][3]
   1 [1][0] [1][1] [1][2] [1][3]
   2 [2][0] [2][1] [2][2] [2][3]

However in memory C/C++ will lay out the table array like so:

   Memory Address  Array Element
     table + 00:      [0][0]
     table + 01:      [0][1]
     table + 02:      [0][2]
     table + 03:      [0][3]
     table + 04:      [1][0]
     table + 05:      [1][1]
     table + 06:      [1][2]
     table + 07:      [1][3]
     table + 08:      [2][0]
     table + 09:      [2][1]
     table + 10:      [2][2]
     table + 11:      [2][3]

Remember that in C and C++ the name of an array converts to a pointer to (i.e. address of) the first element.

As you can see the columns for a particular row proceed though memory contiguously but the rows do not. In fact the arrangement is rows of 4 columns placed one after another. To locate a column for a given row we just add its index to the start address of the column. To locate the start of row address however we need to multiply the row number by the number of columns in the table, hence row 0 starts at offset 0*4 = 0, row 1 at offset 1*4 = 4, and row 2 at offset 2*4 = 8. So the complete formula to calculate the offset from the start of table for element table[r][c] is:

   (4*r) + c.

Generalising this for any size of table of char gives a formula:

   (Number_of_columns * row_index) + column_index

You will notice that the number of columns value is required to calculate the 'stride' value for rows. However the number of rows value, which just happens to be the first dimension size given, is not needed.

Should the elements be of a size other then 1 then all that is required is that the offset value from the formula be multiplied by the size of the elements.

In all cases the offset value is added to the address of the start of the array to yield a memory address for the element.

This principle can be extended to additional dimensions, which I shall leave to you to ponder if you are interested.

Hope this has been of use.  


All Answers

Answers by Expert:

Ask Experts


Ralph McArdell


I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.


My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.


©2016 About.com. All rights reserved.