수색…


소개

포인터는 메모리의 위치를 ​​참조하는 주소입니다. 일반적으로 함수 나 데이터 구조가 참조 된 메모리를 복사하지 않고도 메모리를 인식하고 수정할 수 있도록하기 위해 사용됩니다. 포인터는 기본 (내장) 또는 사용자 정의 유형 모두에서 사용할 수 있습니다.

포인터는 "역 참조" * , "주소" & "및"화살표 " -> 연산자를 사용합니다. '*'및 '->'연산자는 가리키는 메모리에 액세스하는 데 사용되고 & 연산자는 메모리에서 주소를 가져 오는 데 사용됩니다.

통사론

  • <데이터 유형> * <변수 이름>;
  • <데이터 유형> * <변수 이름> = & <동일한 데이터 유형의 변수 이름>;
  • <데이터 유형> * <변수 이름> = <동일한 데이터 유형의 값>;
  • int * foo; // 정수 값을 가리키는 포인터
  • int * bar = & myIntVar;
  • 긴 막대 [2];
  • 긴 * bar [] = {& myLongVar1, & myLongVar2}; // 같음 : long * bar [2]

비고

같은 줄에 여러 포인터를 선언 할 때의 문제점을 알고 있어야합니다.

int* a, b, c; //Only a is a pointer, the others are regular ints.

int* a, *b, *c; //These are three pointers!

int *foo[2]; //Both *foo[0] and *foo[1] are pointers.

포인터 기본 사항

C ++ 11

참고 : 다음 모든 경우에 C ++ 11 상수 nullptr 이 있다고 가정합니다. 이전 버전의 경우 nullptr 을 비슷한 역할을하는 데 사용 된 상수 인 NULL nullptr .

포인터 변수 만들기

포인터 변수는 특정 * 구문을 사용하여 만들 수 있습니다 (예 : int *pointer_to_int; .
변수가 포인터 타입 ( int * ) 일 때 변수는 단지 메모리 주소를 포함합니다. 메모리 주소는 기본 유형 ( int )의 데이터가 저장되는 위치입니다.

차이점은 변수의 크기를 동일한 유형의 포인터 크기와 비교할 때 명확합니다.

// Declare a struct type `big_struct` that contains
// three long long ints.
typedef struct {
    long long int foo1;
    long long int foo2;
    long long int foo3;
} big_struct;

// Create a variable `bar` of type `big_struct`
big_struct bar;
// Create a variable `p_bar` of type `pointer to big_struct`.
// Initialize it to `nullptr` (a null pointer).
big_struct *p_bar0 = nullptr;

// Print the size of `bar`
std::cout << "sizeof(bar) = " << sizeof(bar) << std::endl;
// Print the size of `p_bar`.
std::cout << "sizeof(p_bar0) = " << sizeof(p_bar0) << std::endl;

/* Produces:
    sizeof(bar) = 24
    sizeof(p_bar0) = 8
*/

다른 변수의 주소 가져 오기

포인터는 일반적인 변수처럼 서로간에 할당 될 수 있습니다. 이 경우 포인터가 가리키는 실제 데이터아니라 한 포인터에서 다른 포인터로 복사되는 메모리 주소 입니다.
게다가 그들은 null 메모리 위치를 나타내는 nullptr 값을 취할 수 있습니다. nullptr 과 같은 포인터는 잘못된 메모리 위치를 포함하고 있으므로 유효한 데이터를 참조하지 않습니다.

& 변수의 주소 앞에 연산자 & 를 붙임으로써 주어진 타입의 변수의 메모리 주소를 얻을 수 있습니다. & 로 반환되는 값은 변수의 메모리 주소 ( 변수가 범위를 벗어나지 않는 유효한 데이터)를 포함하는 기본 유형에 대한 포인터입니다.

// Copy `p_bar0` into `p_bar_1`.
big_struct *p_bar1 = p_bar0;

// Take the address of `bar` into `p_bar_2`
big_struct *p_bar2 = &bar;

// p_bar1 is now nullptr, p_bar2 is &bar.

p_bar0 = p_bar2;

// p_bar0 is now &bar.

p_bar2 = nullptr;

// p_bar0 == &bar
// p_bar1 == nullptr
// p_bar2 == nullptr

참고 문헌과는 대조적으로 :

  • 두 포인터를 할당해도 할당 포인터가 참조하는 메모리를 덮어 쓰지 않습니다.
  • 포인터는 null 일 수 있습니다.
  • 운영자의 주소는 명시 적으로 요구됩니다.

포인터의 내용에 액세스하기

주소를 취하는 것은 & 요구하므로 컨텐트에 액세스하는 경우 접두사로 비 참조 연산자 * 사용해야합니다. 포인터가 참조 해제되면 기본 유형 (실제로는 참조)의 변수가됩니다. 그런 다음 const 가 아닌 경우 읽기 및 수정할 수 있습니다.

(*p_bar0).foo1 = 5;

// `p_bar0` points to `bar`. This prints 5.
std::cout << "bar.foo1 = " << bar.foo1 << std::endl;

// Assign the value pointed to by `p_bar0` to `baz`.
big_struct baz;
baz = *p_bar0;

// Now `baz` contains a copy of the data pointed to by `p_bar0`.
// Indeed, it contains a copy of `bar`.

// Prints 5 as well
std::cout << "baz.foo1 = " << baz.foo1 << std::endl;

* 와 연산자의 조합 . -> 로 축약됩니다.

std::cout << "bar.foo1 = " << (*p_bar0).foo1 << std::endl; // Prints 5
std::cout << "bar.foo1 = " <<  p_bar0->foo1  << std::endl; // Prints 5

잘못된 포인터를 역 참조

포인터를 역 참조 할 때 유효한 데이터를 가리키는 지 확인해야합니다. 잘못된 포인터 (또는 널 포인터)를 참조 해제하면 메모리 액세스 위반이 발생하거나 가비지 데이터를 읽거나 쓸 수 있습니다.

big_struct *never_do_this() {
   // This is a local variable. Outside `never_do_this` it doesn't exist.
   big_struct retval;
   retval.foo1 = 11;
   // This returns the address of `retval`.
   return &retval;
   // `retval` is destroyed and any code using the value returned
   // by `never_do_this` has a pointer to a memory location that
   // contains garbage data (or is inaccessible).
}

이러한 시나리오에서 g++clang++ 은 경고를 올바르게 발행합니다.

(Clang) warning: address of stack memory associated with local variable 'retval' returned [-Wreturn-stack-address]
(Gcc)   warning: address of local variable ‘retval’ returned [-Wreturn-local-addr]

따라서 포인터가 함수의 인수 일 때주의해야하며 null 일 수 있습니다.

void naive_code(big_struct *ptr_big_struct) {
    // ... some code which doesn't check if `ptr_big_struct` is valid.
    ptr_big_struct->foo1 = 12;
}

// Segmentation fault.
naive_code(nullptr);

포인터 작업

포인터에 대해 두 가지 연산자가 있습니다. 주소 연산자 (&) : 피연산자의 메모리 주소를 반환합니다. 내용 중 (Dereference) 연산자 (*) : 연산자에 의해 지정된 주소에있는 변수의 값을 반환합니다.

int var = 20;
int *ptr;
ptr = &var;

cout << var << endl;
//Outputs 20 (The value of var)

cout << ptr << endl;
//Outputs 0x234f119 (var's memory location)

cout << *ptr << endl;
//Outputs 20(The value of the variable stored in the pointer ptr

별표 (*)는 포인터임을 나타내는 간단한 목적으로 포인터를 선언하는 데 사용됩니다. 이것을 지정된 주소에있는 값을 얻는 데 사용되는 역 참조 연산자와 혼동하지 마십시오. 그것들은 단순히 같은 부호로 표현 된 두 가지 다른 것들입니다.

포인터 산술

증가 / 감소

포인터는 증가 또는 감소 될 수 있습니다 (접두어 및 접미어). 포인터를 증가 시키면 포인터 값이 배열에서 현재 가리키는 요소보다 한 요소 앞으로 이동합니다. 포인터를 줄이면 포인터가 배열의 이전 요소로 이동합니다.

포인터가 가리키는 유형이 완료되지 않은 경우 포인터 계산이 허용되지 않습니다. void 는 항상 불완전한 형태입니다.

char* str = new char[10]; // str = 0x010
++str;                    // str = 0x011  in this case sizeof(char) = 1 byte

int* arr = new int[10];   // arr = 0x00100
++arr;                    // arr = 0x00104 if sizeof(int) = 4 bytes

void* ptr = (void*)new char[10];
++ptr;    // void is incomplete.

끝 요소에 대한 포인터가 증가하면 포인터는 배열 끝을지나 한 요소를 가리 킵니다. 이러한 포인터는 역 참조 될 수 없지만 감소 될 수 있습니다.

배열에서 one-past-the-end 요소에 대한 포인터를 증가 시키거나 배열에서 첫 번째 요소에 대한 포인터를 감소 시키면 정의되지 않은 동작이 발생합니다.

배열이 아닌 객체에 대한 포인터는 크기 1의 배열 인 것처럼 포인터 산술을 위해 처리 될 수 있습니다.

더하기 / 빼기

포인터에 정수 값을 추가 할 수 있습니다. 그들은 1만큼 증가하지 않고 특정 수로 작동합니다. 포인터에서 정수 값을 뺄 수 있고 포인터 감소로 작용할 수 있습니다. 증가 / 감소와 마찬가지로 포인터는 완전한 유형을 가리켜 야합니다.

char* str = new char[10];  // str = 0x010
str += 2;                  // str = 0x010 + 2 * sizeof(char) = 0x012

int* arr = new int[10];    // arr = 0x100
arr += 2;                  // arr = 0x100 + 2 * sizeof(int) = 0x108, assuming sizeof(int) == 4.

포인터 차이점

동일한 유형에 대한 두 포인터 간의 차이를 계산할 수 있습니다. 두 포인터는 같은 배열 객체 내에 있어야합니다. 그렇지 않으면 정의되지 않은 동작이 발생합니다.

동일한 배열에서 두 개의 포인터 PQ 가 주어지면 P 가 배열의 i 번째 요소이고 Qj 번째 요소 인 경우 P - Qi - j 됩니다. 결과의 유형은 <cstddef> std::ptrdiff_t 입니다.

char* start = new char[10];  // str = 0x010
char* test = &start[5];
std::ptrdiff_t diff = test - start; //Equal to 5.
std::ptrdiff_t diff = start - test; //Equal to -5; ptrdiff_t is signed.


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow