development

포인터 산술

big-blog 2020. 12. 11. 19:08
반응형

포인터 산술


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


첫째, 빙키 비디오가 도움이 될 수 있습니다. 포인터에 대한 멋진 비디오입니다. 산술의 예는 다음과 같습니다.

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"의 동의어 일 수 있지만 다른 컴파일러에서는 다른 유형일 수 있습니다. 알 수 없으므로 일반 typedef를 선택합니다 ptrdiff_t.


내가 포인터를 배운 곳은 다음과 같습니다. http://www.cplusplus.com/doc/tutorial/pointers.html

포인터를 이해하면 포인터 산술이 쉽습니다. 그것과 일반 산술의 유일한 차이점은 포인터에 추가하는 숫자에 포인터가 가리키는 유형의 크기가 곱해진다는 것입니다. 당신이에 대한 포인터를 가지고 예를 들어, intint의 크기가 4 바이트, (pointer_to_int + 4)메모리 주소에 앞서 16 바이트 (4의 int)를 평가합니다.

그래서 당신이 쓸 때

(a_pointer + a_number)

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

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

정규 산술에서.


NLP를 적용하면 주소 산술이라고 부릅니다. '포인터'는 잘못된 사람들이 가르쳤거나 잘못된 방식으로 잘못된 예를 들어 잘못된 단계에 있기 때문에 주로 두려워하고 오해합니다. 아무도 그것을 '얻지'않는다는 것은 놀라운 일이 아닙니다.

포인터를 가르 칠 때 교수진은 "p는 a에 대한 포인터, p의 값은 a의 주소"등에 대해 계속 진행합니다. 작동하지 않습니다. 여기에 당신이 만들 원료가 있습니다. 연습하면 학생들이 얻을 수 있습니다.

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

'a'는 a에 저장된 'what'정수를 얻는 방법입니다 ( 'value of a'를 사용하지 마십시오). '& a'는 자체가 저장된 'where'를 얻는 방법입니다 ( 'address'라고 말하십시오).

이것이 작동하려면 'b = a', 양쪽 모두 같은 유형이어야합니다. a가 int이면 b는 int를 저장할 수 있어야합니다. (그래서 ______ b, 공백은 'int'로 채워집니다)

'p = & a'가 작동하려면 양쪽이 동일한 유형이어야합니다. a가 정수이고 & a가 주소이면 p는 정수 주소를 저장할 수 있어야합니다. (그래서 ______ p, 공백은 'int *'로 채워집니다)

이제 int * p를 다르게 작성하여 유형 정보를 가져옵니다.

int * |

'p'는 무엇입니까? ans : 'int *'입니다. 그래서 'p'는 정수의 주소입니다.

int | *피

'* 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", sizeof (char), sizeof (int), sizeof (float));

따라서 주소의 'next'는 sizeof (데이터 유형) 앞으로 이동합니다.

이것은 저와 제가 가르치 던 모든 사람들에게 효과적이었습니다.


다음 문자열 길이 함수에 대한 포인터 산술의 좋은 예를 고려합니다.

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

So, the key thing to remember is that a pointer is just a word-sized variable that's typed for dereferencing. That means that whether it's a void *, int *, long long **, it's still just a word sized variable. The difference between these types is what the compiler considers the dereferenced type. Just to clarify, word sized means width of a virtual address. If you don't know what this means, just remember on a 64-bit machine, pointers are 8 bytes, and on a 32-bit machine, pointers are 4 bytes. The concept of an address is SUPER important in understanding pointers. An address is a number capable of uniquely identifying a certain location in memory. Everything in memory has an address. For our purposes, we can say that every variable has an address. This isn't necessarily always true, but the compiler lets us assume this. The address itself is byte granular, meaning 0x0000000 specifies the beginning of memory, and 0x00000001 is one byte into memory. This means that by adding one to a pointer, we're moving one byte forward into memory. Now, lets take arrays. If you create an array of type quux that's 32 elements big, it will span from the beginning of it's allocation, to the beginning of it's allocation plus 32*sizeof(quux), since each cell of the array is sizeof(quux) big. So, really when we specify an element of an array with array[n], that's just syntactic sugar (shorthand) for *(array+sizeof(quux)*n). Pointer arithmetic is really just changing the address that you're referring to, which is why we can implement strlen with

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

since we're just scanning along, byte by byte until we hit a zero. Hope that helps!


There are several ways to tackle it.

The intuitive approach, which is what most C/C++ programmers think of, is that pointers are memory addresses. litb's example takes this approach. If you have a null pointer (which on most machines corresponds to the address 0), and you add the size of an int, you get the address 4. This implies that pointers are basically just fancy integers.

Unfortunately, there are a few problems with this. To begin with, it may not work. A null pointer is not guaranteed to actually use the address 0. (Although assigning the constant 0 to a pointer yields the null pointer).

Further, you're not allowed to increment the null pointer, or more generally, a pointer must always point to allocated memory (or one element past), or the special null pointer constant 0.

So a more correct way of thinking of it is that pointers are simply iterators allowing you to iterate over allocated memory. This is really one of the key ideas behind the STL iterators. They're modelled to behave very much as pointers, and to provide specializations that patch up raw pointers to work as proper iterators.

A more elaborate explanation of this is given here, for example.

But this latter view means that you should really explain STL iterators, and then simply say that pointers are a special case of these. You can increment a pointer to point to the next element in the buffer, just like you can a std::vector<int>::iterator. It can point one element past the end of an array, just like the end iterator in any other container. You can subtract two pointers that point into the same buffer to get the number of elements between them, just like you can with iterators, and just like with iterators, if the pointers point into separate buffers, you can not meaningfully compare them. (For a practical example of why not, consider what happens in a segmented memory space. What's the distance between two pointers pointing to separate segments?)

Of course in practice, there's a very close correlation between CPU addresses and C/C++ pointers. But they're not exactly the same thing. Pointers have a few limitations that may not be strictly necessary on your CPU.

Of course, most C++ programmers muddle by on the first understanding, even though it's technically incorrect. It's typically close enough to how your code ends up behaving that people think they get it, and move on.

But for someone coming from Java, and just learning about pointers from scratch, the latter explanation may be just as easily understood, and it's going to spring fewer surprises on them later.


This is one pretty good at link here about Pointer Arithmetic

For example:

Pointer and array

Formula for computing the address of ptr + i where ptr has type T *. then the formula for the address is:

addr( ptr + i ) = addr( ptr ) + [ sizeof( T ) * i ]

For for type of int on 32bit platform, addr(ptr+i) = addr(ptr)+4*i;

Subtraction

We can also compute ptr - i. For example, suppose we have an int array called arr. int arr[ 10 ] ; int * p1, * p2 ;

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

참고URL : https://stackoverflow.com/questions/394767/pointer-arithmetic

반응형