C에서 참조로 전달


204

C가 참조로 변수 전달을 지원하지 않으면 왜 작동합니까?

#include <stdio.h>

void f(int *j) {
  (*j)++;
}

int main() {
  int i = 20;
  int *p = &i;
  f(p);
  printf("i = %d\n", i);

  return 0;
}

산출:

$ gcc -std=c99 test.c
$ a.exe
i = 21 

22
이 코드의 어디에서 참조 를 전달 합니까?
Atul

15
C는 참조로 전달하지 않으며 포인터를 사용하여 에뮬레이션 할 수 있습니다 .
일부 프로그래머 친구

6
올바른 문장은 "C는 참조로 변수를 암시 적으로 전달하는 것을 지원하지 않습니다 "입니다 &. 함수를 호출하기 전에 명시 적으로 참조를 작성하고 (with *) 함수에서 명시 적으로 참조를 해제해야 합니다.
Chris Dodd

2
호출 할 때 코드 출력은 정확히 f(&i);참조에 의한 전달의 구현이며, C
EsmaeelE

@Someprogrammerdude 포인터를 전달하면 참조로 전달됩니다. 이것은 "정통한"C 프로그래머들이 자부하는 사실 중 하나 인 것 같습니다. 그들이 쫓아내는 것처럼. "아마도 THINK C는 참조로 전달할 수 있지만 실제로 harharhar로 전달되는 메모리 주소의 값은 아닙니다." 문자 그대로 참조로 전달한다는 것은 변수 값 자체가 아니라 변수가 저장된 위치의 메모리 주소를 전달한다는 의미입니다. 이것이 C가 허용하는 것이며 포인터는 변수 메모리 위치에 대한 참조 이기 때문에 포인터를 전달할 때마다 참조로 전달 됩니다.
YungGun

답변:


315

포인터 의 값 을 메소드에 전달한 다음 참조하는 정수를 얻기 위해 역 참조하기 때문입니다.


4
f (p); -> 이것은 가치에 의한 통과를 의미합니까? 그런 다음 그것을 가리키는 정수를 얻기 위해 그것을 역 참조하십시오. -> 자세한 설명을 부탁드립니다.
bapi

4
@bapi, 포인터 역 참조는 "이 포인터가 참조하는 값을 얻는"것을 의미합니다.
Rauni Lillemets

우리는 포인터를 전달하는 대신 변수의 주소를 취하는 함수를 호출하는 메소드를 호출합니다. 예 : func1 (int & a). 이것은 참조에 의한 호출이 아닙니까? 이 경우 참조가 실제로 취해지고 포인터의 경우 값으로 만 포인터를 전달하기 때문에 여전히 값으로 전달됩니다.
Jon Wheelock

1
포인터를 사용할 때 중요한 사실은 포인터의 사본 이 함수에 전달 된다는 것입니다 . 그런 다음 함수는 원래 포인터가 아닌 해당 포인터를 사용합니다. 이것은 여전히 ​​가치에 지나지 않지만 작동합니다.
Danijel

1
@Danijel 함수의 복사본에 다른 사본이 아닌 포인터를 전달할 수 있습니다. 예를 들어, 함수 호출 func: func(&A);이것은 아무것도 복사하지 않고 함수에 대한 A에 대한 포인터를 전달합니다. 값별로 전달되지만 해당 값은 참조이므로 변수 A를 '참조 별 전달'입니다. 복사 할 필요가 없습니다. 참조로 전달하는 것은 유효합니다.
YungGun

124

그것은 참조에 의한 전달이 아니며 다른 사람들이 언급 한 것처럼 값에 의한 전달입니다.

C 언어는 예외없이 가치를 전달합니다. 포인터를 매개 변수로 전달한다고해서 참조로 전달되는 것은 아닙니다.

규칙은 다음과 같습니다.

함수가 실제 매개 변수 값을 변경할 수 없습니다.


함수의 스칼라 매개 변수와 포인터 매개 변수의 차이점을 살펴 보겠습니다.

스칼라 변수

이 짧은 프로그램은 스칼라 변수를 사용하여 값별 패스를 보여줍니다. param공식 매개 변수라고 variable하며 함수 호출시 실제 매개 변수라고합니다. param기능의 증가 는 변하지 않습니다 variable.

#include <stdio.h>

void function(int param) {
    printf("I've received value %d\n", param);
    param++;
}

int main(void) {
    int variable = 111;

    function(variable);
    printf("variable %d\m", variable);
    return 0;
}

결과는

I've received value 111
variable=111

통과 기준의 환영

코드 조각을 약간 변경합니다. param포인터입니다.

#include <stdio.h>

void function2(int *param) {
    printf("I've received value %d\n", *param);
    (*param)++;
}

int main(void) {
    int variable = 111;

    function2(&variable);
    printf("variable %d\n", variable);
    return 0;
}

결과는

I've received value 111
variable=112

그러면 매개 변수가 참조로 전달되었다고 믿을 수 있습니다. 아니었다. 값으로 전달되었으며 param 값은 주소입니다. int 타입 값이 증가했고, 그것이 참조에 의한 함수 호출이라고 생각하게 만드는 부작용입니다.

포인터-값으로 전달

그 사실을 어떻게 증명 / 증명할 수 있습니까? 스칼라 변수의 첫 번째 예제를 시도해 볼 수 있지만 스칼라 대신 주소 (포인터)를 사용합니다. 그것이 도움이 될 수 있는지 봅시다.

#include <stdio.h>

void function2(int *param) {
    printf("param's address %d\n", param);
    param = NULL;
}

int main(void) {
    int variable = 111;
    int *ptr = &variable;

    function2(ptr);
    printf("ptr's address %d\n", ptr);
    return 0;
}

결과적으로 두 주소가 동일합니다 (정확한 값에 대해 걱정하지 마십시오).

결과 예 :

param's address -1846583468
ptr's address -1846583468

내 의견으로는 이것은 포인터가 가치에 의해 전달된다는 것을 분명히 증명합니다. 그렇지 않으면 ptrNULL함수 호출 후.


69

C에서 참조로 전달은 변수 (포인터)의 주소를 전달하고 함수 내에서 해당 주소를 역 참조하여 실제 변수를 읽거나 쓰는 방식으로 시뮬레이션됩니다. 이것을 "C 스타일 패스 바이 레퍼런스"라고합니다.

출처 : www-cs-students.stanford.edu


50

위 코드에는 참조로 전달이 없기 때문입니다. 포인터 (예 :)를 사용하면 void func(int* p)주소별로 전달됩니다. 이것은 C ++에서 참조로 전달됩니다 (C에서는 작동하지 않음).

void func(int& ref) {ref = 4;}

...
int a;
func(a);
// a is 4 now

1
주소 별 답변이 마음에 듭니다. 더 의미가 있습니다.
Afzaal Ahmad Zeeshan 2016

이 문맥에서 주소와 참조는 동의어입니다. 그러나이 용어를 사용하여 둘을 구별 할 수 있습니다. 원래 의미에 대해서는 정직하지 않습니다.
YungGun

27

변수의 주소를 역 참조 연산자 를 사용하여 값을 조작하는 함수에 전달하기 때문에 예제가 작동합니다 .

C는 참조 데이터 유형을 지원하지 않지만 예제와 같이 포인터 값을 명시 적으로 전달하여 참조 별 전달을 시뮬레이션 할 수 있습니다.

C ++ 참조 데이터 유형은 강력하지 않지만 C에서 상속 된 포인터 유형보다 안전하다고 간주됩니다. 다음은 C ++ 참조 를 사용하도록 조정 된 예입니다 .

void f(int &j) {
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}

3
Wikipedia 기사는 C가 아니라 C ++에 관한 것입니다. 참조는 C ++ 이전에 존재했으며 존재하는 특수 C ++ 구문에 의존하지 않습니다.

1
@Roger : 좋은 지적 ... 내 대답에서 C ++에 대한 명시 적 참조를 제거했습니다.
Daniel Vassallo 8:10에

1
그리고 그 새로운 기사는 "참조는 종종 포인터 (pointer)라고 불린다"고 말합니다.

12

으로 포인터 (주소 위치) 를 전달합니다 .

"업데이트하고 싶은 데이터가있는 곳이 여기에 있습니다."라고 말하는 것과 같습니다.


6

p는 포인터 변수입니다. 그 값은 i의 주소입니다. f를 호출하면 i의 주소 인 p 의 값전달합니다 .



5

C에서는 모든 것이 가치가 있습니다. 포인터를 사용 하면 변수 의 이 변하기 때문에 참조로 전달되는 환상을 얻을 수 있습니다 . 그러나 포인터 변수의 주소를 인쇄하면 영향을받지 않습니다. ㅏ복사본 어드레스는 전달 된 상기 기능. 아래는이를 보여주는 스 니펫입니다.

void add_number(int *a) {
    *a = *a + 2;
}

int main(int argc, char *argv[]) {
   int a = 2;

   printf("before pass by reference, a == %i\n", a);
   add_number(&a);
   printf("after  pass by reference, a == %i\n", a);

   printf("before pass by reference, a == %p\n", &a);
   add_number(&a);
   printf("after  pass by reference, a == %p\n", &a);

}

before pass by reference, a == 2
after  pass by reference, a == 4
before pass by reference, a == 0x7fff5cf417ec
after  pass by reference, a == 0x7fff5cf417ec

4

변수 p에 대한 포인터 (메모리 주소)를 함수 f에 전달하기 때문에. 즉, 참조가 아닌 포인터를 전달합니다.


4

짧은 대답 : 예, C는 포인터를 사용하여 참조로 매개 변수 전달을 구현합니다.

매개 변수 전달을 구현하는 동안 프로그래밍 언어 설계자는 세 가지 전략 (또는 의미 론적 모델)을 사용합니다. 즉, 서브 프로그램으로 데이터를 전송하거나 서브 프로그램에서 데이터를 수신하거나 두 가지를 모두 수행합니다. 이러한 모델은 일반적으로 해당 모드에서 모드, 출력 모드 및 입력 모드라고합니다.

언어 디자이너는 다음 세 가지 기본 매개 변수 전달 전략을 구현하기 위해 여러 모델을 고안했습니다.

Pass-by-Value (모드 의미론) Pass-by-Result (출력 모드 의미론) Pass-by-Value-Result (입력 모드 의미론) Pass-by-Reference (입력 모드 의미론) Pass-by-Name (입력 모드) 의미론)

통과 기준은 입력 모드 파라미터 전달을위한 두 번째 기술입니다. 기본 루틴과 서브 프로그램간에 데이터를 앞뒤로 복사하는 대신 런타임 시스템은 서브 프로그램의 데이터에 대한 직접 액세스 경로를 보냅니다. 이 전략에서 서브 프로그램은 데이터를 주 루틴과 효과적으로 공유하는 데이터에 직접 액세스 할 수 있습니다. 이 기술의 주요 장점은 공간을 복제 할 필요가없고 데이터 복사 작업이 없기 때문에 시간과 공간에서 절대적으로 효율적이라는 것입니다.

C에서 매개 변수 전달 구현 : C는 매개 변수로 포인터를 사용하여 값으로 전달 및 참조로 전달 (시행 모드) 시맨틱을 구현합니다. 포인터는 서브 프로그램으로 전송되며 실제 데이터는 전혀 복사되지 않습니다. 그러나 포인터는 메인 루틴의 데이터에 대한 액세스 경로이므로 서브 프로그램은 메인 루틴의 데이터를 변경할 수 있습니다. C는 ALGOL68에서이 방법을 채택했습니다.

C ++에서 매개 변수 전달 구현 : C ++은 포인터를 사용하고 참조 유형이라는 특수한 유형의 포인터를 사용하여 참조 별 전달 (입력 모드) 시맨틱도 구현합니다. 참조 유형 포인터는 서브 프로그램 내에서 암시 적으로 역 참조되지만 해당 의미도 참조로 전달됩니다.

따라서 핵심 개념은 데이터를 서브 프로그램에 복사하는 대신 참조 별 전달이 데이터에 대한 액세스 경로를 구현한다는 것입니다. 데이터 액세스 경로는 명시 적으로 역 참조 된 포인터 또는 자동 역 참조 된 포인터 (참조 유형) 일 수 있습니다.

자세한 정보는 Robert Sebesta, 10th Ed., Chapter 9의 Programming Languages ​​of Programming Languages ​​책을 참조하십시오.


3

참조로 int를 전달하지 않고 값으로 int에 대한 포인터를 전달합니다. 다른 구문, 같은 의미.


1
+1 "다른 문법, 같은 의미." .. 의미는 구문보다 중요합니다.

사실이 아니다. 호출하여 void func(int* ptr){ *ptr=111; int newValue=500; ptr = &newvalue }함께 int main(){ int value=0; func(&value); printf("%i\n",value); return 0; }, 당신이 참조로 전달하는 경우 111 대신 500, 그것을 참조로 매개 변수를 전달 지원하지 않습니다 500 C를 인쇄해야 인쇄합니다.
Konfle Dolex

@Konfle, 구문 적으로 참조로 전달하는 ptr = &newvalue경우 허용되지 않습니다. 차이점에 관계없이 C에 추가 기능 ( "참조"자체를 재 할당 할 수있는 기능)이 있기 때문에 "동일한 의미"가 정확한 것은 아니라고 지적합니다.
xan

우리는 ptr=&newvalue그것이 참조로 전달되는 것과 같은 것을 쓰지 않습니다 . 대신 ptr=newvalueC ++의 예를 다음과 같이 작성 합니다. void func(int& ptr){ ptr=111; int newValue=500; ptr = newValue; }func ()에 전달 된 매개 변수의 값은 다음이 500됩니다.
Konfle Dolex

위의 내 의견의 경우 참조로 매개 변수를 전달하는 것은 의미가 없습니다. 그러나 매개 변수가 POD 대신 객체 인 경우 param = new Class()함수 내부에서 변경된 후에 는 값 (포인터)에 의해 전달 된 경우 호출자에게 영향을 미치지 않기 때문에 큰 차이 가 있습니다. 경우 param참조에 의해 전달되는 변경 사항이 호출자 볼 수있을 것이다.
Konfle Dolex

3

C에서 참조로 전달하려면 &변수에 사용해야하는 주소 연산자 를 사용하지만 포인터 변수를 사용했기 때문에 p접두사 연산자를 접두사로 사용할 필요가 없습니다. &i매개 변수로 사용한 경우에 해당됩니다 f(&i).

p값 을 추가하여 역 참조 하고 해당 값이 어떻게 일치하는지 확인할 수도 있습니다 i.

printf("p=%d \n",*p);

printf를 추가해야한다고 알리기 위해 모든 주석 (해설 블록 포함)을 반복해야한다고 생각한 이유는 무엇입니까?

@Neil :이 오류는 @William의 편집에 의해 도입되었으므로 이제 되돌릴 것입니다. 이제 tommieb은 대부분 올바른 것입니다. 변수뿐만 아니라 모든 객체에 적용 할 수 있습니다.

2

포인터와 참조는 서로 다른 두 thigng입니다.

내가 보지 못했던 몇 가지 것들.

포인터는 무언가의 주소입니다. 다른 변수처럼 포인터를 저장하고 복사 할 수 있습니다. 따라서 크기가 있습니다.

참조는 별명으로 간주해야합니다. 크기가 없으며 저장할 수 없습니다. 반드시 무언가를 참조해야합니다. 널이거나 변경 될 수 없습니다. 때로는 컴파일러가 참조를 포인터로 저장해야하지만 구현 세부 사항입니다.

참조를 사용하면 소유권 처리, null 검사, 사용 역 참조와 같은 포인터 관련 문제가 없습니다.


1

'포인터 기준'(포인터 사용)은 처음부터 C에 있습니다. 왜 그렇지 않다고 생각합니까?


7
기술적으로 참조로 전달되지 않기 때문입니다.
Mehrdad Afshari

7
포인터 값을 전달하는 것은 참조를 통한 전달과 다릅니다. 의 값을 갱신 j( 하지 *j )의하면 f()에는 영향이 없다 i하여 main().
John Bode

6
그것은 의미 같은 일이 참조로 전달하는 것과 같은, 그게 참조로 전달되는 말을 충분합니다. 사실, C 표준은 "참조"라는 용어를 사용하지 않지만 놀랍지도 문제도 아닙니다. 우리는 표준을 참조하더라도 SO에 대해 표준을 말하지 않습니다. 그렇지 않으면 rvalue에 대해 말하는 사람이 아무도 없습니다 (C 표준은 용어를 사용하지 않습니다).

4
@Jim : John의 의견을 불쾌하게했다고 말해 주셔서 감사합니다.

1

실제로 C는 참조로 전달을 지원한다고 생각합니다.

대부분의 언어는 구문 대신 설탕이 가치 대신 참조로 전달되도록 요구합니다. (예를 들어 C ++는 매개 변수 선언에 &가 필요합니다).

C는 또한 이것을 위해 구문 설탕이 필요합니다. 매개 변수 유형 선언과 &에 있습니다. 그래서 *와 & 참조로 전달하는 C 구문입니다.

이제 참조에 의한 실제 전달은 인수 측이 아니라 매개 변수 선언에 대한 구문 만 필요하다고 주장 할 수 있습니다.

하지만 지금은 C #을 제공 하지 참조 통과하여 지원을 하고 에 문법 설탕 요구 를 모두 매개 변수 및 인수 측면을.

C가 바이 패스를 통과하지 않는다는 주장은 구문 요소가 그것을 기반으로하는 기술적 구현을 ​​나타내도록한다. 이는 모든 구현에 다소 적용되기 때문에 전혀 논증이 아니다.

남은 유일한 주장은 C에서 ref로 전달하는 것이 모 놀리 식 기능이 아니라 기존의 두 기능을 결합한다는 것입니다. (&로 인수를 참조하고 *로 유형을 참조하십시오.) 예를 들어 C #에는 두 개의 구문 요소가 필요하지만 서로없이 사용할 수는 없습니다.

이것은 분명히 위험한 주장입니다. 언어의 다른 많은 기능이 다른 기능으로 구성되어 있기 때문입니다. (C ++에서 문자열 지원과 같은)


1

당신이하고있는 일은 참조로 전달하지 않고 값으로 전달하는 것입니다. 변수 'p'의 값을 함수 'f'에 보내기 때문에 (주로 f (p);)

C에서 참조로 전달하는 동일한 프로그램은 다음과 같습니다.

#include <stdio.h>

void f(int &j) {    //j is reference variable to i same as int &j = i
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}

산출:-

3:12 : 오류 : '&'토큰 앞에 예상되는 ';', ','또는 ')'
             무효 f (int & j);
                        ^
9 : 3 : 경고 : 함수 'f'의 암시 적 선언
               파);
               ^
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.