Arhn - архитектура программирования

Функция, возвращающая указатель на массив

Мне удалось работать с массивами переменной длины в C, и теперь у меня есть следующее:

#include <stdio.h>
#include <stdlib.h>

int (*foo(size_t row, size_t col))[3];

int main(void){
    size_t row, col;


    printf("Give the ROW: ");
    if ( scanf("%zu",&row) != 1){
        printf("Error, scanf ROW\n");
        exit(1);
    }

    printf("Give the COL: ");
    if ( scanf("%zu",&col) != 1){
        printf("Error, scanf COL\n");
        exit(2);
    }

    int (*arr)[col] = foo(row, col);

    for ( size_t i = 0; i < row; i++){
        for( size_t j = 0; j < col; j++){
            printf("%d ",*(*(arr+i)+j));
        }
    }

    free(arr);
}


int (*foo(size_t row, size_t col))[3]{
    int (*arr)[col] = malloc(row * col * sizeof(int));
    int l=0;

    if (arr == NULL){
        printf("Error, malloc\n");
        exit(3);
    }

    for ( size_t i = 0; i < row; i++){
        for( size_t j = 0; j < col; j++){
            *(*(arr+i)+j) = l;
            l++;
        }
    }

    return arr;
}

Выход:

Give the ROW: 2
Give the COL: 5
0 1 2 3 4 5 6 7 8 9

Теперь это:

int (*foo(size_t row, size_t col))[3]{ /* code */ }

означает, если я правильно понял, что объявляет foo как функцию с двумя параметрами (size_t row, size_t col), которая возвращает указатель на массив 3 из int.

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

В любом случае здесь int (*foo(size_t row, size_t col))[3] у меня есть [3], который я не понимаю, как он работает и как я могу сделать это возможным во время выполнения (конечно, только если это возможно), что-то вроде int (*foo(size_t row, size_t col))[SIZE].

Я читал несколько книг про C, но точного объяснения этой ситуации нет и гугл тоже не помогает, поэтому у меня два вопроса:

1) Возможно ли это int (*foo(size_t row, size_t col))[SIZE] , где SIZE должен быть параметром? Или я должен объявить эту функцию по-другому?

2) это правильный путь для того, что я пробовал здесь, или есть другая альтернатива?

Я только пытаюсь вернуть указатель на массив, размер которого известен во время выполнения, а не во время компиляции. Вызов malloc и free происходит только один раз, и это хороший подход, потому что всякий раз, когда вызывается malloc, наша программа мешает ядру выделить память и пометить страницу как доступную для записи. Таким образом, этот метод имеет меньше накладных расходов на kernel. Его можно написать одним malloc работающим с VLA

ИЗМЕНИТЬ:

@ChronoKitsune сказал, что я должен использовать/попробовать [] (массив неопределенного размера), и в этом случае функция станет такой:

int (*foo(size_t row, size_t col))[]{ /* code */ }

Это то, что я должен использовать?

27.05.2016

  • 1) Нет, но попробуйте [] (массив неопределенного размера). 2) Да, у вас правильный синтаксис. Наиболее эквивалентной альтернативой было бы просто вернуть обычный указатель. Затем вы должны либо объявить arr как int *arr = foo(row, col); и использовать его как arr[i * col + j] (или *(arr + i * col + j)), либо привести возвращаемое значение к объявлению arr как в int (*arr)[col] = (int (*)[col])foo(row, col);, чтобы иметь возможность использовать arr[i][j] (или *(*(arr + i) + j), поскольку вы любите использования нотации указателя, а не более читаемого эквивалента с использованием нотации массива). 27.05.2016
  • @ChronoKitsune Я пробовал это, но не был уверен, что это нормально, отсюда и вопрос. Я тоже мог набрать [0]. :) 2) Мне нужен этот синтаксис int (*arr)[col] 27.05.2016
  • @ChronoKitsune У меня уже есть это решение, но это не то, что мне нужно. 27.05.2016
  • Синтаксис [] формально обозначает массив неполного типа (C11, раздел 6.7). .6.2p4). Возврат указателя на этот массив разрешен. Однако вы не можете разыменовать этот указатель, пока указатель не будет присвоен массиву с полным типом. Например, если вы объявили int (*arr)[] = foo(row, col);, то вы вообще не могли сделать *arr или arr[...]. int (*arr)[col] = foo(row, col); разрешено, и вы можете успешно использовать указатель по своему усмотрению, поскольку int (*arr)[col] является указателем на полный тип. 27.05.2016
  • @ChronoKitsune В моем случае это означает, что все в порядке. 27.05.2016

Ответы:


1

В любом случае, здесь int (*foo(size_t row, size_t col))[3] у меня есть это [3], и я не совсем понимаю, как оно работает.

Объявления типов легче всего читаются изнутри наружу. Я начну с простого и построю по вашему примеру. Если вы пишете

int foo[3];

or

int (foo)[3];

вы объявляете foo как массив из 3 ints. Круглые скобки здесь выполняют функцию группировки, определяющую приоритет, как и в выражениях, но они не нужны, поскольку в обоих случаях тип интерпретируется одинаково.

Если вы пишете

int (*foo)[3];

вы объявляете foo указателем на массив из 3 int. Эквивалентно, вы можете прочитать это как утверждение, что вещь, на которую указывает foo, представляет собой массив из 3 int, при этом подразумевается, что foo является указателем. В этом случае скобки необходимы; без них вы бы объявили foo массивом из 3 int *.

Если вы пишете

int (*foo(size_t row, size_t col))[3];

вы объявляете, что foo — это функция, принимающая два аргумента типа size_t, возвращаемое значение которых указывает на массив из 3 ints.

и как я могу сделать это возможным во время выполнения (конечно, только если это возможно), что-то вроде int (*foo(size_t row, size_t col))[SIZE].

Если SIZE не является константой времени компиляции, вы не можете этого сделать. C2011 считает, что

Если идентификатор объявлен как имеющий переменно изменяемый тип, он [...] не должен иметь связи и иметь область действия либо блока, либо области прототипа функции. [...]

(6.7.6.2/2)

Другими словами, поскольку все функции имеют файловую область и либо внутреннюю, либо внешнюю связь, типы возвращаемых функций не могут быть VLA, указателями на VLA или любым подобным типом (это типы с переменным изменением).

Как правило, в таких случаях возвращается указатель на первый элемент массива, а не указатель на весь массив. Указываемый адрес одинаков в любом случае, но тип отличается:

int *foo(size_t row, size_t col);

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

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

Обновление:

Также можно объявить вашу функцию для возврата указателя на массив неопределенного размера, как предложил @ChronoKitsune. Синтаксис будет

int (*bar(size_t row, size_t col))[];

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

int (*array_ptr)[] = bar(x, y);

и для доступа к элементам указанного массива:

int z = (*array_ptr)[1];

Сравните это с

int *ptr = foo(x, y);
int w = ptr[2];
27.05.2016
  • Вы имеете в виду что-то вроде Это, если это не то, что мне нужно, если нет, не могли бы вы предоставить мне DEMO, где у меня есть указатель на массив в основной функции и функция, которая это делает? 27.05.2016
  • Или можно просто использовать (например, указатель @ChronoKitsune) этот [] (массив неопределенного размера) ==> int (*func(size_t row, size_t col))[]; ? 27.05.2016
  • @Michi, да, ваш пример IdeaOne - это то, что я описывал. Да, вы можете использовать [] форму, предложенную ChronoKitsune, но я думаю, что вам будет труднее работать с ней практически во всех отношениях. 27.05.2016
  • Спасибо, мне нужно было знать, законно ли это. Пожалуйста, предоставьте это вам Ответ. 27.05.2016
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..