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 *되므로의 첫 번째 매개 변수 foochar *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.