36

Is there such a thing as a jagged array in C or C++?

When I compile this:

int jagged[][] = { {0,1}, {1,2,3} };

I get this error:

error: declaration of `jagged' as multidimensional array must have bounds for all dimensions except the first

Sam
  • 7,252
  • 16
  • 46
  • 65
unknown
  • 1,313
  • 3
  • 12
  • 7

12 Answers12

29

In C I would use an array of pointers.

For instance:

int *jagged[5];

jagged[0] = malloc(sizeof(int) * 10);
jagged[1] = malloc(sizeof(int) * 3);

etc etc.

Cromulent
  • 3,788
  • 4
  • 31
  • 41
18

There's a bunch of ways to do it. Here's another way:

int jagged_row0[] = {0,1};
int jagged_row1[] = {1,2,3};
int *jagged[] = { jagged_row0, jagged_row1 };
rampion
  • 87,131
  • 49
  • 199
  • 315
  • 6
    +1. This is where C99's compound literals show off: `int *jagged[] = { (int[]){0,1}, (int[]){1, 2, 3} };` isn't that nice too? – Johannes Schaub - litb Jul 05 '09 at 09:08
  • 8
    The trouble with this solution is that the sub-arrays immediately decay to pointers, so you have no means of telling what the bounds are. –  Jul 05 '09 at 09:36
  • @Neil, i didn't think of this at all. Of course you are right. Good point :) – Johannes Schaub - litb Jul 05 '09 at 09:41
  • 1
    @Neil: that's where guardian values come in handy to mark the end of the array – Christoph Jul 05 '09 at 10:06
  • 3
    @Christoph Guardian values can be problematic for arrays of numbers, as there may be no allowable "special" value. I think storing the array dimension explicitly somehow is better practice. –  Jul 05 '09 at 10:15
  • @Neil Agreed on the subject of guardian values. Integers are overloaded enough as it is. Since I can't rely on the sizeof(array)/sizeof(*array) trick when passing arrays to functions, I always put array sizes in a size_t or something. – rampion Jul 05 '09 at 12:12
16

If you just want to initialise it, you can say:

int jagged[][3] = { {0,1}, {1,2,3} };

but the array will still have the shape [2][3]. If you want a true jagged array, you will have to create it dynamically. And if you do that, and are using C++, you should use a std::vector, as friol suggests.

Sam
  • 7,252
  • 16
  • 46
  • 65
13

In C++ (not compiled, and probably there's a more compact syntax):

std::vector<std::vector<int> > myArray;

myArray.push_back(std::vector<int>());
myArray.push_back(std::vector<int>());

myArray[0].push_back(0);
myArray[0].push_back(1);

myArray[1].push_back(1);
myArray[1].push_back(2);
myArray[1].push_back(3);

So now you can access the elements with, for example, myArray[0][0], etc.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
friol
  • 6,996
  • 4
  • 44
  • 81
4

In C99 you can do the following:

int jagged_row0[] = {0,1};
int jagged_row1[] = {1,2,3};

int (*jagged[])[] = { &jagged_row0, &jagged_row1 }; // note the ampersand

// also since compound literals are lvalues ...
int (*jagged2[])[] = { &(int[]){0,1}, &(int[]){1,2,3} };  

The only difference here (as compared to rampion's answer) is that the arrays don't decay to pointers and one has to access the individual arrays via another level of indirection - (e.g. *jagged[0] - and the size of each row has to be recorded - i.e. sizeof(*jagged[0]) will not compile) - but they're jagged-appearing to the bone ;)

Faisal Vali
  • 32,723
  • 8
  • 42
  • 45
  • I thought you can't make arrays of incomplete type... oh, you're making an array of pointers to an incomplete type, that's possible but doesn't buy you anything over rampion's answer. – Ben Voigt Jul 20 '12 at 19:38
4

The reason you got the error is that you must specify the bounds for at least the outer dimension; i.e.

int jagged[][3] = {{0,1},{1,2,3}};

You cannot have jagged[0] be a 2-element array of int and jagged[1] be a 3-element array of int; an N-element array is a different type from an M-element array (where N != M), and all elements of an array must be the same type.

What you can do is what the others have suggested above and create jagged as an array of pointers to int; that way each element can point to integer arrays of different sizes:

int row0[] = {0,1};
int row1[] = {1,2,3};
int *jagged[] = {row0, row1};

Even though row0 and row1 are different types (2-element vs. 3-element arrays of int), in the context of the initializer they are both implicitly converted to the same type (int *).

John Bode
  • 119,563
  • 19
  • 122
  • 198
4

With C++11 initializer lists this can be written more compactly:

#include <vector>
#include <iostream>

int main() {
    // declare and initialize array
    std::vector<std::vector<int>> arr = {{1,2,3}, {4,5}};
    // print content of array
    for (auto row : arr) {
        for (auto col : row)
            std::cout << col << " ";
        std::cout << "\n";
    }
}

The output is:

$ g++ test.cc -std=c++11 && ./a.out
1 2 3 
4 5 

For reference:

Community
  • 1
  • 1
moooeeeep
  • 31,622
  • 22
  • 98
  • 187
3

You can also use the compound literals in c to initialize a truly jagged array which is contiguous in memory as follows:

int (*arr[]) = { (int []) {0, 1}, (int []){ 2, 3, 4}, (int []){5, 6, 7, 8} }

This will be laid out contiguously in memory.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • Just out of curiosity: do you know which C standard is required for that to work? – Cherusker Jan 21 '19 at 21:03
  • 1
    @Cherusker, C99 does support this. More details are available [here](https://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Compound-Literals.html). – robespierre Jan 21 '19 at 21:26
1
//
//jaggedArrays.cpp
//
//program to implement jagged arrays in c++
//
#include<iostream>
#include<iomanip>
using namespace std;

int main()
{
    int rows, i, j;
    cout << endl << "Enter no of rows : ";
    cin >> rows;

    int columnsSizeOfEachRow[rows];

    cout << endl;
    for( i = 0 ; i < rows ; i++ )
    {
        cout << "Enter column size for row no " << i + 1 << " : ";
        cin >> columnsSizeOfEachRow[i];
    }
    
    int *jaggedArray[rows];
    for (i = 0 ; i < rows ; i++)
        jaggedArray[i] = new int[columnsSizeOfEachRow[i]];
    
    cout << endl;
    for(i = 0 ; i < rows ; i++)
    {
        for ( j = 0 ; j < columnsSizeOfEachRow[i] ;j++)
        {
            cout << "Array[" << i + 1 << "][" << j + 1 << "] << ";
            cin >> jaggedArray[i][j];
        }
        cout << endl;
    }

    cout << endl << endl << "Jagged array is as follows : " << endl;
    for( i = 0 ; i < rows ; i++)
    {
        for ( j = 0 ; j < columnsSizeOfEachRow[i] ;j++)
            cout << setw(3) <<jaggedArray[i][j] << " ";
        cout << endl;
    }    

    return 0;
}
Steve Summit
  • 45,437
  • 7
  • 70
  • 103
Wasit Shafi
  • 854
  • 1
  • 9
  • 15
1

By using dynamic allocation in cpp we can create jagged arrays.

For example:

#include<iostream>
using namespace std;
    
int main(){
    int n;
    cout<<"Enter n:";
    cin>>n;
    
    cout<<"Enter elements:"; 
    int **p = new int *[n];
    
    for(int i=0;i<n;i++){
        p[i] = new int[i+1];
        for(int j=0;j<(i+1);j++){
            cin>>p[i][j];
        }
    } 
    
    cout<<"Jagged Array:"<<endl; 
    for(int i=0;i<n;i++){
        for(int j=0;j<(i+1);j++){
            cout<<p[i][j]<<" ";           
        }
        cout<<endl;
    }
    
    for(int i=0;i<n;i++){
        delete []p[i];
    }
    delete []p;
}
    

For n=3, we have created a jagged array in the following look:

  • Enter n: 3

Enter elements:
1
1 2
1 2 3
Jagged Array:
1
1 2
1 2 3

xKobalt
  • 1,498
  • 2
  • 13
  • 19
0

The jagged arrays do exist in c++/c but the syntax is quite complex and you have to handle many things.

There are two types of jagged arrays in c++.
1) STATIC JAGGED ARRAY(A 2d array in which the size will be a constant number and there will be different number of columns in each row).
2) DYNAMIC JAGGED ARRAY(A 2d array in which the size will be any number taken from user and there will be different number of columns in each row)


1)STEPS OF IMPLEMENTING STATIC JAGGED ARRAY

  • Using array and a pointer
    • 1) Declare 1-D arrays with the number of rows you will need
    • 2) The size of each array(array for the elements in the row) will be the number of columns (or elements) in the row
    • 3) Declare a 1-D array of pointers that will hold the addresses of the arrows
    • 4) The size of the 1-D array is the number of rows you want in the jagged array


    #include<iostream>
    #include<string>
    using namespace std;
    int main()
    {
        int row0[4] = { 1,2,3,4 };
        int row1[2] = { 5,6 };
        int* jagged[2] = { row0,row1 };
        int Size[2] = { 4,2 }, k = 0;
        for (int i = 0; i < 2; i++)
        {
            int* ptr = jagged[i];
            for (int j = 0; j < Size[k]; j++)
            {
                cout << *ptr << "";
                ptr++;
            }
            cout << endl;
            k++;
            jagged[i]++;
        }
        return 0;
    }
    

    The output is as follows


    1234
    56



    1)STEPS OF IMPLEMENTING DYNAMIC JAGGED ARRAY

  • Using an array of pointer
    • 1) Declare an array of pointers(jagged array)
    • 2) The size of this array will be the number of rows required in the jagged array
    • 3) For each pointer in the array allocate memory for the number of elements you want in this row.


    #include<iostream>
    #include<string>
    using namespace std;
    int main()
    {
        //2 rows
        int* jagged[2];
    
        //Allocate memeory for the elements in the row 0
        jagged[0] = new int[1];
    
        //Allocate memory for the elements in row 1
        jagged[1] = new int[5];
    
        //Array to hold the size of each row
        int Size[2] = { 1,5 },k = 0, number = 100;
    
        //User enters the numbers
        for (int i = 0; i < 2; i++)
        {
            int* p = jagged[i];
    
            for (int j = 0; j < Size[k]; j++)
            {
                *p = number++;
    
                //move the pointer
                p++;
            }
            k++;
        }
        k = 0;
    
        //Display elements in Jagged array
        for (int i = 0; i < 2; i++)
        {
            int* q = jagged[i];
            for (int j = 0; j < Size[k]; j++)
            {
                cout << *q << "";
                //move the pointer to the next element
                q++;
            }
            cout << endl;
            k++;
            //move the pointer to the next row
            jagged[i]++;
        }
        delete[] jagged[0];
        delete[] jagged[1];
        return 0;
    }
    

    The output is as follows

    100
    101102103104105


    bilalmohib
    • 280
    • 3
    • 16
    • I'm pretty sure this should use `delete[] jagged[0];` and `delete[] jagged[1];`, not `delete[] jagged;`. – Ruzihm Mar 18 '21 at 21:23
    • @Ruzihm But you know when we delete a pointer so all the indexes are automatically deleted so we want to delete them all instead of just deleting only one index we want to free the memory.Hope you understand.Thanks. – bilalmohib Mar 21 '21 at 03:24
    • 1
      Running the code in this answer with valgrind says **definitely lost: 24 bytes in 2 blocks** both at `operator new[](unsigned long)`. But, removing `jagged[i]++;` and replacing `delete[] jagged;` with `delete[] jagged[0]; delete[] jagged[1];` reports **no leaks are possible**. – Ruzihm Mar 21 '21 at 03:49
    • 1
      Also even compiling this code in g++ gives a warning: `foo.cpp:49:14: warning: deleting array ‘jagged’ 49 | delete[] jagged;` – Ruzihm Mar 21 '21 at 05:39
    • @Ruzihm Thank you Sir.Editing according to your suggestion. – bilalmohib Mar 21 '21 at 15:32
    • this still says **definitely lost: 24 bytes in 2 blocks both at operator new[](unsigned long)**. You still have to remove `jagged[i]++;`, because `delete[]` needs the addresses returned by `new[]`, and `jagged[i]++` changes them. – Ruzihm Mar 21 '21 at 19:35
    • @Ruzihm but changing jagged[i]++; will result in a result which we dont need so we cant work with that. – bilalmohib Mar 22 '21 at 15:34
    • Please explain what you mean by that. Between `jagged[i]++;` and `delete[] jagged[0]; delete[] jagged[1];`, the value of `jagged[i];` isn't read ever again. So, removing it won't change anything about what is printed (you can try this for yourself, if you don't believe me), and removing it will stop a memory leak. – Ruzihm Mar 22 '21 at 15:43
    0

    No, there are no jagged multidimensional arrays in C nor C++. You can create various constructs that perform similar function at some memory cost (like array of pointers to arrays), but not an actual C-style multidimensional array.

    The reason is that C-style arrays, no matter how many dimensions, occupy contiguous memory area with no metadata. So, memory-wise, they're all single-dimensional. It's only the cleverness of pointer arithmetic (striding the pointer by the size of a row) that gives you the functionality of extra dimensions. A jagged array laid out serially has different row sizes, so it cannot be strode by a constant value, so it requires additional storage depending on data size, thus is impossible to express in C type system.

    It becomes clearer when you consider to what pointer multidimensional array decay to: Array to pointer decay and passing multidimensional arrays to functions

    And that's why you see the error message must have bounds for all dimensions except the first, because all dimensions except the first are necessary to stride the array.

    Agent_L
    • 4,960
    • 28
    • 30