itsource

C에서 참조로 통과

mycopycode 2022. 9. 28. 00:09
반응형

C에서 참조로 통과

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 

그것은 다른 사람들이 말한 것처럼 pass-by-value가 아니라 pass-by-value이다.

C 언어는 예외 없이 pass-by-value입니다.포인터를 파라미터로 전달한다고 해서 pass-by-reference가 전달되는 것은 아닙니다.

규칙은 다음과 같습니다.

함수는 실제 파라미터 값을 변경할 수 없습니다.

(위의 인용은 실제로 K&R이라는 책에서 인용한 것입니다.)


함수의 스칼라와 포인터 파라미터의 차이를 알아보겠습니다.

스칼라 변수

패스 바이 밸류 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 type 값이 증가하여 pass-by-reference 함수 호출이라고 생각되는 부작용입니다.

포인터 - 전달된 값

어떻게 하면 그 사실을 보여주거나 증명할 수 있을까요?음, 스칼라 변수의 첫 번째 예를 시도해 볼 수도 있지만 스칼라 대신 주소(포인터)를 사용합니다.그게 도움이 될 수 있는지 봅시다.

#include <stdio.h>

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

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

    function2(ptr);
    printf("address ptr is pointing to %d\n", ptr);
    return 0;
}

그 결과, 2 개의 주소가 같게 됩니다(정확한 값은 걱정하지 말아 주세요).

결과 예:

address param is pointing to -1846583468
address ptr   is pointing to -1846583468

내 생각에 이것은 포인터가 가치로 전달된다는 것을 분명히 증명한다. 이외의 경우는, 「」입니다.ptrNULL함수가 호출된 후.

포인터 을 메서드에 전달한 다음 포인터를 참조하지 않고 가리키는 정수를 가져옵니다.

C에서 Pass-by-reference는 변수(포인터)의 주소를 전달하고 함수 내에서 해당 주소를 참조함으로써 실제 변수를 읽거나 쓰도록 시뮬레이트된다.이를 "C style pass-by-reference"라고 합니다.

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

참고하다(「」등):void func(int* p)는 패스 어드레스입니다패스 바이 어드레스이것은 C++에서의 패스 바이 레퍼런스입니다(C에서는 동작하지 않습니다).

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

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

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

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

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

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

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

  return 0;
}

C에는 pass-by-reference가 없지만 p는 i를 참조하고 p는 값으로 전달합니다.

하려면 연산자 C의 합니다.&poperator.address-of operator의 만약 당신이 이 모든 것을 사용했다면&i 변수: 파미터음음 음음음음음 음 as as as as as as as as as as as as 。f(&i).

할 수도 있습니다.p이 어떻게 합니다.i:

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

C에서는 모든 것이 pass-by-value이다.포인터를 사용하면 변수의 이 변경되기 때문에 참조를 통해 전달되는 것처럼 착각할 수 있습니다.다만, 포인터 변수의 주소를 인쇄하는 경우는, 영향을 받지 않는 것을 알 수 있습니다.주소 의 복사본이 함수에 전달됩니다.아래는 그것을 설명하는 단편입니다.

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

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

간단한 답변:네, C는 포인터를 사용하여 참조에 의해 전달되는 파라미터를 구현합니다.

매개 변수 전달을 구현하는 동안 프로그래밍 언어 설계자는 세 가지 다른 전략(또는 의미 모델)을 사용합니다. 즉, 데이터를 하위 프로그램으로 전송하거나, 하위 프로그램에서 데이터를 수신하거나, 둘 다 수행합니다.이러한 모델은 일반적으로 in 모드, out 모드 및 inout 모드로 알려져 있습니다.

언어 설계자는 다음과 같은 세 가지 기본 매개 변수 전달 전략을 구현하기 위해 몇 가지 모델을 고안했습니다.

Pass-by-Value(모드 시멘틱스) Pass-by-Result(아웃모드 시멘틱스) Pass-by-Value-Result(인아웃모드 시멘틱스) Pass-By-Reference(인아웃모드 시멘틱스) Pass-By-Name(인아웃모드 시멘틱스)

pass-by-reference는 inout-mode 파라미터 전달의 두 번째 기법입니다.런타임 시스템은 메인 루틴과 서브 프로그램 간에 데이터를 복사하는 대신 서브 프로그램을 위한 데이터에 대한 직접 액세스 경로를 보낸다.이 전략에서는 서브 프로그램이 데이터에 직접 액세스하여 메인 루틴과 데이터를 효과적으로 공유할 수 있습니다.이 기술의 주요 장점은 공간을 중복할 필요가 없고 데이터 복사 작업이 없기 때문에 시간과 공간 면에서 매우 효율적이라는 것입니다.

C: C에서의 파라미터 전달 실장은 Pass-by-value 및 Pass-by-reference(inout 모드) 시멘틱스를 파라미터로 사용하여 구현합니다.포인터가 하위 프로그램으로 전송되고 실제 데이터는 전혀 복사되지 않습니다.단, 포인터는 메인 루틴의 데이터에 대한 액세스 패스이기 때문에 서브 프로그램은 메인 루틴의 데이터를 변경할 수 있다.C는 ALGOL68에서 이 방법을 채택했다.

C++에서의 파라미터 전달 실장: C++는 포인터와 참조 타입이라고 불리는 특수한 종류의 포인터를 사용하여 pass-by-reference(inout 모드) 의미도 실장합니다.참조 유형 포인터는 하위 프로그램 내에서 암묵적으로 참조되지만 의미론도 참조별로 참조됩니다.

여기서 중요한 개념은 데이터를 서브프로그램에 복사하는 대신 패스 바이 레퍼런스가 데이터에 대한 액세스 경로를 구현한다는 것입니다.데이터 액세스 경로는 명시적으로 참조된 포인터 또는 자동 참조된 포인터(참조 유형)가 될 수 있습니다.

자세한 내용은 제9장 제10판 로버트 세베스타의 '프로그래밍 언어의 개념'을 참조하십시오.

참조로 int를 전달하는 것이 아니라 값으로 int로의 포인터를 전달하는 것입니다.구문도 다르고 의미도 같아

값별로 포인터(주소 위치)를 전달하고 있습니다.

마치 "업데이트를 원하는 데이터가 여기에 있습니다."라고 말하는 것과 같습니다.

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

당신이 하고 있는 것은 참조가 아닌 가치로 패스하는 것입니다.변수 'p'의 값을 함수 'f'로 보내기 때문입니다(주로 f(p);).

C의 패스바이 레퍼런스와 같은 프로그램은 다음과 같습니다(!!!).이 프로그램은 C에서 pass-by-reference를 지원하지 않기 때문에 2개의 오류를 발생시킵니다.)

#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'f(a);
^

(Java 및 Javascript와 같이) 포인터를 참조라고 부르는 것은 pass-by-reference와 완전히 다른 단어입니다.C는 pass-by-reference를 지원하지 않습니다.다음은 참조로 값을 전달하는 것이 아니라 값별로 포인터만 전달한다는 것을 보여주기 위해 다시 작성된 예제입니다.

#include <stdio.h>

void f(int *j) {
  int k = (*j) + 1;
  j = &k;
}

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


  printf("i(ptr) = %p\n", &i);
  printf("j(ptr) = %p\n", p);


  return 0;
}

출력은 다음과 같습니다.

i = 20
j = 20
i(ptr) = 0x7ffdfddeee1c
j(ptr) = 0x7ffdfddeee1c

보시다시피 값은 그대로이지만 포인터도 변경되지 않습니다.다만, C++ 에서는, 참조의 패스 바이를 허가합니다.다음은 C++ 컴파일러를 사용한 동일한 예이지만 헤더에 앰퍼샌드를 추가하여 참조 파라미터로 만듭니다.

#include <stdio.h>

void f(int *&j) {   // note the & makes this a reference parameter.
                    // can't be done in C
  int k = (*j) + 1;
  j = &k;
}

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


  printf("i(ptr) = %p\n", &i);
  printf("j(ptr) = %p\n", p);


  return 0;
}

출력은 다음과 같습니다.

i = 20
j = 21
i(ptr) = 0x7ffcb8fc13fc
j(ptr) = 0x7ffcb8fc13d4

실제 포인터를 변경할 수 있었습니다!

참고로 드래곤북은 컴파일러에 관한 고전 컴퓨터 사이언스 교과서입니다.이 책은 지금까지 가장 인기 있는 컴파일러 책이기 때문에(혹은 적어도 제가 대학 다닐 때, 제가 틀렸을 수도 있습니다), 언어를 설계하거나 컴파일러를 쓰는 대부분의 사람들이 이 책에서 따랐다고 생각합니다.이 책의 1장에서는 이러한 개념을 매우 명확하게 설명하고 C가 값별로만 제공되는 이유를 설명합니다.

포인터와 참조는 2개의 다른 시그니처입니다.

내가 언급하지 않은 몇 가지 것들이 있다.

포인터는 무언가의 주소입니다.포인터는 다른 변수와 마찬가지로 저장 및 복사할 수 있습니다.따라서 크기가 있습니다.

참조는 어떤 ALIAS로 간주해야 합니다.크기가 없으므로 저장할 수 없습니다.반드시 참조해야 합니다. 즉, null이거나 변경할 수 없습니다.때때로 컴파일러는 참조를 포인터로 저장해야 하지만, 이는 구현 세부 사항입니다.

참조를 사용하면 소유권 처리, null 확인, 사용 시 참조 해제와 같은 포인터에 문제가 없습니다.

사실 C는 pass by reference를 지원한다고 생각합니다.

대부분의 언어에서는 통사설탕이 값이 아닌 참조로 전달되어야 합니다(예를 들어 파라미터 선언에서는 C++가 &를 필요로 합니다).

C는 또한 이것을 위해 통사당을 필요로 한다.파라미터 유형 선언에서는 *이고 인수에서는 &입니다.* 및 & 패스바이 레퍼런스의 C 구문입니다.

이제 실제 패스바이 레퍼런스는 파라미터 선언에만 구문을 요구해야 하며 인수 측에 구문을 요구해서는 안 된다고 주장할 수 있습니다.

그러나 이제 참조 전달에 의해 지원되며 파라미터와 인수 양쪽에 구문설탕이 필요한 C#이 출시되었습니다.

C에 by-ref 패싱이 없기 때문에 구문 요소가 C를 표현하는 것은 모든 구현에 어느 정도 적용되므로 전혀 논거가 되지 않습니다.

유일한 인수는 C에서 참조를 통과하는 것이 단일 기능이 아니라 기존의 2개의 기능을 결합하는 것입니다(인수의 ref by &, ref를 type by *). 예를 들어 C#에는 2개의 구문 요소가 필요하지만 서로 없이는 사용할 수 없습니다.

언어의 많은 다른 특징들이 다른 특징들로 구성되어 있기 때문에 이것은 분명히 위험한 논쟁입니다.(C++의 문자열 지원 등)

그 코드 프래그먼트(세세한 변경 포함)

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

C++에도 존재하며, 의미적으로는 와 동등합니다.

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

컴파일러는 함수의 동일한 이진 코드를 생성해야 합니다.add_number두 경우 모두.정수를 값으로 간주하면 해당 값은 참조를 통해 전달됩니다. 여기서 위쪽 패턴에서는 참조가 포인터로 나타납니다.

결론
C는 참조에 의한 인스턴스 전달의 의미를 지원합니다.
기술적으로는int *a합격하다*a(참고 자료입니다).

Pass by reference(포인터 사용)는 처음부터 C에 있었습니다.왜 아닐까요?

언급URL : https://stackoverflow.com/questions/2229498/passing-by-reference-in-c

반응형