Go에서 포인터를 사용하는 이유는 무엇입니까?


100

Go의 포인터가 함수 인수의 변형을 허용한다는 것을 알고 있지만 참조 (적절한 const 또는 가변 한정자 포함) 만 채택했다면 더 간단하지 않았을 것입니다. 이제 포인터와 맵 및 채널과 같은 일부 내장 유형에 대해 암시 적으로 참조로 전달합니다.

내가 뭔가를 놓치고 있거나 Go의 포인터가 불필요한 복잡성입니까?


1
다음은 명확히하는 데 도움이되는 질문입니다. stackoverflow.com/questions/795160/… 참조를 값으로 전달하는 것과 실제로 참조로 전달하는 것에는 차이가 있습니다.
— R. Martinho Fernandes

1
참고 : 문제는 Java에 관한 것이지만 여기에도 적용됩니다.
— R. Martinho Fernandes

1
"지도 및 채널과 같은 일부 내장 유형의 경우 암시 적으로 참조로 전달합니다." 아니요, Go에서는 모든 것이 가치에 의해 전달됩니다. 일부 유형은 내부 변경 가능 상태를 갖기 때문에 참조 유형입니다 (비공식적으로 설명 됨).
— newacct 2012

이 질문의 문제는 "참조"가 잘 정의 된 속성을 가진 단일 항목이 아니라는 것입니다. "참조"라는 용어는 매우 모호합니다. 우리는 답변에서 얼마나 많은 사람들이 "참조"라는 단어로 다른 것을 읽었는지 볼 수 있습니다. 따라서이 질문은 Go 포인터와 질문이 염두에두고있는 참조간에 정확히 어떤 차이점이 있는지 자세히 설명해야합니다.
— mtraceur

답변:


36

http://www.golang-book.com/8 에서 가져온 예를 정말 좋아합니다 .

func zero(x int) {
    x = 0
}
func main() {
    x := 5
    zero(x)
    fmt.Println(x) // x is still 5
}

대조적으로

func zero(xPtr *int) {
    *xPtr = 0
}
func main() {
    x := 5
    zero(&x)
    fmt.Println(x) // x is 0
}

42
질문은 "왜 우리는 참조 대신 포인터 를 가지고 있는가"였고이 예제가 참조와 함께 작동하지 않는 이유를 이해할 수 없습니다.
— AndreKR

@AndreKR 참조로 전달할 것인지 값으로 전달할 것인지 선택할 수 있기 때문입니다. 둘 다 바람직 할 수있는 몇 가지 경우가 있습니다.
— JDSweetBeat dec.

9
@DJMethaneMan "포인터 대 값에 의한 전달"이 아니라 "포인터 대 참조"입니다!
— AndreKR

부수적으로 C # 2.0에서는 "ref"키워드를 통해 참조에 의한 전달이 추가되었습니다. 우리는 포인터 포인터 포인터를 가질 수 있기 때문에 물론 포인터는 ... 아직 어떤 경우에 더 편리합니다
— 로비 팬

Go가 가장 쉬운 인기 언어 중 하나라는 것을 이해하지 못했지만 이러한 "기능"이 포함되어 있습니다. 적어도 여기에서 이것을 지적하는 사람들에게는 혼란스럽고 불필요 해 보입니다.
— Akito

33

포인터는 여러 가지 이유로 유용합니다. 포인터를 사용하면 메모리 레이아웃을 제어 할 수 있습니다 (CPU 캐시의 효율성에 영향을 미침). Go에서 모든 멤버가 연속 메모리에있는 구조를 정의 할 수 있습니다.

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

이 경우 Point구조체는 LineSegment구조체 내에 포함됩니다 . 그러나 항상 데이터를 직접 포함 할 수는 없습니다. 이진 트리 또는 연결 목록과 같은 구조를 지원하려면 일종의 포인터를 지원해야합니다.

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode
}

Java, Python 등은 복합 유형을 임베드 할 수 없기 때문에이 문제가 없으므로 임베딩과 포인팅을 구문 적으로 구분할 필요가 없습니다.

Go 포인터로 해결 된 Swift / C # 구조체 문제

이를 달성하기위한 가능한 대안을 구별하는 것이다 struct및 classC # 및 스위프트처럼. 그러나 여기에는 한계가 있습니다. 일반적으로 함수가 구조체를 inout매개 변수로 사용하여 구조체 복사를 방지하도록 지정할 수 있지만 구조체에 대한 참조 (포인터)를 저장할 수는 없습니다. 즉, 풀 할당자를 만드는 데 유용하다고 생각되면 구조체를 참조 유형으로 처리 할 수 ​​없습니다 (아래 참조).

커스텀 메모리 할당 자

포인터를 사용하여 고유 한 풀 할당자를 만들 수도 있습니다 (원칙을 보여주기 위해 많은 검사를 제거하여 매우 단순화 됨).

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode

  nextFreeNode *TreeNode; // For memory allocation
}

var pool [1024]TreeNode
var firstFreeNode *TreeNode = &pool[0] 

func poolAlloc() *TreeNode {
    node := firstFreeNode
    firstFreeNode  = firstFreeNode.nextFreeNode
    return node
}

func freeNode(node *TreeNode) {
    node.nextFreeNode = firstFreeNode
    firstFreeNode = node
}

두 값 바꾸기

포인터를 사용하면 swap. 그것은 두 변수의 값을 바꾸는 것입니다.

func swap(a *int, b *int) {
   temp := *a
   *a = *b
   *b = temp
}

결론

Java는 Google과 같은 곳에서 시스템 프로그래밍을위한 C ++를 완전히 대체 할 수 없었습니다. 부분적으로는 메모리 레이아웃 및 사용을 제어 할 수있는 능력이 없기 때문에 성능을 동일한 수준으로 조정할 수 없기 때문입니다 (캐시 누락은 성능에 상당한 영향을 미침). Go는 많은 영역에서 C ++를 대체하는 것을 목표로하고 있으므로 포인터를 지원해야합니다.


7
C #에서는 참조로 구조체를 전달할 수 있습니다. "ref"및 "out"키워드를 참조하십시오.
— olegz

1
좋아요, 그것은 Swift와 같습니다. 내 예를 업데이트하는 방법에 대해 생각할 것입니다.
— Erik Engheim

29

참조는 재 할당 할 수 없지만 포인터는 지정할 수 있습니다. 이것만으로도 참조를 사용할 수없는 많은 상황에서 포인터가 유용합니다.


17
참조를 재 할당 할 수 있는지 여부는 언어 별 구현 문제입니다.
— crantok

28

Go는 간결하고 미니멀 한 언어로 설계되었습니다. 따라서 값과 포인터로 시작되었습니다. 나중에 필요에 따라 일부 참조 유형 (슬라이스, 맵 및 채널)이 추가되었습니다.


Go 프로그래밍 언어 : 언어 디자인 FAQ : 배열이 값인 반면 맵, 슬라이스 및 채널이 참조되는 이유는 무엇입니까?

"이 주제에 대한 많은 역사가 있습니다. 초기에는 맵과 채널이 구문 상 포인터 였고 비 포인터 인스턴스를 선언하거나 사용하는 것이 불가능했습니다. 또한 배열이 작동하는 방식에 어려움을 겪었습니다. 결국 우리는 엄격한 분리를 결정했습니다. 포인터와 값의 증가로 인해 언어를 사용하기가 더 어려워졌습니다. 배열의 참조 형식을 처리하는 슬라이스를 포함한 참조 유형을 도입하여 이러한 문제를 해결했습니다. 참조 유형은 언어에 약간의 유감스러운 복잡성을 추가하지만 사용성에 큰 영향을 미칩니다. Go는 더 생산적이고 편안한 언어를 도입했습니다. "


빠른 컴파일은 Go 프로그래밍 언어의 주요 디자인 목표입니다. 비용이 있습니다. 피해 중 하나는 변수 (기본 컴파일 시간 상수 제외)와 매개 변수를 불변으로 표시하는 기능입니다. 요청되었지만 거절되었습니다.


golang-nuts : go language. 약간의 피드백과 의심.

"형 시스템에 const를 추가하면 모든 곳에 나타나도록 강제하고 무언가 변경되면 모든 곳에서 제거해야합니다. 어떤 식 으로든 불변 객체를 표시하는 데 약간의 이점이있을 수 있지만, const 유형 한정자는 제대로 작동하지 않는다고 생각합니다. 토고."


FWIW, Go의 "참조 유형"도 다시 할당 할 수 있습니다. 암시 적 포인터와 비슷합니까?
— Matt Joiner

1
포인터 (및 길이, 용량 등)를 포함하는 구조체의 특수 구문입니다.
— mk12 2013-07-15
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.