왜 증분 포인터?


25

나는 최근에 C ++을 배우기 시작했으며, 대부분의 사람들이 (내가 읽은 것에 따르면) 포인터로 고심하고 있습니다.

전통적인 의미가 아니기 때문에 나는 그것이 무엇인지, 왜 사용되는지, 어떻게 유용 할 수 있는지 이해하지만, 포인터를 증가시키는 것이 어떻게 유용한 지 이해할 수 없습니다. 누구든지 포인터를 증가시키는 방법에 대한 설명을 제공 할 수 있습니까 유용한 개념과 관용적 C ++?

이 질문은 Bjarne Stroustrup 의 C ++ 여행 이라는 책을 읽은 후에 나왔습니다. Java에 익숙하기 때문에이 책을 추천 받았으며 Reddit의 사람들은 좋은 '전환'책이라고 말했습니다. .


11
포인터는 반복자입니다
찰스 샐비어

1
읽지 말아야 할 것을 읽는 컴퓨터 바이러스를 작성하는 가장 좋아하는 도구 중 하나입니다. 앱에서 가장 일반적인 취약점 중 하나이기도합니다 (예상 영역을지나 포인터를 증가시키고 읽거나 쓰는 경우)> HeartBleed 버그를 참조하십시오.
Sam

1
@vasile 이것은 포인터에 대한 나쁜 점입니다.
Cruncher

4
C ++의 장점 / 나쁜 점은 segfault를 호출하기 전에 훨씬 더 많은 일을 할 수 있다는 것입니다. 일반적으로 다른 프로세스의 메모리, 시스템 메모리 또는 보호 된 앱 메모리에 액세스하려고 할 때 segfault가 발생합니다. 일반적인 응용 프로그램 페이지 내에서의 모든 액세스는 시스템에서 허용되며 합리적인 제한을 적용하는 것은 프로그래머 / 컴파일러 / 언어에 달려 있습니다. C ++을 사용하면 원하는대로 할 수 있습니다. 자체 메모리 관리자가있는 openssl은 사실이 아닙니다. 기본 C ++ 메모리 액세스 메커니즘 만 있습니다.
Sam

1
@INdek : 액세스하려는 메모리가 보호되어있는 경우에만 segfault를 얻게됩니다. 대부분의 운영 체제는 페이지 수준에서 보호 기능을 할당하므로 일반적으로 포인터가 시작하는 페이지의 모든 항목에 액세스 할 수 있습니다. OS가 4K 페이지 크기를 사용하는 경우 상당한 양의 데이터입니다. 포인터가 힙 어딘가에서 시작하면 액세스 할 수있는 데이터 양이 누구인지 추측합니다.
TMN

답변:


46

배열이 있으면 배열의 요소를 가리 키도록 포인터를 설정할 수 있습니다.

int a[10];
int *p = &a[0];

이곳까지 p의 첫번째 요소 점 a이다 a[0]. 이제 다음 요소를 가리 키도록 포인터를 증가시킬 수 있습니다.

p++;

이제 p두 번째 요소 인을 가리 킵니다 a[1]. 를 사용하여 여기에서 요소에 액세스 할 수 있습니다 *p. 이것은 배열 요소에 액세스하기 위해 정수 색인 변수를 사용해야하는 Java와 다릅니다.

그 포인터에 않는 C ++ 포인터 증가 하지 배열의 요소를 가리 것은 정의되지 않은 동작 .


23
그러나 C ++을 사용하면 배열 경계 외부의 액세스와 같은 프로그래밍 오류를 피할 책임이 있습니다.
그렉 휴길

9
아니요, 배열 요소 이외 의 항목을 가리키는 포인터를 증가시키는 것은 정의되지 않은 동작입니다. 그러나 휴대 성이 아닌 낮은 수준의 작업을 수행하는 경우 포인터를 늘리는 것은 메모리의 다음 항목에 액세스하는 것 이상입니다.
Greg Hewgill

4
배열이거나 처리 될 수있는 몇 가지가 있습니다. 텍스트 문자열은 실제로 문자 배열입니다. 경우에 따라 long int는 바이트 배열로 취급되지만 쉽게 문제를 일으킬 수 있습니다.
AMADANON Inc.

6
type을 알려주지 만 동작 은 5.7 추가 연산자 [expr.add]에 설명되어 있습니다. 특히, 5.7 / 5는 과거 한 쪽 끝을 제외하고 배열 외부로가는 것이 UB라고 말합니다.
쓸모없는

4
마지막 단락은 다음과 같습니다. 포인터 피연산자와 결과가 동일한 배열 객체의 요소를 가리키는 경우 평가시 오버플로가 발생하지 않습니다. 그렇지 않으면 동작이 정의되지 않습니다 . 따라서 결과가 배열에 없거나 끝을 지나치지 않으면 UB를 얻습니다.
쓸모없는

37

포인터의 의미는 (알렉산더 스테파노 프의의 기반으로 C ++ 표준 라이브러리의 뒤에 디자인 철학의 기본적인 측면 반영하기 때문에 포인터를 증가 시켜도, 관용적 C ++입니다 STL를 )

여기서 중요한 개념은 STL이 컨테이너, 알고리즘 및 반복자를 중심으로 설계되었다는 것입니다. 포인터는 단순히 반복자 입니다.

물론, 포인터를 증가 (또는 더하기 / 빼기)하는 기능은 C로 돌아갑니다. 포인터 연산을 사용하여 많은 C- 문자열 조작 알고리즘을 간단히 작성할 수 있습니다. 다음 코드를 고려하십시오.

char string1[4] = "abc";
char string2[4];
char* src = string1;
char* dest = string2;
while ((*dest++ = *src++));

이 코드는 포인터 산술을 사용하여 널 종료 C- 문자열을 복사합니다. 루프는 null이 발생하면 자동으로 종료됩니다.

C ++에서는 포인터 시맨틱이 반복자 개념으로 일반화됩니다 . 대부분의 표준 C ++ 컨테이너는 반복자를 제공하며 이는 beginand end함수 를 통해 액세스 할 수 있습니다 . 반복자는 포인터처럼 작동하며, 증분, 역 참조, 때로는 감소 또는 진행될 수 있습니다.

를 반복하기 위해 다음 std::string과 같이 말합니다.

std::string s = "abcdef";
std::string::iterator it = s.begin();
for (; it != s.end(); ++it) std::cout << *it;

평범한 C- 문자열에 대한 포인터를 증가시키는 것처럼 반복자를 증가시킵니다. 이 개념이 강력한 이유는 템플릿을 사용 하여 필요한 개념 요구 사항을 충족하는 모든 유형의 반복자에 대해 작동하는 함수를 작성할 수 있기 때문 입니다. 그리고 이것이 STL의 힘입니다.

std::string s1 = "abcdef";
std::vector<char> buf;
std::copy(s1.begin(), s1.end(), std::back_inserter(buf));

이 코드는 문자열을 벡터에 복사합니다. 이 copy함수는 증분 (일반 포인터 포함)을 지원 하는 모든 반복자 와 함께 작동하는 템플릿입니다 . copy평범한 C- 문자열 에서 동일한 기능을 사용할 수 있습니다 .

   const char* s1 = "abcdef";
   std::vector<char> buf;
   std::copy(s1, s1 + std::strlen(s1), std::back_inserter(buf));

우리 는 반복자를 지원 하는 하나 또는 모든 사용자 정의 컨테이너 copy에서 사용할 수 있습니다 . std::mapstd::set

포인터는 특정 유형의 반복자 인 random access iterator 라는 점에 유의하십시오. 즉 , +and -연산자로 증가, 감소 및 전진을 지원합니다 . 다른 반복자 유형은 포인터 의미론의 하위 집합 만 지원합니다. 양방향 반복기 는 적어도 증분 및 감소를 지원합니다. 앞으로 반복자 지원은 적어도 증가. (모든 반복자 유형은 역 참조를 지원합니다.)이 copy함수에는 최소한 증분을 지원하는 반복자가 필요합니다.

여기에서 다양한 반복기 개념에 대해 읽을 수 있습니다 .

따라서 포인터 증가는 C- 어레이를 반복하거나 C- 어레이의 요소 / 오프셋에 액세스하는 관용적 인 C ++ 방식입니다.


3
첫 번째 예제에서와 같이 포인터를 사용하지만 반복자로 생각하지는 않았지만 이제는 의미가 있습니다.
염료 염료

1
"null이 발생하면 루프가 자동으로 종료됩니다." 이것은 끔찍한 관용구입니다.
Charles Wood

9
@CharlesWood, 그렇다면 당신은 C가 꽤 끔찍한 것을 발견해야한다고 생각합니다
Siler

7
@CharlesWood : 대안은 문자열의 길이를 루프 제어 변수로 사용하는 것입니다. 이는 문자열을 두 번 순회하는 것을 의미합니다 (한 번은 길이를 결정하고 한 번은 문자를 복사 함). 1MHz PDP-7에서 실행하면 실제로 더해질 수 있습니다.
TMN

3
@INdek : 우선, C와 C ++는 중요한 변경을 피하기 위해 모든 비용을 피하려고 노력합니다. 그리고 문자열 리터럴의 기본 동작을 변경하는 것은 상당히 수정 될 것이라고 말합니다. 그러나 가장 중요한 것은 0으로 끝나는 문자열은 단지 규칙입니다 (문자열 리터럴은 기본적으로 0으로 끝나고 라이브러리 함수는 그것을 기대한다는 사실에 따라 쉽게 따라갈 수 있습니다). 아무도 C에서 카운트 된 문자열을 사용하지 못하게합니다. 여러 C 라이브러리가이를 사용합니다 (예 : OLE의 BSTR 참조).
Matteo Italia

16

포인터 산술은 C에 있기 때문에 C ++에 있습니다. 포인터 산술은 C에 있습니다. 어셈블러 의 일반적인 관용구이기 때문 입니다.

"증가 레지스터"가 "상수 값 1을로드하고 레지스터에 추가"보다 빠른 시스템이 많이 있습니다. 더욱이, 몇몇 시스템에서는 단일 명령어로 "레지스터 B에 지정된 주소에서 DWORD를 A에로드 한 다음 sizeof (DWORD)를 B에 추가"할 수 있습니다. 요즘에는 최적화 컴파일러가 이것을 정렬 할 것으로 기대할 수 있지만 1973 년에는 실제로 옵션이 아니 었습니다.

이것은 기본적으로 C 배열이 경계 검사되지 않고 C 문자열에 크기가 포함되어 있지 않은 동일한 이유입니다. 언어는 모든 바이트와 모든 명령이 계산되는 시스템에서 개발되었습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.