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

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

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



답변

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
}


답변

포인터는 여러 가지 이유로 유용합니다. 포인터를 사용하면 메모리 레이아웃을 제어 할 수 있습니다 (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 # 구조체 문제

이를 달성하기위한 가능한 대안을 구별하는 것이다 structclassC # 및 스위프트처럼. 그러나 여기에는 한계가 있습니다. 일반적으로 함수가 구조체를 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 ++를 대체하는 것을 목표로하고 있으므로 포인터를 지원해야합니다.


답변

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


답변

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


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

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


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


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

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


답변