programing

포인터 산술

copysource 2023. 1. 24. 10:03
반응형

포인터 산술

포인터 산술에 대한 좋은 기사나 설명(블로그, 예)이 있습니까?시청자는 C와 C++를 배우는 자바 프로그래머 무리입니다.

여기서 힌트를 얻었습니다.http://www.cplusplus.com/doc/tutorial/pointers.html

포인터를 이해하면 포인터 계산이 쉬워집니다.일반 산술과 유일한 차이점은 포인터에 추가하는 숫자에 포인터가 가리키는 유형의 크기를 곱한다는 것입니다.를 들어, 포인터가 「」에 있는 는, 「」입니다.int ★★★int 4바이트입니다.(pointer_to_int + 4)16인치(4ints)입니다.

그래서 글을 쓸 때

(a_pointer + a_number)

포인터 산술에서 실제로 일어나는 일은

(a_pointer + (a_number * sizeof(*a_pointer)))

규칙적인 산술로

첫째, 빙키 비디오가 도움이 될 수 있습니다.포인터에 대한 좋은 영상이에요.산술의 예를 다음에 나타냅니다.

int * pa = NULL;
int * pb = NULL;
pa += 1; // pa++. behind the scenes, add sizeof(int) bytes
assert((pa - pb) == 1);

print_out(pa); // possibly outputs 0x4
print_out(pb); // possibly outputs 0x0 (if NULL is actually bit-wise 0x0)

(늘 포인터 값을 엄밀하게 포함하는 포인터를 증가시키는 것은 정의되지 않은 동작입니다.포인터의 값에만 관심이 있었기 때문에 NULL을 사용했습니다.일반적으로 배열 요소를 가리킬 때만 증가/감소를 사용하십시오.)

다음은 두 가지 중요한 개념을 보여줍니다.

  • 포인터에 정수를 추가/감산하면 포인터를 N개씩 전진/후진시키는 것을 의미합니다.따라서 int의 크기가 4바이트인 경우 pa는 1씩 증가된 후 플랫폼에 0x4를 포함할 수 있습니다.
  • 다른 포인터에 의한 포인터의 감산은 요소로 측정되는 거리를 얻는 것을 의미합니다.따라서 pa에서 pb를 빼면 1이 됩니다.이는 1개의 요소 거리가 있기 때문입니다.

실제적인 예시로.함수를 작성했을 때 사용자가 시작 포인터와 종료 포인터를 제공한다고 가정합니다(C++에서는 매우 일반적입니다).

void mutate_them(int *begin, int *end) {
    // get the amount of elements
    ptrdiff_t n = end - begin;
    // allocate space for n elements to do something...
    // then iterate. increment begin until it hits end
    while(begin != end) {
        // do something
        begin++;
    }
}

ptrdiff_t(끝에서 시작)일부 컴파일러에서는 "int"와 동의어일 수 있지만 다른 컴파일러에서는 다른 유형일 수 있습니다.수 합니다.ptrdiff_t

NLP를 적용하여 주소 산술이라고 부릅니다.잘못된 사람이 가르쳐주거나 잘못된 예시와 함께 잘못된 단계에 있기 때문에 두려움과 오해를 받는 경우가 대부분입니다.아무도 그것을 이해하지 못하는 것은 당연하다.

포인터를 가르칠 때 교직원들은 "p는 a에 대한 포인터, p의 값은 a의 주소"와 같은 식으로 계속한다. 그것은 효과가 없을 것이다.이것은 당신이 만들 수 있는 원재료입니다. 그것으로 연습하면 당신의 학생들이 그것을 얻을 수 있을 것입니다.

int a'는 정수이며 정수형 값을 저장합니다.'int* p', p는 'int star'이며 'int star' 유형 값을 저장합니다.

'a'는 a에 저장된 'what' 정수를 얻는 방법('value of a'를 사용하지 않음), '&a'는 a 자체가 저장된 'where'를 얻는 방법('address'라고 말하려고 함)입니다.

'b = a'가 작동하려면 양쪽의 유형이 동일해야 합니다.a가 int인 경우 b는 int를 저장할 수 있어야 합니다(따라서 ___b, 공백은 'int'로 채워집니다).

'p = &a'가 작동하려면 양쪽의 유형이 동일해야 합니다.a가 정수, &a가 주소일 경우 p는 정수의 주소를 저장할 수 있어야 합니다.(따라서 ____p, 공백은 'int *'로 채워집니다).

이제 int *p를 다르게 입력하여 유형 정보를 표시합니다.

int* | p

'p'가 뭐죠? ans: 'int *'이므로 'p'는 정수의 주소입니다.

int | *p

'*p'가 뭐죠? ans: 'int'입니다.'*p'는 정수입니다.

이제 주소 산술로 넘어가겠습니다.

int a; a=1; a=a+1;

'a=a+1'에서는 무엇을 합니까? '다음'이라고 생각하십시오.a는 숫자이기 때문에 이것은 '다음 번호'라고 말하는 것과 같습니다.a가 1이므로 next라고 하면 2가 됩니다.

// 잘못된 예.경고를 받았습니다!!!int *p int a; p = &a; p=p+1;

'p=p+1'에서는 무엇을 합니까?아직 '다음'이라고 되어 있습니다.이번 p는 숫자가 아니라 주소입니다.그래서 우리가 말하는 것은 '다음 주소'입니다.다음 주소는 데이터 유형, 구체적으로는 데이터 유형의 크기에 따라 달라집니다.

printf d %d %d " , size of ( char ) , size of ( int ) , size of ( int ) , size of ( size of ) ;

따라서 주소에 대한 '다음'은 (데이터 유형)의 크기를 앞으로 이동합니다.

이건 나와 내가 가르치던 모든 사람들에게 효과가 있었어

그것을 해결할 수 있는 몇 가지 방법이 있다.

직관적인 접근법은 대부분의 C/C++ 프로그래머들이 생각하는 바와 같이 포인터는 메모리 주소입니다.Litb의 예는 다음과 같은 접근방식을 취합니다.(대부분의 머신에서 주소0 에 대응하고 있는) 늘 포인터가 있어, int 의 사이즈를 추가하면, 주소 4 가 표시됩니다.이는 포인터가 기본적으로 화려한 정수일 뿐이라는 것을 의미합니다.

안타깝게도 여기에는 몇 가지 문제가 있습니다.우선, 그것은 효과가 없을지도 모른다.Null 포인터는 주소 0을 실제로 사용하는 것을 보장하지 않습니다(단, 상수 0을 포인터에 할당하면 Null 포인터가 생성됩니다).

또한 늘 포인터를 늘릴 수 없습니다.또는 일반적으로 포인터는 항상 할당된 메모리(또는1개의 요소 과거) 또는 특수한 늘 포인터 상수 0을 가리켜야 합니다.

따라서 포인터는 단순히 할당된 메모리에 대해 반복할 수 있는 반복기일 뿐입니다.이것은 STL 반복자의 이면에 있는 중요한 아이디어 중 하나입니다.이들은 포인터로서 매우 많이 동작하고 적절한 반복자로서 작동하기 위해 원시 포인터를 패치하는 전문화를 제공하도록 모델링됩니다.

예를 들어, 여기에 이것에 대한 보다 상세한 설명이 있습니다.

그러나 이 후자의 견해는 STL 반복자를 실제로 설명하고 포인터가 이러한 특수한 경우라고 간단히 말해야 한다는 것을 의미합니다. 수 「버퍼의 다음 요소」를 포인트 하는 .std::vector<int>::iterator다른 컨테이너의 엔드 리터레이터와 마찬가지로 어레이의 끝을 지나 하나의 요소를 가리킬 수 있습니다.같은 버퍼를 가리키는 2개의 포인터를 빼서 이들 사이의 요소의 수를 얻을 수 있습니다.반복기와 마찬가지로 포인터가 다른 버퍼를 가리키면 의미 있는 비교는 할 수 없습니다(세그먼트 메모리 공간에서 무슨 일이 일어나는지 실제적인 예에서는 생각해 보십시오).다른 세그먼트를 가리키는 두 포인터의 거리는 얼마나 됩니까?

물론 실제로 CPU 주소와 C/C++ 포인터 사이에는 매우 밀접한 상관관계가 있습니다.하지만 정확히 같은 것은 아닙니다.포인터에는 CPU에 엄밀하게 필요하지 않을 수 있는 몇 가지 제한이 있습니다.

물론 대부분의 C++ 프로그래머들은 첫 번째 이해로 그럭저럭 살아가고 있습니다. 비록 기술적으로 정확하지는 않지만요.일반적으로 코드가 어떻게 동작하는지에 충분히 가깝기 때문에 사람들은 이해했다고 생각하고 넘어갑니다.

그러나 Java에서 온 누군가에게, 그리고 처음부터 포인터에 대해 배우는 것만으로, 후자의 설명은 쉽게 이해될 수 있고, 나중에 그들에게 더 적은 놀라움을 줄 것이다.

다음 문자열 길이 함수의 포인터 산술의 좋은 예를 생각해 보겠습니다.

int length(char *s)
{
   char *str = s;
   while(*str++);
   return str - s;
}

중요한 점은 포인터는 참조용으로 입력되는 단어 크기의 변수라는 것입니다.즉, void *, int *, long ** 이든 상관없이 단어 크기 변수일 뿐입니다.이러한 유형의 차이는 컴파일러가 참조된 유형으로 간주하는 것입니다.확실히 하자면, 워드 사이즈는 가상 주소의 폭을 의미합니다.이것이 무엇을 의미하는지 모를 경우 64비트 머신에서는 포인터가 8바이트, 32비트 머신에서는 포인터가 4바이트라는 것을 기억하십시오.주소의 개념은 포인터를 이해하는 데 매우 중요합니다.주소는 메모리 내의 특정 위치를 일의로 식별할 수 있는 번호입니다.기억 속에 있는 모든 것에는 주소가 있다.따라서 모든 변수에는 주소가 있다고 할 수 있습니다.이것이 반드시 맞는 것은 아니지만 컴파일러는 이를 가정할 수 있도록 합니다.주소 자체는 바이트 단위로 되어 있습니다.즉, 0x0000000은 메모리의 시작을 나타내고 0x00000001은 메모리의 1바이트입니다.즉, 포인터에 1을 추가함으로써 메모리로 1바이트 전진하는 것입니다.이제 어레이를 살펴보겠습니다.32개의 요소의 배열을 작성하면 배열의 각 셀이 크기(quux)이므로 할당 시작부터 할당 시작까지 32*sizeof(quux)가 추가됩니다.배열의 요소를 배열[n]로 지정하면 *(array+sizeof(quux)*n)에 대한 구문설탕(shorthand)이 됩니다.포인터 산술은 실제로 당신이 언급하고 있는 주소를 변경하는 것일 뿐이며, 이것이 우리가 strlen을 실장할 수 있는 이유입니다.

while(*n++ != '\0'){
  len++;
}

0이 될 때까지 한 바이트 한 바이트씩 스캔하는 거니까요도움이 됐으면 좋겠네요!

이 링크는 포인터 산술에 대한 링크에 매우 적합합니다.

예를 들어 다음과 같습니다.

포인터와 배열

ptr + i의 주소를 계산하는 공식(여기서 ptr의 타입 T *)은 다음과 같습니다.

addr ( ptr + i ) = addr ( ptr ) + [ size of ( T ) * i ]

32비트 플랫폼의 int 타입의 경우 addr(ptr+i) = addr(ptr)+4*i;

뺄셈

ptr - i도 계산할 수 있습니다.예를 들어 arr. int arr[ 10 ]; int * p1, * p2라는 이름의 int 배열이 있다고 가정합니다.

p1 = arr + 3 ; // p1 == & arr[ 3 ] 
p2 = p1 - 2 ; // p1 == & arr[ 1 ] 

언급URL : https://stackoverflow.com/questions/394767/pointer-arithmetic

반응형