포인터를 사용하면 어떤 프로그래밍 문제를 가장 잘 해결할 수 있습니까? [닫은]


58

글쎄, 기본적으로 포인터를 사용하는 방법을 이해하지만 더 나은 프로그래밍을 위해 포인터를 사용하는 가장 좋은 방법은 아닙니다.

포인터를 사용하여 더 잘 이해할 수 있도록 해결해야 할 좋은 프로젝트 나 문제는 무엇입니까?


어떤 프로젝트 / 프로그램이 아닌 포인터를 사용하여 어떤 프로그래밍 문제를 가장 잘 해결할 수 있는지 물어보십시오. 포인터를 사용하거나 사용하지 않고 프로그램을 작성할 수 있습니다. 프로젝트 (프로젝트)는 의미있는 답변을하기에는 너무 큰 구성입니다.
Oded

37
프로그래밍 문제는 해결되지 않은 포인터를 사용하여 생성됩니다. :)
xpda

4
실제로, 포인터없이 어떤 문제를 해결할 수 있는지 궁금합니다. 확실하지 않습니다.
deadalnix

4
@deadalnix, 명시 적 포인터 또는 암시 적 포인터를 의미하는지 여부에 따라 다릅니다. 암시 적 포인터를 의미하는 경우 (즉, 어딘가에 사용되는 포인터가있는 경우) 스택 또는 힙을 사용하는 프로그램을 작성하는 것은 불가능합니다. 명시 적 포인터를 의미하는 경우 제약을받지 않으면 컴파일러에서 최적화 된 것으로 가정하고 새 스택 프레임 (기본적으로 함수 호출 사용)을 만들고 꼬리 호출을 사용하여 할당 해제하여 모든 크기의 할당을 수행 할 수 있습니다.
dan_waterworth

3
이 답변 중 일부는 끔찍합니다.

답변:


68

메모리에서 많은 양의 데이터를 조작하면 포인터가 실제로 빛을 발합니다.

참조로 큰 객체를 전달하는 것은 평범한 오래된 숫자를 전달하는 것과 같습니다. 객체를 복사하여 변경 한 다음 원본을 대신하여 사본을 다시 전달하는 대신 필요한 부분을 직접 조작 할 수 있습니다.


12
이것은 훌륭한 답변이며 대량의 데이터를 조작하기 위해 큰 객체가 필요하지 않다고 덧붙입니다. 때때로 큰 개체를 조작하면 문제가 해결되지만 더 일반적인 문제는 작은 개체를 실제로 자주 조작하는 것입니다. 당신이 그것에 대해 생각한다면, 그것은 모든 컴퓨터 프로그램에 관한 것입니다.
jhocking

44

포인터 개념을 사용하면 데이터 스토리지를 복제하지 않고도 주소별로 데이터를 참조 할 수 있습니다. 이 방법을 사용하면 다음과 같은 효율적인 알고리즘을 작성할 수 있습니다.

  1. 정렬
    정렬 알고리즘에서 데이터를 이동할 때 데이터 자체 대신 포인터를 움직일 수 있습니다. 100 자릿수의 문자열에서 수백만 행을 정렬하는 것을 생각하십시오. 불필요한 데이터 이동을 많이 저장합니다.

  2. 연결된 목록
    레코드와 관련된 전체 데이터가 아니라 다음 및 / 또는 이전 항목 위치를 저장할 수 있습니다.

  3. 전달 매개 변수이
    경우 데이터 자체 대신 데이터 주소를 전달합니다. 다시 말하지만, 수백만 행에서 실행되는 이름 압축 알고리즘을 생각해보십시오.

이 개념은 포인터가 외래 키 와 유사한 관계형 데이터베이스와 같은 데이터 구조로 확장 될 수 있습니다 . 일부 언어는 C # 및 COBOL과 같은 포인터 사용을 권장하지 않습니다.

다음과 같은 여러 장소에서 예를 찾을 수 있습니다.

다음 게시물은 어떤 식 으로든 관련 될 수 있습니다.


14
C #이 포인터 사용을 권장하지 않는다고 말하지는 않습니다. 대신 관리되지 않는 포인터 의 사용을 권장하지 않습니다 . C #에는 값 대신 참조로 전달되는 많은 것들이 있습니다.
Blakomen

좋은 설명, 좋은 설명
Sabby

5
C # 의견에 대해 공감해야했습니다. C # "참조"는 포인터와 동등합니다. C #은 실제로 관리되는 힙의 개체에 대한 포인터를 다루고 있다는 것을 알지 못하도록 추상화합니다.
MattDavey

1
@MattDavey : C # 참조는 포인터가 아닙니다. 그것들을 추가하거나, 그것들을 사용하여 배열을 인덱싱하고, 여러 간접 지시를 사용할 수 없습니다. 예를 들어 참조에 대한 참조를 가질 수 없습니다.
Billy ONeal

5
@ 빌리 "산술을 할 수 있다면 포인터 일 뿐이다"라는 솔직한 말이지 만, 나는 당신과 논쟁 할 인내심이 없다.
MattDavey

29

포인터를 사용하면 비 연속적이고 비선형 데이터 구조를 만들 수 있습니다. 여기서 요소는 여러 가지 다른 방식으로 복잡한 방식으로 관련 될 수 있습니다.

연결된 목록 (단일, 이중 및 원형으로 연결된), 트리 (빨간색, AVL, trie, 이진, 공간 분할…) 및 그래프는 단순한 값이 아닌 참조 측면에서 가장 자연스럽게 구성 할 수있는 구조의 예입니다. .


당신은 = 배타적 논리합 연결리스트를 [잊고있어
dan_waterworth

@dan_waterworth : 아니요. 그 흑 마법을 너무 잘 압니다.
존 퍼디

8

간단한 방법 중 하나는 다형성입니다. 다형성은 포인터에서만 작동합니다.

또한 동적 메모리 할당이 필요할 때마다 포인터를 사용합니다. C에서는 일반적으로 데이터를 배열에 저장해야하지만 컴파일 타임에 크기를 모르는 경우에 발생합니다. 그런 다음 malloc을 호출하여 메모리를 할당하고 포인터를 액세스합니다. 또한 알고 있는지 여부에 관계없이 배열을 사용할 때 포인터를 사용하고 있습니다.

for(int i = 0; i < size; i++)
   std::cout << array[i];

for(int i = 0; i < size; i++)
   std::cout << *(array + i);

이 지식을 통해 전체 어레이를 한 줄에 복사하는 것과 같은 멋진 작업을 수행 할 수 있습니다.

while( (*array1++ = *array2++) != '\0')

C ++에서는 new를 사용하여 객체에 메모리를 할당하고 포인터에 저장합니다. 컴파일 타임이 아닌 런타임 중에 객체를 생성해야 할 때마다이 작업을 수행합니다 (예 : 메소드가 새 객체를 생성하여 목록에 저장).

포인터를 더 잘 이해하려면 :

  1. 전통적인 c- 문자열 및 배열과 함께 작동하는 일부 프로젝트를 찾으십시오.
  2. 상속을 사용하는 일부 프로젝트를 찾으십시오.

  3. 이빨을 깎는 프로젝트는 다음과 같습니다.

파일에서 두 개의 nxn 행렬을 읽고 기본 벡터 공간 연산을 수행하고 결과를 화면에 인쇄합니다.

이렇게하려면 두 개의 배열 배열 (다차원 동적 배열)이 있으므로 동적 배열을 사용하고 포인터로 배열을 참조해야합니다. 해당 프로젝트를 완료 한 후에는 포인터를 사용하는 방법을 알 수 있습니다.


2
"다형성은 포인터에서만 작동합니다." 정확하지 않음 : 다형성도 참조와 함께 작동합니다. 궁극적으로 포인터이지만, 특히 초보자에게는 구별이 중요하다고 생각합니다.
Laurent Pireyn

한 줄에 배열을 복사하는 예는 실제로 일반 배열이 아닌 한 줄에 0으로 끝나는 문자열을 복사하는 것입니다.
tcrosley

8

포인터가 왜 중요한지 이해하려면 힙 할당과 스택 할당의 차이점을 이해해야합니다.

다음은 스택 할당의 예입니다.

struct Foo {
  int bar, baz
};

void foo(void) {
  struct Foo f;
}

스택에 할당 된 객체는 현재 함수 실행 기간 동안 만 존재합니다. 호출 foo이 범위를 벗어나면 변수도 마찬가지 f입니다.

이것이 문제가되는 한 가지 경우는 함수에서 정수 유형 이외의 것을 반환 해야하는 경우입니다 (예 : 위 예제의 Foo 구조).

예를 들어 다음 함수는 "정의되지 않은 동작"이라고합니다.

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  struct Foo f;
  return &f;
}

struct Foo *함수에서 와 같은 것을 반환 하려면 실제로 힙 할당이 필요합니다.

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  return malloc(sizeof(struct Foo));
}

이 함수 malloc는 힙에 객체를 할당하고 해당 객체에 대한 포인터를 반환합니다. 여기서 "객체"라는 용어는 느슨하게 사용되는데, 이는 객체 지향 프로그래밍의 의미에서 객체가 아닌 "무언가"를 의미합니다.

힙 할당 객체의 수명은 프로그래머가 제어합니다. 이 객체의 메모리는 프로그래머가 해제 할 때까지, 즉 호출 free()하거나 프로그램이 종료 될 때까지 예약 됩니다.

편집 :이 질문은 C ++ 질문으로 표시되지 않습니다. 연산자는 C ++ newnew[]같은 기능을 수행한다 malloc. 연산자 deletedelete[]유사합니다 free. 동안 newdelete++ 객체를 할당하고 무료 C에 독점적으로 사용되어야한다,의 사용 mallocfreeC ++ 코드에서 완벽하게 합법적이다.


1
또한, "고정 할당 변수"및 "동적 할당 변수"로 알려진 (+1); -
umlcat

4

사소한 프로젝트를 C로 작성하면 포인터를 사용하는 방법 /시기를 알아야합니다. C ++에서는 대부분 내부적으로 포인터를 관리하는 RAII 가능 객체를 사용하지만 C에서는 원시 포인터가 훨씬 더 널리 사용됩니다. 어떤 종류의 프로젝트를 해야하는지에 관해서는 사소한 것이 될 수 있습니다.

  • 작고 간단한 웹 서버
  • 유닉스 커맨드 라인 툴 (less, cat, sort 등)
  • 학습을 위해서가 아니라 자체적으로 수행하려는 실제 프로젝트

나는 마지막을 추천합니다.


그래서 나는 단지하고 싶은 프로젝트 (2D 게임처럼)를 위해 가고 포인터를 필요로하고 배우는 것으로 가정합니다.
dysoco

3
내 경험상, 당신의 기술 수준에 약간 미치지 못하는 실제 프로젝트를 수행하는 것이 배우는 가장 좋은 방법입니다. 작은 "장난감"프로그램을 읽고 쓰면 개념을 배울 수 있습니다. 그러나 이러한 개념을 사용하여 더 나은 프로그램을 작성하는 방법을 배우려면 장난감이 아닌 프로그램을 작성하면됩니다.
Viktor Dahl

3

포인터로 해결할 수있는 거의 모든 프로그래밍 문제는 다른 안전한 유형의 참조 로 해결할 수 있습니다 ( C ++ 참조를 참조 하지 않고 변수를 갖는 일반적인 CS 개념은 다른 곳에 저장된 데이터의 값을 나타냄).

메모리 주소를 직접 조작 할 수있는 특정 하위 수준의 참조 구현으로 인한 포인터 는 매우 강력하지만 사용하기에는 약간 위험 할 수 있습니다 (예 : 프로그램 외부의 메모리 위치).

포인터를 직접 사용하면 안전 점검을 수행하지 않아도 포인터가 약간 빨라진다는 이점이 있습니다. C 스타일 포인터를 직접 구현하지 않는 Java와 같은 언어는 약간의 성능 저하가 발생하지만 디버그하기 어려운 여러 유형을 줄입니다.

간접적으로 필요한 이유는 목록이 상당히 길지만 본질적으로 두 가지 핵심 아이디어는 다음과 같습니다.

  1. 큰 객체의 값을 복사하면 속도가 느리고 객체를 RAM에 두 번 (잠재적으로 비용이 많이 드는) 저장하게되지만 참조로 복사하면 주소에 대해 몇 바이트의 RAM 만 사용하여 거의 즉각적으로 복사 할 수 있습니다. 예를 들어, 메모리에 ~ 1000 개의 큰 객체 (각각 약 1MB의 RAM)가 있고 사용자가 현재 객체 (사용자가 수행 할)를 선택할 수 있어야한다고 가정하십시오. selected_object객체 중 하나에 대한 참조 인 변수 를 갖는 것이 현재 객체의 값을 새 변수에 복사하는 것보다 훨씬 효율적입니다.
  2. 연결된 목록 또는 트리와 같은 다른 개체를 참조하는 복잡한 데이터 구조가있는 경우 데이터 구조의 각 항목은 데이터 구조의 다른 항목을 나타냅니다. 데이터 구조에서 다른 항목을 참조하면 목록 중간에 새 항목을 삽입했기 때문에 목록의 모든 항목을 메모리에서 이동할 필요가 없습니다 (일정 시간 삽입 가능).

2

포인터를 사용하면 픽셀 수준 이미지 조작이 거의 항상 쉽고 빠릅니다. 때로는 포인터 만 사용하는 것이 가능합니다.


1
나는 "때로는 포인터를 사용하는 것만 가능하다"라는 말이 사실이라고 생각하지 않는다. 개발자에게 포인터를 노출시키지 않는 언어가 있지만 동일한 공간에서 문제를 해결하는 데 사용할 수 있습니다.
Thomas Owens

아마도 ... 사용 가능한 모든 언어에 대한 지식이 없지만 포인터가없는 언어를 사용하여 이미지에 컨볼 루션 필터를 적용하는 루틴을 작성하고 싶지는 않을 것입니다.
Dave Nay

@Thomas, 당신이 정확하지만, 질문은 개발자에게 포인터를 노출시키는 C ++에 관한 것입니다.
Jonathan Henson

@Jonathan 그럼에도 불구하고 포인터를 표시하지 않는 언어로 문제를 해결할 수 있다면 포인터를 사용하지 않고 포인터를 표시하는 언어로 문제를 해결할 수도 있습니다. 첫 번째 문장은 사실이지만 두 번째 문장은 거짓입니다. (내가 아는 한) 포인터를 사용하지 않고는 해결할 수없는 문제가 없습니다.
Thomas Owens

1
반대로, 이미지 처리에는 종종 "좌표 산술"이 필요합니다. 즉, 각도의 사인 및 코사인을 취하고 x 및 y 좌표와 곱하는 것입니다. 다른 경우 랩 어라운드 또는 경계를 벗어난 픽셀 복제가 필요합니다. 이런 점에서 포인터 산술은 다소 부적절합니다.
rwong

1

포인터는 사용자를 방해하지 않고 표면 아래의 많은 프로그래밍 언어로 사용됩니다. C / C ++는 그들에게 접근 권한을 제공합니다.

사용시기 : 데이터 복사가 비효율적이기 때문에 가능한 한 자주. 사용하지 않을 경우 : 개별적으로 변경할 수있는 두 개의 사본을 원할 때. (기본적으로 object_1의 내용을 메모리의 다른 위치로 복사하고 포인터를 반환하게됩니다-이번에는 object_2를 가리킴)


1

포인터는 C에서 데이터 구조 구현에 필수적인 부분이며 데이터 구조는 사소한 프로그램의 필수 부분입니다.

포인터가 왜 그렇게 중요한지 배우고 싶다면 연결된 목록이 무엇인지 배우고 포인터를 사용하지 않고 목록을 작성하는 것이 좋습니다. 나는 당신에게 불가능한 도전을 설정하지 않았습니다. (힌트 : 포인터는 메모리의 위치를 ​​참조하는 데 사용됩니다. 어레이에서 항목을 어떻게 참조합니까?)


데이터 구조를 언급하면 ​​+1, 알고리즘 사용은 자연스러운 결과입니다.
Matthieu M.

0

실생활의 예로서 제한 주문서를 작성하십시오.

예를 들어 ITCH 4.1 피드에는 가격 (및 우선 순위)이 변경 될 수있는 "대체"주문 개념이 있습니다. 주문을 받아서 다른 곳으로 옮길 수 있기를 원합니다. 포인터로 이중 엔드 큐를 구현하면 작업이 매우 쉽습니다.


0

다양한 StackExchange 사이트를 살펴보면 이와 같은 질문을하는 것이 오히려 모호하다는 것을 알고 있습니다. 비판과 폭식의 위험에 처해 나는 정직 할 것이다. 이것은 트롤이나 화염을 의미하는 것이 아니며, 질문에 대한 정직한 평가를 통해 도움을 줄 수 있습니다.

그리고 그 평가는 다음과 같습니다 : 이것은 C 프로그래머에게 물어 보는 매우 이상한 질문입니다. "나는 C를 모른다"는 것입니다. 좀 더 냉소적으로 분석한다면,이 "질문"에 대한 후속 조치가 있습니다 : "시간을 들이지 않고 경험이 풍부한 C 프로그래머의 지식을 신속하고 갑자기 습득 할 수있는 지름길이 있습니다. 독립적 인 연구를 위해? " 누군가가 줄 수있는 "응답"은 기본 개념과 그 사용을 이해하는 레거시를 수행하고 대신 할 수있는 대안이 아닙니다.

웹을 통해 사람들에게 물어 보는 것보다 C를 먼저 배우는 것이 더 건설적인 것 같습니다. C를 잘 알고 있으면 이와 같은 질문을하지 않아도 "칫솔을 사용하여 어떤 문제를 가장 잘 해결할 수 있습니까?"라고 묻는 것과 같습니다.


0

큰 메모리 객체로 작업하는 동안 포인터가 실제로 빛을 내더라도 포인터 없이도 동일한 방법으로 작업 할 수 있습니다.

포인터는 소위 동적 프로그래밍에 절대적으로 필요합니다. 프로그램을 실행하기 전에 필요한 메모리 양을 모르는 경우입니다. 동적 프로그래밍에서는 런타임 중에 메모리 청크를 요청하고 자신의 데이터를 그 안에 넣을 수 있습니다. 따라서 해당 데이터 청크와 함께 작동하려면 포인터 또는 참조 (여기에서 차이는 중요하지 않음)가 필요합니다.

런타임 동안 특정 메모리를 요구하고 새로 획득 한 메모리에 데이터를 저장하는 한 다음을 수행 할 수 있습니다.

  1. 자체 확장 데이터 구조를 가질 수 있습니다. 그것들은 용량이 소진되는 한 추가 메모리를 요구함으로써 스스로 확장 할 수있는 구조입니다. 모든 자체 확장 구조의 주요 특성은 작은 메모리 블록 (구조에 따라 노드, 목록 항목 등이라고 함)으로 구성되며 모든 블록에는 다른 블록에 대한 참조가 포함된다는 것입니다. 이 "연결된"구조는 그래프, 트리, 목록 등 대부분의 최신 데이터 유형을 만듭니다.

  2. OOP (Object-Oriented Programming) 패러다임을 사용하여 프로그래밍 할 수 있습니다. 전체 OOP는 직접 변수를 사용하지 않고 클래스 인스턴스 (객체라고 함)에 대한 참조 및 조작을 기반으로합니다. 포인터가없는 단일 인스턴스는 존재할 수 없습니다 (포인터가 없어도 정적 전용 클래스를 사용할 수는 있지만 예외 임).


0

웃긴, 방금 C ++에 대한 질문에 대답하고 포인터에 대해 이야기했습니다.

짧은 버전은 1) 사용중인 라이브러리가 당신을 강요하지 않는 한 포인터가 필요하지 않다는 것입니다 .2) nullable 참조가 필요합니다.

배열, 목록, 문자열 등이 필요하면 스택에 넣고 stl 객체를 사용하십시오. stl 객체를 반환하거나 전달하는 것은 객체 대신 포인터를 복사하는 내부 코드가 있고 데이터를 쓰면 데이터를 복사하기 때문에 빠르다 (확인되지 ​​않은 사실). 이것은 라이브러리 작성기에서 더 쉽게 만들 수있는 새로운 C ++ 11조차도 일반 C ++입니다.

귀하의 질문은이 부분에서 답변 될 수 있습니다

포인터를 사용하는 경우 다음 두 조건 중 하나에 있어야합니다. 1) 널 입력 가능 입력을 전달하고 있습니다. 예를 들어 선택적 파일 이름이 있습니다. 2) 소유권을 포기하려는 경우. 당신이 포인터를 전달하거나 반환하는 것처럼, 당신은 그것의 사본이 남아 있지 않거나 당신이주는 포인터를 사용하지 않습니다

ptr=blah; func(ptr); //never use ptr again for here on out.

그러나 포인터 또는 스마트 포인터를 오랫동안 사용하지 않았으며 응용 프로그램을 프로파일 링했습니다. 매우 빠르게 실행됩니다.

추가 참고 사항 : 나는 내 자신의 구조체를 작성하고 전달합니다. 포인터를 사용하지 않고 어떻게해야합니까? STL 컨테이너가 아니므로 참조를 통과하는 속도가 느립니다. 나는 항상 내 데이터 목록 / deques /지도 등을로드합니다. 일종의 목록 /지도가 아닌 한 객체를 반환하는 것을 기억하지 못합니다. 문자열조차도 아닙니다. 단일 객체에 대한 코드를 살펴본 결과 이와 같은 작업을 수행 { MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }하므로 객체를 여러 개 반환하거나 (단일) 참조로 미리 할당 / 전달합니다.

이제 글을 쓰고 있다면 자신의 deque, map 등을 사용해야합니다. 포인터를 사용해야합니다. 그러나 당신은 필요하지 않습니다. STL을 통해이를 염려 할 수 있습니다. 데이터와 솔루션 만 작성하면됩니다. 보관할 용기가 아님;)

나는 당신이 포인터를 사용하지 않기를 바랍니다 : D. 당신을 강요하는 libs를 다루는 행운을 빕니다


0

포인터는 메모리 매핑 장치 작업에 매우 유용합니다. 제어 레지스터를 반영하는 구조를 정의한 다음 메모리에있는 실제 제어 레지스터의 주소에 할당하고 직접 조작 할 수 있습니다. MMU가 시스템 메모리 공간에 매핑 한 경우 카드 나 칩의 전송 버퍼를 직접 가리킬 수도 있습니다.


0

포인터를 집게 손가락으로보고 몇 가지 작업을 수행하는 데 사용합니다.

  1. 누군가가 당신이 가지고 다닐 수없는 것을 요구할 때, 거리가 어디인지와 같이, 나는이 거리를 가리킬 것입니다.
  2. 무언가를 세거나 지나갈 때 우리는 같은 손가락을 사용할 것입니다. 그것이 우리가 배열에서하는 것입니다.

이 가난한 대답을 용서하십시오


0

귀하의 질문에 C ++ 태그가 지정되어 있으므로 해당 언어에 대한 귀하의 질문에 답변 해 드리겠습니다.

C ++에는 포인터와 참조가 구분되므로 특정 동작을 용이하게하기 위해 포인터 (또는 스마트 포인터)가 필요한 두 가지 시나리오가 있습니다. 그것들은 다른 상황에서 사용될 수 있지만, "사용하기 가장 좋은 방법"을 묻고 다른 상황에서는 더 나은 대안이 있습니다.

1. 다형성

기본 클래스 포인터를 사용하면 포인터가 가리키는 객체 유형에 따라 가상 메서드를 호출 할 수 있습니다.

2. 영속 객체 생성

스택 대신 힙에 객체를 동적으로 만들 때 포인터가 필요합니다. 객체 수명이 생성 된 범위보다 길어질 때 필요합니다.

다른 사람들이 이미 언급했듯이 "좋은 프로젝트 또는 해결해야 할 문제"와 관련하여 사소한 프로젝트는 포인터를 사용합니다.


어쩌면 누군가 포인터를 사용하는 것보다 객체를 슬라이스하는 것을 선호 할 것입니다. 해피 디버깅 : D
deadalnix
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.