Возврат указателя на двумерный массив [C ]

#c #arrays #vector

#c #массивы #вектор

Вопрос:

Я не знаю почему, но он не возвращает двумерный массив, который я объявляю в функции.

 char** canvas() {
    
    char canvas[18][8];
    
    fill(canvas[0], canvas[0]   18 * 8, 'O');
    
    return canvas;
    
}
  

Комментарии:

1. Совет: Используйте std::vector<int> of size 18 * 8` и эмулируйте его размерность. Альтернативой является ручное выделение памяти, которое в спешке превращается в кучу невеселых действий.

2. Примечание: char x[a][b] никоим образом не совместим с char** . Один представляет собой двумерный массив, другой — указатель на массив указателей . Ваш компилятор должен сходить с ума. Если это не так, включите больше предупреждений, таких как -Wall .

3. Какой стандарт C вы используете?

Ответ №1:

canvas является локальной переменной, ее время жизни истекает, когда функция выходит за пределы области видимости, обращение к этой переменной через указатель после этого вызовет неопределенное поведение.

Возвращаемый тип функции также несовместим с объектом, который вы пытаетесь вернуть.

Если вам необходимо использовать массивы в стиле C (выведенные из вашего комментария), вы можете сделать что-то вроде:

 char (*canvas())[8] //pointer to array return type
{    
    char (*canvas)[8] = new char[18][8]; //C   manual allocation
 
    //std::fill(canvas[0], canvas[0]   18 * 8, 'O'); //works
    std::fill_n(canvas[0], 18 * 8, 'O'); //better, as suggested by Ted Lyngmo
    
    return canvas;     
}
  

Затем вы можете вызвать его следующим образом:

 int main()
{
    char(*canv)[8] = canvas(); //assign it

    for (int i = 0; i < 18; i  ) //use it like a 2D array
    {
        for (int j = 0; j < 8; j  )
        {
            std::cout << canv[i][j];
        }
        std::cout << std::endl;
    }
    delete[] canv; //delete object after it's used
}
  

Поскольку вы используете C , я бы предложил контейнер типа STL std::vector , то есть вектор векторов std::vector<std::vector<char>> , который вы также можете использовать std::array , поскольку размеры известны во время компиляции, т. е. std::array<std::array<char, 8>, 18> .

Для реализации на C std::array :

 std::array<std::array<char, 8>, 18> canvas()
{
    std::array<std::array<char, 8>, 18> canvas;

    std::fill_n(canvas.begin()->begin(), 18 * 8, 'O');

    return canvas;
}
  
 int main()
{
    std::array<std::array<char, 8>, 18> canv = canvas();
    //...
}
  

Для std::vector реализации:

 std::vector<std::vector<char>> canvas()
{
    std::vector<std::vector<char>> canvas{18, std::vector<char>(8, '0')};

    return canvas;
}
  
 int main()
{
    std::vector<std::vector<char>> canv = canvas();
    //...
}
  

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

 for (autoamp; a : canv)
{
    for (autoamp; ch : a)
    {
        std::cout << ch;
    }
    std::cout << std::endl;
}
  

Вывод во всех случаях:

 OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
  

Комментарии:

1. std::array это не то же самое, что массив. Массивы на C используются довольно редко, std::array или std::vector почти всегда превосходят их.

2. @john, да, конечно, но в данном случае, поскольку размеры известны во время компиляции, это вариант.

3. @DiaskyVB Если границы массива известны во время компиляции, то вы могли бы использовать std::array но, вероятно, ваш текущий компилятор не будет его поддерживать.

4. @TedLyngmo, это звучит разумно, кстати, хорошо замечено [] .

5. @TedLyngmo, еще раз, хорошо замечено, я чувствую, что должен поделиться голосами с вами 🙂

Ответ №2:

Распространенное заблуждение, что поскольку char[n] преобразуется в char* , то должна быть также возможность преобразования char[n][m] в char** , но это не так.

Проблема в том, что элементы char[n] и char* одинаковы. Каждый элемент является a char , поэтому преобразование выполняется нормально.

Но с char[n][m] каждым элементом есть char[m] и с char** каждым элементом есть char* . Это не одно и то же, и преобразование недопустимо.

Простое решение заключается в использовании векторов вместо массивов.

 vector<vector<char>> canvas(){
    
    
    vector<vector<char>> canvas(18, vector<char>(8));
    
    fill(canvas[0], canvas[0]   18 * 8, 'O');
    
    return canvas;
    
    
}
  

Комментарии:

1. 11 19 [Ошибка] ‘>>’ должно быть ‘> >’ в списке аргументов вложенного шаблона

2. @DiaskyVB У вас, должно быть, очень старый компилятор или вы используете его с очень старыми опциями. Но если вы должны затем поставить пробел между >> .

3. Я использую Devc 5.11

4. Это очень старый компилятор, есть современные компиляторы C , доступные бесплатно. Вам следует обновить.

5. @TedLyngmo Да, спасибо, я скопировал код операционной системы, не посмотрев на него достаточно внимательно.

Ответ №3:

Это может быть странный синтаксис:

 char (*canvas())[8] //pointer to array return type
  

для улучшения разборчивости может быть полезно использовать псевдонимы типов:

 using canvas_ptr = char (*)[8];
  

Таким образом, вы можете написать решение в стиле c таким образом:

 canvas_ptr getCanvas()
{ 
    canvas_ptr canvas = new char[18][8]; //C   allocation
    std::cout << "size:" << std::size(canvas) << 'n';
    std::fill_n(canvas[0], 18 * 8, 'O'); //as suggested by Ted Lyngmo
    
    return canvas;
}


int main()
{
    canvas_ptr c = getCanvas();

    for (int i = 0; i < 18; i  ) //use it like a 2D array
    {
        for (int j = 0; j < 8; j  )
        {
            std::cout << c[i][j];
        }
        std::cout << std::endl;
    }
    delete[] c; //delete object after it's used
}