글쎄, 기본적으로 포인터를 사용하는 방법을 이해하지만 더 나은 프로그래밍을 위해 포인터를 사용하는 가장 좋은 방법은 아닙니다.
포인터를 사용하여 더 잘 이해할 수 있도록 해결해야 할 좋은 프로젝트 나 문제는 무엇입니까?
글쎄, 기본적으로 포인터를 사용하는 방법을 이해하지만 더 나은 프로그래밍을 위해 포인터를 사용하는 가장 좋은 방법은 아닙니다.
포인터를 사용하여 더 잘 이해할 수 있도록 해결해야 할 좋은 프로젝트 나 문제는 무엇입니까?
답변:
메모리에서 많은 양의 데이터를 조작하면 포인터가 실제로 빛을 발합니다.
참조로 큰 객체를 전달하는 것은 평범한 오래된 숫자를 전달하는 것과 같습니다. 객체를 복사하여 변경 한 다음 원본을 대신하여 사본을 다시 전달하는 대신 필요한 부분을 직접 조작 할 수 있습니다.
포인터 개념을 사용하면 데이터 스토리지를 복제하지 않고도 주소별로 데이터를 참조 할 수 있습니다. 이 방법을 사용하면 다음과 같은 효율적인 알고리즘을 작성할 수 있습니다.
정렬
정렬 알고리즘에서 데이터를 이동할 때 데이터 자체 대신 포인터를 움직일 수 있습니다. 100 자릿수의 문자열에서 수백만 행을 정렬하는 것을 생각하십시오. 불필요한 데이터 이동을 많이 저장합니다.
연결된 목록
레코드와 관련된 전체 데이터가 아니라 다음 및 / 또는 이전 항목 위치를 저장할 수 있습니다.
전달 매개 변수이
경우 데이터 자체 대신 데이터 주소를 전달합니다. 다시 말하지만, 수백만 행에서 실행되는 이름 압축 알고리즘을 생각해보십시오.
이 개념은 포인터가 외래 키 와 유사한 관계형 데이터베이스와 같은 데이터 구조로 확장 될 수 있습니다 . 일부 언어는 C # 및 COBOL과 같은 포인터 사용을 권장하지 않습니다.
다음과 같은 여러 장소에서 예를 찾을 수 있습니다.
다음 게시물은 어떤 식 으로든 관련 될 수 있습니다.
포인터를 사용하면 비 연속적이고 비선형 데이터 구조를 만들 수 있습니다. 여기서 요소는 여러 가지 다른 방식으로 복잡한 방식으로 관련 될 수 있습니다.
연결된 목록 (단일, 이중 및 원형으로 연결된), 트리 (빨간색, AVL, trie, 이진, 공간 분할…) 및 그래프는 단순한 값이 아닌 참조 측면에서 가장 자연스럽게 구성 할 수있는 구조의 예입니다. .
간단한 방법 중 하나는 다형성입니다. 다형성은 포인터에서만 작동합니다.
또한 동적 메모리 할당이 필요할 때마다 포인터를 사용합니다. 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를 사용하여 객체에 메모리를 할당하고 포인터에 저장합니다. 컴파일 타임이 아닌 런타임 중에 객체를 생성해야 할 때마다이 작업을 수행합니다 (예 : 메소드가 새 객체를 생성하여 목록에 저장).
포인터를 더 잘 이해하려면 :
상속을 사용하는 일부 프로젝트를 찾으십시오.
이빨을 깎는 프로젝트는 다음과 같습니다.
파일에서 두 개의 nxn 행렬을 읽고 기본 벡터 공간 연산을 수행하고 결과를 화면에 인쇄합니다.
이렇게하려면 두 개의 배열 배열 (다차원 동적 배열)이 있으므로 동적 배열을 사용하고 포인터로 배열을 참조해야합니다. 해당 프로젝트를 완료 한 후에는 포인터를 사용하는 방법을 알 수 있습니다.
포인터가 왜 중요한지 이해하려면 힙 할당과 스택 할당의 차이점을 이해해야합니다.
다음은 스택 할당의 예입니다.
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 ++ new
와 new[]
같은 기능을 수행한다 malloc
. 연산자 delete
와 delete[]
유사합니다 free
. 동안 new
과 delete
++ 객체를 할당하고 무료 C에 독점적으로 사용되어야한다,의 사용 malloc
및 free
C ++ 코드에서 완벽하게 합법적이다.
사소한 프로젝트를 C로 작성하면 포인터를 사용하는 방법 /시기를 알아야합니다. C ++에서는 대부분 내부적으로 포인터를 관리하는 RAII 가능 객체를 사용하지만 C에서는 원시 포인터가 훨씬 더 널리 사용됩니다. 어떤 종류의 프로젝트를 해야하는지에 관해서는 사소한 것이 될 수 있습니다.
나는 마지막을 추천합니다.
포인터로 해결할 수있는 거의 모든 프로그래밍 문제는 다른 안전한 유형의 참조 로 해결할 수 있습니다 ( C ++ 참조를 참조 하지 않고 변수를 갖는 일반적인 CS 개념은 다른 곳에 저장된 데이터의 값을 나타냄).
메모리 주소를 직접 조작 할 수있는 특정 하위 수준의 참조 구현으로 인한 포인터 는 매우 강력하지만 사용하기에는 약간 위험 할 수 있습니다 (예 : 프로그램 외부의 메모리 위치).
포인터를 직접 사용하면 안전 점검을 수행하지 않아도 포인터가 약간 빨라진다는 이점이 있습니다. C 스타일 포인터를 직접 구현하지 않는 Java와 같은 언어는 약간의 성능 저하가 발생하지만 디버그하기 어려운 여러 유형을 줄입니다.
간접적으로 필요한 이유는 목록이 상당히 길지만 본질적으로 두 가지 핵심 아이디어는 다음과 같습니다.
selected_object
객체 중 하나에 대한 참조 인 변수 를 갖는 것이 현재 객체의 값을 새 변수에 복사하는 것보다 훨씬 효율적입니다.포인터를 사용하면 픽셀 수준 이미지 조작이 거의 항상 쉽고 빠릅니다. 때로는 포인터 만 사용하는 것이 가능합니다.
포인터는 C에서 데이터 구조 구현에 필수적인 부분이며 데이터 구조는 사소한 프로그램의 필수 부분입니다.
포인터가 왜 그렇게 중요한지 배우고 싶다면 연결된 목록이 무엇인지 배우고 포인터를 사용하지 않고 목록을 작성하는 것이 좋습니다. 나는 당신에게 불가능한 도전을 설정하지 않았습니다. (힌트 : 포인터는 메모리의 위치를 참조하는 데 사용됩니다. 어레이에서 항목을 어떻게 참조합니까?)
다양한 StackExchange 사이트를 살펴보면 이와 같은 질문을하는 것이 오히려 모호하다는 것을 알고 있습니다. 비판과 폭식의 위험에 처해 나는 정직 할 것이다. 이것은 트롤이나 화염을 의미하는 것이 아니며, 질문에 대한 정직한 평가를 통해 도움을 줄 수 있습니다.
그리고 그 평가는 다음과 같습니다 : 이것은 C 프로그래머에게 물어 보는 매우 이상한 질문입니다. "나는 C를 모른다"는 것입니다. 좀 더 냉소적으로 분석한다면,이 "질문"에 대한 후속 조치가 있습니다 : "시간을 들이지 않고 경험이 풍부한 C 프로그래머의 지식을 신속하고 갑자기 습득 할 수있는 지름길이 있습니다. 독립적 인 연구를 위해? " 누군가가 줄 수있는 "응답"은 기본 개념과 그 사용을 이해하는 레거시를 수행하고 대신 할 수있는 대안이 아닙니다.
웹을 통해 사람들에게 물어 보는 것보다 C를 먼저 배우는 것이 더 건설적인 것 같습니다. C를 잘 알고 있으면 이와 같은 질문을하지 않아도 "칫솔을 사용하여 어떤 문제를 가장 잘 해결할 수 있습니까?"라고 묻는 것과 같습니다.
큰 메모리 객체로 작업하는 동안 포인터가 실제로 빛을 내더라도 포인터 없이도 동일한 방법으로 작업 할 수 있습니다.
포인터는 소위 동적 프로그래밍에 절대적으로 필요합니다. 프로그램을 실행하기 전에 필요한 메모리 양을 모르는 경우입니다. 동적 프로그래밍에서는 런타임 중에 메모리 청크를 요청하고 자신의 데이터를 그 안에 넣을 수 있습니다. 따라서 해당 데이터 청크와 함께 작동하려면 포인터 또는 참조 (여기에서 차이는 중요하지 않음)가 필요합니다.
런타임 동안 특정 메모리를 요구하고 새로 획득 한 메모리에 데이터를 저장하는 한 다음을 수행 할 수 있습니다.
자체 확장 데이터 구조를 가질 수 있습니다. 그것들은 용량이 소진되는 한 추가 메모리를 요구함으로써 스스로 확장 할 수있는 구조입니다. 모든 자체 확장 구조의 주요 특성은 작은 메모리 블록 (구조에 따라 노드, 목록 항목 등이라고 함)으로 구성되며 모든 블록에는 다른 블록에 대한 참조가 포함된다는 것입니다. 이 "연결된"구조는 그래프, 트리, 목록 등 대부분의 최신 데이터 유형을 만듭니다.
OOP (Object-Oriented Programming) 패러다임을 사용하여 프로그래밍 할 수 있습니다. 전체 OOP는 직접 변수를 사용하지 않고 클래스 인스턴스 (객체라고 함)에 대한 참조 및 조작을 기반으로합니다. 포인터가없는 단일 인스턴스는 존재할 수 없습니다 (포인터가 없어도 정적 전용 클래스를 사용할 수는 있지만 예외 임).
웃긴, 방금 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를 다루는 행운을 빕니다
귀하의 질문에 C ++ 태그가 지정되어 있으므로 해당 언어에 대한 귀하의 질문에 답변 해 드리겠습니다.
C ++에는 포인터와 참조가 구분되므로 특정 동작을 용이하게하기 위해 포인터 (또는 스마트 포인터)가 필요한 두 가지 시나리오가 있습니다. 그것들은 다른 상황에서 사용될 수 있지만, "사용하기 가장 좋은 방법"을 묻고 다른 상황에서는 더 나은 대안이 있습니다.
기본 클래스 포인터를 사용하면 포인터가 가리키는 객체 유형에 따라 가상 메서드를 호출 할 수 있습니다.
스택 대신 힙에 객체를 동적으로 만들 때 포인터가 필요합니다. 객체 수명이 생성 된 범위보다 길어질 때 필요합니다.
다른 사람들이 이미 언급했듯이 "좋은 프로젝트 또는 해결해야 할 문제"와 관련하여 사소한 프로젝트는 포인터를 사용합니다.