C를 사용하여 배열 반환


153

나는 C를 처음 접했고 배열을 다루는 메소드에 대한 도움이 필요하다. Java 프로그래밍에서 왔으므로 int [] method()배열을 반환하기 위해 말할 수있었습니다 . 그러나 C를 사용하면 배열을 반환 할 때 포인터를 사용해야한다는 것을 알았습니다. 새로운 프로그래머이기 때문에, 내가 살펴본 많은 포럼 에서조차도 이것을 전혀 이해하지 못합니다.

기본적으로 C에서 char 배열을 반환하는 메서드를 작성하려고합니다. 배열과 함께 메서드를 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 컴파일러가 현재 작동하지 않기 때문에 아직 테스트하지 않았지만 이것을 파악하고 싶습니다.


리턴 배열이 코드 샘플에 표시된 크기로 알려져 있습니까? 답변에 언급 된 스택 문제 외에도 내가 볼 수있는 유일한 단점은 C에서 포인터 / 배열이 작동하는 방식을 고려할 때 반환 배열이 알 수없는 크기이면 크기가 얼마나 큰지 알 수 없다는 것입니다.
— strangefreeworld

예, 들어오는 배열의 크기를 항상 알고 있습니다. 입력 및 출력 배열의 크기는 변경되지 않습니다.
— user1506919

1
C 언어의 개발 * -bell-labs.com
— usr

답변:


225

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

33
옵션 3 : (정적 배열)
— moooeeeep

5
@ moooeeeep : 예, 간단하게 유지하기 위해 의도적으로 제외했지만 함수 내에서 선언 된 정적 데이터에 대한 포인터를 반환 할 수 있습니다.
— Ed S.

3
@ user1506919 : 메모리를 할당하고 할당 해제하는 사람이 분명하기 때문에 실제로 옵션 2를 선호하지만 예제를 추가하겠습니다.
— Ed S.

7
옵션 4 : 고정 크기 배열을 포함하는 구조체를 반환합니다.
— 토드 리먼

2
옵션 5 : 고정 크기 배열을 포함하는 공용체를 리턴하십시오.
— sqr163

27

C의 배열 처리는 Java와 매우 다르므로 그에 따라 생각을 조정해야합니다. C의 배열은 일류 객체가 아닙니다 (즉, 배열 표현식은 대부분의 컨텍스트에서 배열을 유지하지 않습니다). 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의 The 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는 컴파일 타임 상수가 될 것으로 기대 합니다. 즉, 함수는 하나의 배열 크기에서만 작동합니다. 둘째, 아래 첨자를 적용하기 전에 포인터를 역 참조하면 코드가 복잡해집니다. 다차원 배열을 다룰 때 배열에 대한 포인터가 더 잘 작동합니다.


2
"C의 개발"에 대한 귀하의 링크가 좋아 보이는 여기에 우리가 직접해야 ... 깨진있다 : bell-labs.com/usr/dmr/www/chist.html
— Dr.Queso

@ Kundor :받는 것은 bar배열이 아니라 포인터입니다. 함수 매개 변수 선언 T a[N]과 T a[]관련하여 둘 다로 취급됩니다 T *a.
— John Bode

@JohnBode : 당신이 맞아요! 어떤 이유로 고정 크기 배열이 스택에 전달되었다고 생각했습니다. 몇 년 전에 배열의 크기를 매개 변수 서명에 지정해야한다는 사실을 기억하지만 혼란 스러웠습니다.
— Nick Matteo

두 번째 코드 부분의 첫 번째 줄에서 @JohnBode : void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)마지막 매개 변수는 size_tnot 형식 이어야합니다 char.
— Seyfi

11

나는 이것이 주어진 문제에 대한 최선의 해결책이거나 선호되는 해결책이라고 말하지 않습니다. 그러나 함수가 구조체를 반환 할 수 있다는 것을 기억하는 것이 유용 할 수 있습니다. 함수는 배열을 반환 할 수 없지만 배열은 구조체로 래핑 될 수 있으며 함수는 구조체를 반환하여 배열을 전달할 수 있습니다. 고정 길이 배열에서 작동합니다.

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

이 기술의 강점과 약점에 대한 의견을 제시합니다. 나는 그렇게 귀찮게하지 않았습니다.


1
이것이 왜 받아 들여지지 않는지 불분명합니다. 배열에 대한 포인터를 반환 할 수 있는지 여부는 문제가 아닙니다.
— Frank Puck

CHAR_ARRAY returned힙에 메모리가 할당되어 있습니까? 그것은 확실히 스택에있을 수 없습니다 ( returnArray()오른쪽 스택 프레임에서 ?
— Minh Tran

9

이 맛있게 나쁜 구현은 어떻습니까?

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

2
이것은 나의 호기심을 일으켰을 정도로 엄청나게 맛있다. 거기에서 한 일을 조금 더 설명하거나이 맛에 대한 독서를 제안 할 수 있습니까? 미리 감사드립니다.
— Unheilig

1
@Unheilig-이것에 sime 잠재적 인 버그가 있다는 것을 주목하십시오. 그것은 단지 개념의 증거 일뿐입니다. 즉, 트릭은 struct배열 컨테이너 / 객체로 반환 합니다. 그것을 C ++ std :: vector처럼 생각하십시오. 전처리 기는이 int버전을로 확장합니다 struct intArray { int* contents; int size; };.
— pyrospade

1
나는 접근 방식을 좋아한다. 찬성 : 이것은 일반적인 해결책입니다. 대조 : 메모리 집약적 솔루션. kown 크기의 벡터에는 적합하지 않습니다. 어쨌든 이것은 초기 크기 할당으로 업그레이드 할 수 있습니다. definitley가 할당 확인을 추가합니다. :)로 시작하는 아주 좋은 제안
— urkon

객체 지향 데스크탑 사전 혼합 믹스 매쉬. 나는 그것을 좋아한다.
— Jack Giffin

6

귀하의 경우 스택에 배열을 작성하고 함수 범위를 벗어나면 배열이 할당 해제됩니다. 대신 동적으로 할당 된 배열을 만들고 그에 대한 포인터를 반환하십시오.

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

}

2
newC 에는 연산자 가 없습니다 . 즉 C ++입니다.
— Eric Postpischil

1
그리고 sizeof(char)이 보장되고 1,이 경우에 당신은에서 해당 비트를 삭제할 수 있습니다 malloc.
— Ed S.

ok 그래서 새 배열의 내용을 인쇄하려면 'printf'문을 수행하고 'returnedArray'를 'arr'로 바꿀 수 있습니까?
— user1506919

함수를 올바르게 호출하지 않습니다 (서명에 두 개가 필요한 경우 하나의 인수 만).
— Ed S.

에 전달 중 &arr입니다. 당신이 arr되고 싶어하고 char *그것을 사용하여 전달합니다 arr.
— chris

4

여기에보고 된 다른 답변과 같이 힙 메모리 ( 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 */
}

이 예제에서는 배열 정의에 static 키워드를 사용하여 배열 수명 기간 동안 응용 프로그램으로 설정해야하므로 return 문 이후에 삭제되지 않습니다. 물론이 방법으로 전체 응용 프로그램 수명 동안 메모리에서 SIZE 바이트를 차지하므로 크기를 적절하게 조정하십시오!


2

메소드는 잘못 실패한 로컬 스택 변수를 리턴합니다. 배열을 반환하려면 함수 외부에 배열을 만들고 주소로 함수에 전달한 다음 수정하거나 힙에 배열을 만들고 해당 변수를 반환하십시오. 둘 다 작동하지만 첫 번째는 올바르게 작동하기 위해 동적 메모리 할당이 필요하지 않습니다.

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

0

다음과 같은 코드를 사용할 수 있습니다.

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

이렇게하면 나중에 주소를 free로 전달하여 메모리를 해제해야합니다.

다른 옵션이 있습니다. 루틴은 기존 구조의 일부인 배열 (또는 배열의 일부)에 대한 포인터를 리턴 할 수 있습니다. 호출자는 배열을 전달할 수 있으며 루틴은 새 배열에 공간을 할당하는 대신 배열에 쓰기 만합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.