이 글에서는 Go에 참조에 의한 매개변수 전달(C++과 비교)이 있는지 여부를 주로 소개합니다. 필요한 친구는
C++
값 전달:
가장 일반적입니다. 매개변수 전달 방법 함수의 형식 매개변수는 실제 매개변수의 복사본입니다. 함수 내에서 형식 매개변수를 변경해도 함수 외부의 형식 매개변수에는 영향을 미치지 않습니다. 일반적으로 값 전달은 호출자에게 영향을 주지 않고 함수 내의 매개변수를 수정할 때 사용됩니다.
포인터 전달
형식 매개변수는 실제 매개변수의 주소를 가리키는 포인터입니다. 이름에서 알 수 있듯이 형식 매개변수가 가리키는 내용이 함수 내에서 조작되면 실제 매개변수 자체가 수정됩니다. .
참조로 전달
C++에서 참조는 변수의 별칭이며 실제로는 동일하며 메모리의 동일한 주소에도 존재합니다. 즉, 참조가 조작될 때마다 참조된 변수가 매우 직접적으로 조작됩니다.
아래 데모를 보세요:
#include <iostream> //值传递 void func1(int a) { std::cout << "值传递,变量地址:" << &a << ", 变量值:" << a << std::endl; a ++ ; } //指针传递 void func2 (int* a) { std::cout << "指针传递,变量地址:" << a << ", 变量值:" << *a << std::endl; *a = *a + 1; } //引用传递 void func3 (int& a) { std::cout << "指针传递,变量地址:" << &a << ", 变量值:" << a << std::endl; a ++; } int main() { int a = 5; std::cout << "变量实际地址:" << &a << ", 变量值:" << a << std::endl; func1(a); std::cout << "值传递操作后,变量值:" << a << std::endl; std::cout << "变量实际地址:" << &a << ", 变量值:" << a << std::endl; func2(&a); std::cout << "指针传递操作后,变量值:" << a << std::endl; std::cout << "变量实际地址:" << &a << ", 变量值:" << a << std::endl; func3(a); std::cout << "引用传递操作后,变量值:" << a << std::endl; return 0; }
출력 결과는 다음과 같습니다.
변수 실제 주소: 0x28feac, 변수 값: 5
값 전송, 변수 주소: 0x28fe90, 변수 값: 5
값 이후 전송 연산, 변수 값: 5
변수의 실제 주소: 0x28feac, 변수 값: 5
포인터 전송, 변수 주소: 0x28feac, 변수 값: 5
포인터 전송 연산 후, 변수 값: 6
변수의 실제 주소: 0x28feac , 변수값: 6
포인터 전송, 변수 주소: 0x28feac, 변수값: 6
참조 전송 연산 후, 변수값: 7
Go에서 전달되는 매개변수
위에서는 3가지 매개변수 전달 방법을 소개합니다. C++, 값 전달 및 포인터 전달은 이해하기 쉽습니다. 그렇다면 Go에도 이러한 매개변수 전달 방법이 있습니까? 이는 논란을 불러일으켰지만 C++의 참조 전달 개념과 비교하면 Go에는 참조 전달 방법이 없다고 말할 수 있습니다. 왜 내가 이런 말을 하는가? Go에는 변수 참조라는 개념이 없기 때문이다. 하지만 Go에는 참조 유형이 있는데 이에 대해서는 나중에 설명하겠습니다.
먼저 Go에서 값과 포인터를 전달하는 예를 살펴보겠습니다:
package main import ( "fmt" ) func main() { a := 1 fmt.Println( "变量实际地址:", &a, "变量值:", a) func1 (a) fmt.Println( "值传递操作后,变量值:", a) fmt.Println( "变量实际地址:", &a, "变量值:", a) func2(&a) fmt.Println( "指针传递操作后,变量值:", a) } //值传递 func func1 (a int) { a++ fmt.Println( "值传递,变量地址:", &a, "变量值:", a) } //指针传递 func func2 (a *int) { *a = *a + 1 fmt.Println( "指针传递,变量地址:", a, "变量值:", *a) }
출력 결과는 다음과 같습니다.
변수의 실제 주소: 0xc04203c1d0 변수 값: 1
값 전송, 변수 주소: 0xc04203c210 변수 값: 2
값 전송 연산 후 변수 값: 1
변수 실제 주소: 0xc04203c1d0 변수 값: 1
포인터 전송, 변수 주소: 0xc04203c1d0 변수 값: 2
포인터 전송 연산 후 변수 값: 2
될 수 있습니다. Go 기본 유형이 값으로 전달되는 것을 볼 수 있습니다. 포인터 전달은 C++와 다르지 않지만 변수 참조 개념이 없습니다. 그렇다면 Go의 참조 유형을 어떻게 이해합니까?
Go의 참조 유형
Go에서 참조 유형에는 슬라이스, 사전, 채널 등이 포함됩니다. 슬라이싱을 예로 들어보겠습니다. 슬라이스를 참조로 전달합니까?
예:
package main import ( "fmt" ) func main() { m1 := make([]string, 1) m1[0] = "test" fmt.Println("调用 func1 前 m1 值:", m1) func1(m1) fmt.Println("调用 func1 后 m1 值:", m1) } func func1 (a []string) { a[0] = "val1" fmt.Println("func1中:", a) }
출력 결과는 다음과 같습니다.
func1 호출 전 m1 값: [test]
func1: [val1]
func1 호출 후 m1 값: [ val1]
함수에서 슬라이스에 대한 수정 사항은 실제 매개변수 값에 영향을 미칩니다. 이것이 참조에 의한 전달이라는 뜻인가요?
사실 그렇지 않습니다. 이 질문에 대답하려면 먼저 호출 함수 슬라이스 m1이 변경되었는지 확인해야 합니다. 먼저 슬라이싱의 본질을 이해해야 합니다.
슬라이스(Slice)는 배열 조각에 대한 설명입니다. 여기에는 조각의 길이인 배열에 대한 포인터가 포함되어 있습니다.
즉, 위에서 인쇄한 것은 슬라이스 자체가 아니라 슬라이스가 가리키는 배열입니다. 또 다른 예로 슬라이스가 변경되었는지 확인합니다.
package main import ( "fmt" ) func main() { m1 := make([]string, 1) m1[0] = "test" fmt.Println("调用 func1 前 m1 值:", m1, cap(m1)) func1(m1) fmt.Println("调用 func1 后 m1 值:", m1, cap(m1)) } func func1 (a []string) { a = append(a, "val1") fmt.Println("func1中:", a, cap(a)) }
출력 결과는 다음과 같습니다.
func1 호출 전 m1 값: [test] 1
func1: [test val1] 2
func1 호출 전 m1 값: [test] 1
이 결과는 통화 전후에 슬라이스가 변경되지 않았음을 보여줍니다. 앞의 예에서 말하는 "변경"은 실제로 슬라이스의 배열에 대한 포인터가 가리키는 배열의 요소가 변경된 것입니다. 이 문장은 다소 어색할 수 있지만 실제로는 그렇습니다. 참조 유형의 매개변수 전달이 참조별 전달이 아님을 다시 한 번 증명합니다.
슬라이스란 배열 조각에 대한 설명입니다. 여기에는 배열에 대한 포인터와 조각의 길이가 포함되어 있습니다. 관심이 있으시면 http://www.jb51.net/kf/201604/499045.html 문서를 읽어보세요. 슬라이싱의 메모리 모델에 대해 알아보세요.
Summary
요약은 아주 간단하고, 언어도 현상을 꿰뚫어 봐야 본질을 볼 수 있습니다. 기억해야 할 이 글의 결론도 있습니다:
Go에는 참조에 의한 전달이 없습니다.
위 내용은 Go 언어 비교 C++ 참조 매개변수 전달의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!