Возвращение массива с использованием C

Я относительно новичок в C, и мне нужна помощь в методах работы с массивами. Исходя из Java-программирования, я привык говорить int [] method(), чтобы вернуть массив. Тем не менее, я выяснил, что с C вам нужно использовать указатели для массивов, когда вы их возвращаете. Будучи новым программистом, я действительно не понимаю этого вообще, даже со многими форумами, которые я просмотрел.

В основном, я пытаюсь написать метод, который возвращает массив char в C. Я предоставлю метод (разрешаю его returnArray) с массивом. Он создаст новый массив из предыдущего массива и вернет указатель на него. Мне просто нужна помощь в том, как это начать и как читать указатель после его отправки из массива. Любая помощь, объясняющая это, ценится.

Предлагаемый формат кода для возвращаемой функции массива

char *returnArray(char array []){
 char returned [10];
 //methods to pull values from array, interpret them, and then create new array
 return &(returned[0]); //is this correct?
} 

Звонок функции

int main(){
 int i=0;
 char array []={1,0,0,0,0,1,1};
 char arrayCount=0;
 char* returnedArray = returnArray(&arrayCount); ///is this correct?
 for (i=0; i<10;i++)
  printf(%d, ",", returnedArray[i]);  //is this correctly formatted?
}

Я еще не тестировал это, так как мой компилятор C не работает в данный момент, но я хотел бы понять это.

Ответ 1

Вы не можете возвращать массивы из функций в C. Вы также не можете (не должны) делать это:

char *returnArray(char array []){
 char returned [10];
 //methods to pull values from array, interpret them, and then create new array
 return &(returned[0]); //is this correct?
} 

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

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

Вариант 1:

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

char *foo(int count) {
    char *ret = malloc(count);
    if(!ret)
        return NULL;

    for(int i = 0; i < count; ++i) 
        ret[i] = i;

    return ret;
}

Назовите его так:

int main() {
    char *p = foo(10);
    if(p) {
        // do stuff with p
        free(p);
    }

    return 0;
}

Вариант 2:

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

void foo(char *buf, int count) {
    for(int i = 0; i < count; ++i)
        buf[i] = i;
}

И назовите его так:

int main() {
    char arr[10] = {0};
    foo(arr, 10);
    // No need to deallocate because we allocated 
    // arr with automatic storage duration.
    // If we had dynamically allocated it
    // (i.e. malloc or some variant) then we 
    // would need to call free(arr)
}

Ответ 2

C обработка массивов сильно отличается от Java, и вам придется соответствующим образом скорректировать свое мышление. Массивы в C не являются первоклассными объектами (т.е. Выражение массива не сохраняет его "array-ness" в большинстве контекстов). В C выражение типа "N-element array of T" будет неявно преобразовано ( "распад" ) в выражение типа "указатель на T", за исключением случаев, когда выражение массива является операндом sizeof или унарные &, или если выражение массива является строковым литералом, используемым для инициализации другого массива в объявлении.

Кроме всего прочего, это означает, что вы не можете передать выражение массива функции и получить его как тип массива; функция фактически получает тип указателя:

void foo(char *a, size_t asize)
{
  // do something with a
}

int bar(void)
{
  char str[6] = "Hello";
  foo(str, sizeof str);
}

При вызове foo выражение str преобразуется из типа char [6] в char *, поэтому первый параметр foo объявляется char *a вместо char a[6]. В sizeof str, поскольку выражение массива является операндом оператора sizeof, оно не преобразуется в тип указателя, поэтому вы получаете количество байтов в массиве (6).

Если вам действительно интересно, вы можете прочитать Dennis Ritchie "Развитие языка C" , чтобы понять, откуда взялось это лечение.

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

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

void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)
{
  ...
  dstArray[i] = some_value_derived_from(srcArray[i]);
  ...
}

int main(void)
{
  char src[] = "This is a test";
  char dst[sizeof src];
  ...
  returnArray(src, sizeof src, dst, sizeof dst);
  ...
}

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

char *returnArray(const char *srcArray, size_t srcSize, size_t *dstSize)
{
  char *dstArray = malloc(srcSize);
  if (dstArray)
  {
    *dstSize = srcSize;
    ...
  }
  return dstArray;
}

int main(void)
{
  char src[] = "This is a test";
  char *dst;
  size_t dstSize;

  dst = returnArray(src, sizeof src, &dstSize);
  ...
  free(dst);
  ...
}

В этом случае вызывающий отвечает за освобождение массива библиотечной функцией free.

Обратите внимание, что dst в приведенном выше коде является простым указателем на char, а не указателем на массив из char. C-указатель и семантика массива таковы, что вы можете применить индексный оператор [] к выражению типа массива или типа указателя; оба src[i] и dst[i] будут обращаться к i -му элементу массива (хотя только src имеет тип массива).

Вы можете объявить указатель на N-элементный массив T и сделать что-то подобное:

char (*returnArray(const char *srcArr, size_t srcSize))[SOME_SIZE]
{
  char (*dstArr)[SOME_SIZE] = malloc(sizeof *dstArr);
  if (dstArr)
  {
    ...
    (*dstArr)[i] = ...;
    ...
  }
  return dstArr;
}

int main(void)
{
  char src[] = "This is a test";
  char (*dst)[SOME_SIZE];
  ...
  dst = returnArray(src, sizeof src);
  ...
  printf("%c", (*dst)[j]);
  ...
}

Несколько недостатков с вышесказанным. Прежде всего, более старые версии C ожидают, что SOME_SIZE будет константой времени компиляции, что означает, что функция будет работать только с одним размером массива. Во-вторых, вы должны разыменовать указатель перед применением индекса, который загромождает код. Указатели на массивы работают лучше, когда вы имеете дело с многомерными массивами.

Ответ 3

Как насчет этой невероятно злой реализации?

array.h

#define IMPORT_ARRAY(TYPE)    \
    \
struct TYPE##Array {    \
    TYPE* contents;    \
    size_t size;    \
};    \
    \
struct TYPE##Array new_##TYPE##Array() {    \
    struct TYPE##Array a;    \
    a.contents = NULL;    \
    a.size = 0;    \
    return a;    \
}    \
    \
void array_add(struct TYPE##Array* o, TYPE value) {    \
    TYPE* a = malloc((o->size + 1) * sizeof(TYPE));    \
    TYPE i;    \
    for(i = 0; i < o->size; ++i) {    \
        a[i] = o->contents[i];    \
    }    \
    ++(o->size);    \
    a[o->size - 1] = value;    \
    free(o->contents);    \
    o->contents = a;    \
}    \
void array_destroy(struct TYPE##Array* o) {    \
    free(o->contents);    \
}    \
TYPE* array_begin(struct TYPE##Array* o) {    \
    return o->contents;    \
}    \
TYPE* array_end(struct TYPE##Array* o) {    \
    return o->contents + o->size;    \
}

main.c

#include <stdlib.h>
#include "array.h"

IMPORT_ARRAY(int);

struct intArray return_an_array() {
    struct intArray a;
    a = new_intArray();
    array_add(&a, 1);
    array_add(&a, 2);
    array_add(&a, 3);
    return a;
}

int main() {
    struct intArray a;
    int* it;
    int* begin;
    int* end;
    a = return_an_array();
    begin = array_begin(&a);
    end = array_end(&a);
    for(it = begin; it != end; ++it) {
        printf("%d ", *it);
    }
    array_destroy(&a);
    getchar();
    return 0;
}

Ответ 4

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

char * returnArray(char *arr, int size) {
    char *new_arr = malloc(sizeof(char) * size);
    for(int i = 0; i < size; ++i) {
        new_arr[i] = arr[i];
    }
    return new_arr;
}

int main() {

    char arr[7]= {1,0,0,0,0,1,1};
    char *new_arr = returnArray(arr, 7);

    // don't forget to free the memory after you're done with the array
    free(new_arr);

}

Ответ 5

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

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

    typedef
    struct 
    {
        char v[10];
    } CHAR_ARRAY;



    CHAR_ARRAY returnArray(CHAR_ARRAY array_in, int size)
    {
        CHAR_ARRAY returned;

        /*
        . . . methods to pull values from array, interpret them, and then create new array
        */

        for (int i = 0;  i < size; i++ )
            returned.v[i] = array_in.v[i] + 1;

        return returned; // Works!
    } 




    int main(int argc, char * argv[])
    {
        CHAR_ARRAY array = {1,0,0,0,0,1,1};

        char arrayCount = 7;

        CHAR_ARRAY returnedArray = returnArray(array, arrayCount); 

        for (int i = 0; i < arrayCount; i++)
            printf("%d, ", returnedArray.v[i]);  //is this correctly formatted?

        getchar();
        return 0;
    }

Я предлагаю комментарии о сильных и слабых сторонах этого метода. Я не удосужился это сделать.

Ответ 6

Вы можете сделать это, используя память кучи (через вызов malloc()), как и другие ответы, описанные здесь, но вы всегда должны управлять памятью (используйте функцию free() каждый раз, когда вы вызываете свою функцию). Вы также можете сделать это со статическим массивом:

char* returnArrayPointer() 
{
static char array[SIZE];

// do something in your array here

return array; 
}

Вы можете использовать его, не беспокоясь об управлении памятью.

int main() 
{
char* myArray = returnArrayPointer();
/* use your array here */
/* don't worry to free memory here */
}

В этом примере вы должны использовать статическое ключевое слово в определении массива, чтобы установить время жизни массива в приложении, поэтому оно не будет уничтожено после оператора return. Разумеется, таким образом вы занимаете байты SIZE в своей памяти на весь срок службы приложения, поэтому правильно его оцените!

Ответ 7

Ваш метод вернет переменную локального стека, которая не удастся. Чтобы вернуть массив, создайте его вне функции, передайте его по адресу в функцию, затем измените его или создайте массив в куче и верните эту переменную. Оба будут работать, но первый не требует никакого динамического распределения памяти, чтобы заставить его работать правильно.

void returnArray(int size, char *retArray)
{
  // work directly with retArray or memcpy into it from elsewhere like
  // memcpy(retArray, localArray, size); 
}

#define ARRAY_SIZE 20

int main(void)
{
  char foo[ARRAY_SIZE];
  returnArray(ARRAY_SIZE, foo);
}

Ответ 8

Вы можете использовать такой код:

char *MyFunction(some arguments...)
{
    char *pointer = malloc(size for the new array);
    if (!pointer)
        An error occurred, abort or do something about the error.
    return pointer; // Return address of memory to the caller.
}

Когда вы это сделаете, память должна быть освобождена, передав адрес на бесплатную.

Существуют и другие варианты. Подпрограмма может возвращать указатель на массив (или часть массива), который является частью некоторой существующей структуры. Вызывающий может передать массив, и программа просто записывает в массив, а не выделяет пространство для нового массива.

Ответ 9

Вот небольшой пример с различными вариантами передачи строк и возвращаемых строк.

//include DPI
#include "svdpi.h"
//include the IO files
#include <stdio.h>
//include string functions
#include <string.h>
//include use of malloc
#include <stdlib.h>

//to add the ability to use printf
// same inputs as defined in SV



 /* function declaration text char stream passed as pointer value and text2 passed as pointer reference */
  char * return_string_in_c( char *text,  char **text2) {
    char *fix="This variable cannot be changed although it is a pointer, just as example";/*fix allocation and constant*/
    char dest[50]="Variable array created in a function:";/* String array max 50 chars allocated*/
    char *out = malloc(sizeof(char) * 100);/* Dynamic string max 100 chars allocated*/
    /* Concatenate input text and put in out of strcat*/
    strcat(out, text);/* initialize out using strcat and text string*/
    printf("fix : |%s|,dest : |%s|,text : |%s|,out : |%s|\n", fix,dest,text,out);
    *text2=dest;/* allocate pointer value with dest*/
    *text2=out;/* now do the same overwrite allocate pointer value with out*/
    return out;
}
 
 /* main */
 void main() {
    char text[100]="from_main_text_variable";/*max 100 chars allocated*/
    char *text2;/* pointer not allocated*/
    char *point = return_string_in_c(text, &text2);/* &text2 passing by reference*/
    printf("Final destination string : |%s|\n", text2);
    printf("point output : |%s|\n", point);
    
 }

Ответ 10

объявить массив как глобальную переменную, чтобы каждая функция могла ее редактировать. пример   #включают   #include

char a[1000];
void edit(char b);
main()
{
 edit(a);
 printf("%s", a);
}

void edit(char b)
{
 for(int i=0;i<sizeof(b);i++)
 a[i]=i;
}