Существуют ли зазубренные массивы в C/C++?

Есть ли такая вещь, как зубчатый массив в C или C++?

Когда я собираю это:

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

Я получаю эту ошибку:

ошибка: объявление jagged как многомерного массива должно иметь границы для всех измерений, кроме первого

12 ответов

В C I будет использовать массив указателей.

Например:

int *jagged[5];

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

и т. д.

Есть множество способов сделать это. Вот еще один способ:

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

Если вы просто хотите инициализировать его, вы можете сказать:

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

но массив все равно будет иметь форму [2][3]. Если вам нужен истинный зубчатый массив, вам придется создавать его динамически. И если вы делаете это и используете C++, вы должны использовать std::vectorкак говорит фриол.

В C++ (не скомпилировано, и, вероятно, есть более компактный синтаксис):

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);

Так что теперь вы можете получить доступ к элементам, например, myArray[0][0] и т. Д.

С помощью списков инициализаторов C++11 это можно записать более компактно:

#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";
    }
}

Выход:

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

Для справки:

Причина, по которой вы получили ошибку, заключается в том, что вы должны указать границы хотя бы для внешнего измерения; т.е.

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

Вы не можете jagged[0] быть массивом из 2 элементов типа int, а jagged[1] быть массивом из 3 элементов типа int; массив N-элементов отличается от массива M-элементов (где N!= M), и все элементы массива должны быть одного типа.

То, что вы можете сделать, - это то, что другие предложили выше, и создать неровные массивы указателей на int; таким образом, каждый элемент может указывать на целочисленные массивы разных размеров:

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

Несмотря на то, что row0 и row1 являются разными типами (2-элементные или 3-элементные массивы int), в контексте инициализатора они оба неявно преобразуются в один и тот же тип (int *).

В C99 вы можете сделать следующее:

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} };  

Единственное отличие здесь (по сравнению с ответом rampion) состоит в том, что массивы не распадаются на указатели, и нужно обращаться к отдельным массивам через другой уровень косвенности - (например, *jagged[0] - и размер каждой строки должен быть записан - т.е. sizeof(*jagged[0]) не будет компилироваться) - но они зазубренные до костей;)

Вы также можете использовать составные литералы в c для инициализации действительно зубчатого массива, который непрерывен в памяти следующим образом:

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

Это будет выложено непрерывно в памяти.

Используя динамическое размещение в cpp, мы можем создавать неровные массивы.

Например:

#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;
}
    

За n=3, мы создали зубчатый массив следующего вида:

  • Введите n: 3

Введите элементы:
1
1 2
1 2 3
Неровный массив:
1
1 2
1 2 3

//
//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;
}

Нет, зубчатых многомерных массивов нет ни в C, ни в C++. Вы можете создавать различные конструкции, которые выполняют аналогичные функции с некоторой затратой памяти (например, массив указателей на массивы ), но не настоящий многомерный массив в стиле C.

Причина в том, что массивы в стиле C, независимо от количества измерений, занимают непрерывную область памяти без метаданных. Итак, с точки зрения памяти они все одномерны. Только хитрость арифметики указателя ( перемещение указателя по размеру строки) дает вам функциональность дополнительных измерений. Зубчатый массив, выложенный последовательно, имеет разные размеры строк, поэтому его нельзя обвести постоянным значением, поэтому он требует дополнительной памяти в зависимости от размера данных, поэтому его невозможно выразить в системе типов C.

Становится яснее, когда вы рассматриваете, к какому указателю распадается многомерный массив: Массив к распаду указателя и передача многомерного массива в функции

И поэтому вы видите сообщение об ошибкеmust have bounds for all dimensions except the first, потому что все измерения, кроме первого, необходимы для обхода массива.

Зубчатые массивы действительно существуют в C++/c, но синтаксис довольно сложен, и вам придется обрабатывать многие вещи.

В C++ есть два типа зубчатых массивов.
1) СТАТИЧЕСКИЙ Массив с зазубринами(2- мерный массив, в котором размер будет постоянным числом и будет различное количество столбцов в каждой строке).
2) ДИНАМИЧЕСКИЙ Массив с зазубринами(2d массив, в котором размер будет любым числом, взятым у пользователя, и будет различное количество столбцов в каждой строке)


1) ЭТАПЫ РЕАЛИЗАЦИИ СТАТИЧЕСКОГО МОДУЛЬНОГО МАССИВА

  • Использование массива и указателя
    • 1) Объявите одномерные массивы с нужным количеством строк
    • 2) Размер каждого массива (массива для элементов в строке) будет количеством столбцов (или элементов) в строке
    • 3) Объявите одномерный массив указателей, который будет содержать адреса стрелок.
    • 4) Размер одномерного массива - это количество строк, которое вы хотите в неровном массиве.


           #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;
    }
    

    Результат выглядит следующим образом


    1234
    56



    1) ЭТАПЫ РЕАЛИЗАЦИИ ДИНАМИЧЕСКОГО МОДУЛЬНОГО МАССИВА

  • Использование массива указателя
    • 1) Объявить массив указателей (зубчатый массив)
    • 2) Размер этого массива будет количеством строк, необходимых в неровном массиве.
    • 3) Для каждого указателя в массиве выделите память на необходимое количество элементов в этой строке.


           #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;
        return 0;
    }
    

    Результат выглядит следующим образом

    100
    101102103104105


    ПРИМЕЧАНИЕ:(Код скопирован со слайда с курсом университета)

    Другие вопросы по тегам