C의 함수에서`struct '를 반환


171

오늘 저는 두 친구에게 C 사용법을 가르치고있었습니다 struct. 당신이를 반환 할 수 있다면 그 중 하나가 물었다 struct"아니 당신은 동적으로의 포인터 반환 것! 내가 대답되는 함수에서 malloc에드struct 대신 합니다."

주로 C ++을 수행하는 사람에게서 왔으므로 struct값으로 s 를 반환 할 수 없을 것으로 기대 했습니다. C ++에서는 operator =객체에 과부하를 가할 수 있으며 객체를 값으로 반환하는 기능을 갖는 것이 좋습니다. 그러나 C에서는 옵션이 없으므로 컴파일러가 실제로하는 일을 생각하게했습니다. 다음을 고려하세요:

struct MyObj{
    double x, y;
};

struct MyObj foo(){
    struct MyObj a;

    a.x = 10;
    a.y = 10;

    return a;
}        

int main () {

    struct MyObj a;

    a = foo();    // This DOES work
    struct b = a; // This does not work

    return 0;
}    

struct b = a;작동하지 않아야 하는지 이해 operator =합니다. 데이터 유형에 과부하가 걸리지 않습니다 . a = foo();잘 컴파일 되는 방법은 무엇입니까? 다른 의미 struct b = a;입니까? 어쩌면 물어볼 질문은 다음 return과 같습니다.= 서명 하는가?

[편집] : 좋아, 방금 지적한 struct b = a것은 구문 오류입니다. 맞습니다. 그리고 저는 바보입니다! 그러나 그것은 훨씬 더 복잡합니다! 사용 struct MyObj b = a은 실제로 작동합니다! 내가 여기서 무엇을 놓치고 있습니까?


23
struct b = a;구문 오류입니다. 시도하면 어떻게 struct MyObj b = a;되나요?
Greg Hewgill

2
@GregHewgill : 당신은 절대적으로 맞습니다. 그러나 매우 흥미롭게도 struct MyObj b = a;작동하는 것 같습니다 :)
mmirzadeh

답변:


199

=문제없이 함수에서 구조를 반환하거나 연산자를 사용할 수 있습니다. 언어의 잘 정의 된 부분입니다. 유일한 문제 struct b = a는 완전한 유형을 제공하지 않았다는 것입니다. struct MyObj b = a잘 작동합니다. 당신은 구조 전달할 수 있습니다 함수에 . 구조체는 매개 변수 전달, 반환 값 및 할당을 위해 모든 내장 유형과 동일합니다.

다음은 세 가지를 모두 수행하는 간단한 데모 프로그램입니다. 구조를 매개 변수로 전달하고 함수에서 구조를 반환하며 할당 문에서 구조를 사용합니다.

#include <stdio.h>

struct a {
   int i;
};

struct a f(struct a x)
{
   struct a r = x;
   return r;
}

int main(void)
{
   struct a x = { 12 };
   struct a y = f(x);
   printf("%d\n", y.i);
   return 0;
}

다음 예제는 거의 동일하지만 int데모 목적으로 내장 유형을 사용합니다. 두 프로그램은 매개 변수 전달, 할당 등에 대한 값별 전달과 관련하여 동일한 동작을 갖습니다.

#include <stdio.h>

int f(int x) 
{
  int r = x;
  return r;
}

int main(void)
{
  int x = 12;
  int y = f(x);
  printf("%d\n", y);
  return 0;
}

14
꽤 흥미 롭습니다. 나는 항상 이것에 대한 포인터가 필요하다는 인상을 받았다. 나는 틀렸다 :)
mmirzadeh

8
당신은 확실히 포인터 가 필요 하지 않습니다 . 즉, 대부분의 경우 파일을 사용하려고합니다. 값별로 플링 구조를 수행하는 암시 적 메모리 복사본은 메모리 대역폭은 말할 것도없고 CPU 사이클을 낭비 할 수 있습니다.
Carl Norum

10
@CarlNorum은 사본이 malloc + 무료보다 더 많은 비용이 들도록 구조가 얼마나 커야합니까?
josefx

7
@josefx, 하나의 사본? 아마 거대하다. 문제는 일반적으로 값으로 구조를 전달하는 경우 많이 복사하는 것 입니다. 어쨌든 그것은 그렇게 간단하지 않습니다. 로컬 또는 글로벌 구조를 통과 할 수 있으며이 경우 세 번째 할당 비용은 거의 무료입니다.
Carl Norum

7
컴파일 타임에 값에 할당 된 메모리 양을 알지 못하면 함수 본문 외부에서 반환 된 값에 대한 포인터 및 메모리 할당이 필요합니다. 구조체 용이므로 C 함수는 반환에 문제가 없습니다.
reinierpost 2009

33

와 같은 호출을 수행 할 때 a = foo();컴파일러 는 스택에 결과 구조 의 주소 를 푸시하고 이를 foo()함수에 대한 "숨겨진"포인터로 전달할 수 있습니다. 효과적으로 다음과 같이 될 수 있습니다.

void foo(MyObj *r) {
    struct MyObj a;
    // ...
    *r = a;
}

foo(&a);

그러나 이것의 정확한 구현은 컴파일러 및 / 또는 플랫폼에 따라 다릅니다. Carl Norum이 언급했듯이 구조가 충분히 작 으면 레지스터로 완전히 다시 전달 될 수도 있습니다.


11
그것은 완전히 구현에 달려 있습니다. 예를 들어 armcc는 일반 매개 변수 전달 (또는 반환 값) 레지스터에서 충분히 작은 구조를 전달합니다.
Carl Norum

로컬 변수에 대한 포인터를 반환하지 않습니까? 반환 된 구조의 메모리는 foo스택 프레임의 일부가 될 수 없습니다 . 의 귀환 이후에도 생존 할 수있는 장소에 있어야 foo합니다.
Anders Abel

@AndersAbel : Greg가 의미하는 것은 컴파일러가 기본 함수 의 변수에 대한 포인터를 가져 와서 함수에 전달한다는 것 foo입니다. 함수 안에서 foo, 당신은 할당을 수행합니다
mmirzadeh

4
@AndersAbel : *r = a끝에는 호출자의 변수에 로컬 변수의 사본을 효과적으로 적용합니다. 컴파일러가 RVO를 구현 하고 로컬 변수를 a완전히 제거 할 수 있기 때문에 "효과적으로"라고 말합니다 .
Greg Hewgill

3
이 직접 질문에 대답하지 않지만,이 많은 사람들이 구글을 통해 여기에 떨어질 것입니다 이유입니다 c return struct그들은 cdecl 규칙에 알고 : eax값에 의해 일반적으로 구조체 적절하게 내부를하지 않을 것을 반환됩니다 eax. 이것이 내가 찾던 것입니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

14

struct b는 구문 오류 때문에 라인이 작동하지 않습니다. 유형을 포함하도록 확장하면 제대로 작동합니다.

struct MyObj b = a;  // Runs fine

여기서 C는 기본적으로 memcpy소스 구조체에서 대상까지입니다. 이것은 struct값의 할당과 반환 모두에 해당 하며 실제로 C의 다른 모든 값에도 적용됩니다.


실제로 +1, memcpy이 경우에는 적어도 구조가 상당히 큰 경우 많은 컴파일러가 실제로 문자 그대로 호출합니다 .
Carl Norum

따라서 데이터 유형을 초기화하는 동안 memcpy 함수가 작동합니까 ??
bhuwansahni

1
@bhuwansahni 나는 당신이 무엇을 요구하는지 잘 모르겠습니다. 좀 더 자세히 설명해 주시겠습니까?
JaredPar

4
@JaredPar-컴파일러는 종종 문자 그대로memcpy 구조 상황에 대한 함수를 호출 합니다 . 예를 들어, 빠른 테스트 프로그램을 만들어 GCC에서 확인할 수 있습니다. 발생하지 않는 내장 유형의 경우 이러한 유형의 최적화를 트리거하기에 충분히 크지 않습니다.
Carl Norum

3
내가 일하고있는 프로젝트에 memcpy심볼이 정의 되어 있지 않기 때문에 컴파일러가 독자적으로 뱉어 낼 때 "정의되지 않은 심볼"링커 오류가 발생하는 경우가 종종 있습니다.
Carl Norum

9

그렇습니다, 우리는 구조체와 리턴 구조체도 전달할 수 있습니다. 당신이 옳았지만 실제로이 구조체와 같은 데이터 유형을 전달하지 못했습니다. MyObj b = a.

실제로 포인터 나 전역 변수를 사용하지 않고 함수에 대해 둘 이상의 값을 반환하는 더 나은 솔루션을 찾으려고했을 때도 알게되었습니다 .

이제 아래는 동일한 예이며, 평균에 대한 학생 점수의 편차를 계산합니다.

#include<stdio.h>
struct marks{
    int maths;
    int physics;
    int chem;
};

struct marks deviation(struct marks student1 , struct marks student2 );

int main(){

    struct marks student;
    student.maths= 87;
    student.chem = 67;
    student.physics=96;

    struct marks avg;
    avg.maths= 55;
    avg.chem = 45;
    avg.physics=34;
    //struct marks dev;
    struct marks dev= deviation(student, avg );
    printf("%d %d %d" ,dev.maths,dev.chem,dev.physics);

    return 0;
 }

struct marks deviation(struct marks student , struct marks student2 ){
    struct marks dev;

    dev.maths = student.maths-student2.maths;
    dev.chem = student.chem-student2.chem;
    dev.physics = student.physics-student2.physics; 

    return dev;
}

5

내가 기억할 수있는 한, C의 첫 번째 버전은 프로세서 레지스터에 맞는 값만 반환 할 수있었습니다. 즉, 구조체에 대한 포인터 만 반환 할 수 있습니다. 함수 인수에도 동일한 제한이 적용됩니다.

최신 버전에서는 구조체와 같은 더 큰 데이터 객체를 전달할 수 있습니다. 이 기능은 80 년대 또는 90 년대 초에 이미 일반적이었다고 생각합니다.

그러나 배열은 여전히 ​​포인터로 전달 및 반환 될 수 있습니다.


구조체 안에 구조체를 넣으면 값으로 배열을 반환 할 수 있습니다. 값으로 반환 할 수없는 것은 가변 길이 배열입니다.
han

1
예, 구조체 안에 배열을 넣을 수는 있지만 typedef char arr [100]; arr foo () {...} 크기를 알고 있더라도 배열을 리턴 할 수 없습니다.
Giorgio

downvoter가 downvote의 이유를 설명 할 수 있습니까? 답변에 잘못된 정보가 포함되어 있으면 문제를 해결해 드리겠습니다.
조르지오

4

C 로 구조체를 할당 있습니다a = b; . 유효한 구문입니다.

작동하지 않는 라인에서 유형의 일부 (struct 태그)를 간단히 제거했습니다.


4

구조체를 다시 전달할 때는 아무런 문제가 없습니다. 가치에 의해 전달됩니다

그러나 구조체에 로컬 변수의 주소를 가진 멤버가 포함되어 있으면 어떻게됩니까?

struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

이제 e1.name은 get () 함수에 로컬 인 메모리 주소를 포함합니다. get ()이 반환되면 name의 로컬 주소가 해제됩니다. 따라서 발신자가 해당 주소에 액세스하려고 시도하면 해제 된 주소를 시도 할 때 세그먼트 오류가 발생할 수 있습니다. 그건 나빠..

e1.id는 값이 e2.id에 복사되므로 완벽하게 유효합니다.

따라서 항상 함수의 로컬 메모리 주소를 반환하지 않도록 노력해야합니다.

malloced는 무엇이든 원할 때 반환 할 수 있습니다


2
struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

최신 버전의 컴파일러에서 잘 작동합니다. id와 마찬가지로 이름의 내용이 할당 된 구조 변수에 복사됩니다.


1
더 간단합니다 : struct emp get () {return {100, "john"}; }
Chris Reid

1

struct var e2 주소가 수신자 스택에 인수로 푸시되고 값이 할당됩니다. 실제로 get ()은 eax reg에서 e2의 주소를 반환합니다. 이것은 참조에 의한 호출처럼 작동합니다.

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