설명과 함께 예제를 포함하십시오.
int *p;
정수에 대한 포인터를 정의하고 해당 포인터를 *p
역 참조하므로 실제로 p가 가리키는 데이터를 검색합니다.
설명과 함께 예제를 포함하십시오.
int *p;
정수에 대한 포인터를 정의하고 해당 포인터를 *p
역 참조하므로 실제로 p가 가리키는 데이터를 검색합니다.
답변:
그것은이다 일반적으로 충분한 - 당신이 어셈블리를 프로그래밍하지 않는 한 - 직시 포인터 1 2, 제 3 네 번째 등, 프로세스의 메모리에 두 번째 바이트를 참조하여, 숫자 메모리 주소를 포함를 ....
포인터가 가리키는 메모리의 데이터 / 값에 액세스하려는 경우 (숫자 인덱스가있는 주소의 내용) 포인터 를 역 참조 합니다.
컴퓨터 나 언어마다 다른 표기법을 사용하여 컴파일러 나 인터프리터에게 현재 지적한 객체의 (현재) 값에 관심이 있다고 말하고 있습니다. 아래에서는 C와 C ++에 중점을 둡니다.
p
아래와 같은 포인터가 주어지면 C에서 고려하십시오 ...
const char* p = "abc";
... 문자 'a', 'b', 'c'를 인코딩하는 데 사용되는 숫자 값과 텍스트 데이터의 끝을 나타내는 0 바이트가있는 4 바이트는 메모리의 어딘가에 저장되며 그 주소는 데이터는에 저장됩니다 p
. 이런 방식으로 C가 메모리에서 텍스트를 인코딩하는 것을 ASCIIZ라고 합니다.
예를 들어, 문자열 리터럴이 주소 0x1000에 있고 p
32 비트 포인터가 0x2000에 있으면 메모리 내용은 다음과 같습니다.
Memory Address (hex) Variable name Contents
1000 'a' == 97 (ASCII)
1001 'b' == 98
1002 'c' == 99
1003 0
...
2000-2003 p 1000 hex
주소 0x1000에 대한 변수 이름 / 식별자는 없지만 주소를 저장하는 포인터를 사용하여 문자열 리터럴을 간접적으로 참조 할 수 있습니다 p
.
문자가 p
가리키는 것을 가리 키기 위해 p
다음 표기법 중 하나를 사용하여 역 참조합니다 (다시 C).
assert(*p == 'a'); // The first character at address p will be 'a'
assert(p[1] == 'b'); // p[1] actually dereferences a pointer created by adding
// p and 1 times the size of the things to which p points:
// In this case they're char which are 1 byte in C...
assert(*(p + 1) == 'b'); // Another notation for p[1]
또한 지시 된 데이터를 통해 포인터를 이동하여 이동할 때 참조를 해제 할 수 있습니다.
++p; // Increment p so it's now 0x1001
assert(*p == 'b'); // p == 0x1001 which is where the 'b' is...
쓸 수있는 데이터가있는 경우 다음과 같은 작업을 수행 할 수 있습니다.
int x = 2;
int* p_x = &x; // Put the address of the x variable into the pointer p_x
*p_x = 4; // Change the memory at the address in p_x to be 4
assert(x == 4); // Check x is now 4
위의 컴파일 타임에이라는 변수가 필요하다는 것을 알고 있어야 x
하며 코드는 컴파일러가 저장 위치를 정렬하도록 요청하여 주소를 통해 사용할 수 있도록합니다 &x
.
C에서 데이터 멤버가있는 구조에 대한 포인터 인 변수가있는 경우 ->
역 참조 연산자를 사용하여 해당 멤버에 액세스 할 수 있습니다 .
typedef struct X { int i_; double d_; } X;
X x;
X* p = &x;
p->d_ = 3.14159; // Dereference and access data member x.d_
(*p).d_ *= -1; // Another equivalent notation for accessing x.d_
포인터를 사용하려면 컴퓨터 프로그램에서 가리키는 데이터 유형에 대한 통찰력이 필요합니다. 해당 데이터 유형을 나타내는 데 둘 이상의 바이트가 필요한 경우 포인터는 일반적으로 데이터에서 가장 낮은 번호의 바이트를 가리 킵니다.
따라서 좀 더 복잡한 예를 살펴보십시오.
double sizes[] = { 10.3, 13.4, 11.2, 19.4 };
double* p = sizes;
assert(p[0] == 10.3); // Knows to look at all the bytes in the first double value
assert(p[1] == 13.4); // Actually looks at bytes from address p + 1 * sizeof(double)
// (sizeof(double) is almost always eight bytes)
++p; // Advance p by sizeof(double)
assert(*p == 13.4); // The double at memory beginning at address p has value 13.4
*(p + 2) = 29.8; // Change sizes[3] from 19.4 to 29.8
// Note earlier ++p and + 2 here => sizes[3]
때로는 프로그램이 실행될 때까지 얼마나 많은 메모리가 필요한지 알지 못하고 어떤 데이터가 발생하는지 알 수 있습니다. 그런 다음를 사용하여 동적으로 메모리를 할당 할 수 있습니다 malloc
. 주소를 포인터에 저장하는 것이 일반적입니다 ...
int* p = (int*)malloc(sizeof(int)); // Get some memory somewhere...
*p = 10; // Dereference the pointer to the memory, then write a value in
fn(*p); // Call a function, passing it the value at address p
(*p) += 3; // Change the value, adding 3 to it
free(p); // Release the memory back to the heap allocation library
C ++에서 메모리 할당은 일반적으로 new
연산자로 수행되며 다음 과 같이 할당 해제됩니다 delete
.
int* p = new int(10); // Memory for one int with initial value 10
delete p;
p = new int[10]; // Memory for ten ints with unspecified initial value
delete[] p;
p = new int[10](); // Memory for ten ints that are value initialised (to 0)
delete[] p;
아래의 C ++ 스마트 포인터 도 참조하십시오 .
종종 포인터는 메모리에서 일부 데이터 또는 버퍼가 존재하는 곳의 유일한 표시 일 수 있습니다. 해당 데이터 / 버퍼의 지속적인 사용이 필요하거나 메모리 를 호출 free()
하거나 delete
누출을 피하는 기능이 필요한 경우 프로그래머는 포인터 사본에서 작동해야합니다 ...
const char* p = asprintf("name: %s", name); // Common but non-Standard printf-on-heap
// Replace non-printable characters with underscores....
for (const char* q = p; *q; ++q)
if (!isprint(*q))
*q = '_';
printf("%s\n", p); // Only q was modified
free(p);
... 또는 모든 변경 사항의 역전을 신중하게 조정하십시오 ...
const size_t n = ...;
p += n;
...
p -= n; // Restore earlier value...
free(p);
C ++에서는 스마트 포인터 객체를 사용하여 포인터를 저장 및 관리하고 스마트 포인터의 소멸자가 실행될 때 자동으로 할당을 해제하는 것이 가장 좋습니다 . C ++ 11부터 표준 라이브러리는 unique_ptr
할당 된 객체에 대한 단일 소유자가있는 경우 두 가지를 제공 합니다 ...
{
std::unique_ptr<T> p{new T(42, "meaning")};
call_a_function(p);
// The function above might throw, so delete here is unreliable, but...
} // p's destructor's guaranteed to run "here", calling delete
... shared_ptr
공유 소유권 ( 참조 횟수 사용 ) ...
{
auto p = std::make_shared<T>(3.14, "pi");
number_storage1.may_add(p); // Might copy p into its container
number_storage2.may_add(p); // Might copy p into its container } // p's destructor will only delete the T if neither may_add copied it
C에서, NULL
그리고 0
추가로 C ++에서 nullptr
, 포인터가 현재 변수의 메모리 주소를 가지고 있지 않다는 것을 나타 내기 위해 사용될 수 있으며, 포인터 산술에서 역 참조되거나 사용되어서는 안됩니다. 예를 들면 다음과 같습니다.
const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
int c;
while ((c = getopt(argc, argv, "f:")) != -1)
switch (c) {
case f: p_filename = optarg; break;
}
if (p_filename) // Only NULL converts to false
... // Only get here if -f flag specified
C 및 C ++에서 붙박이 숫자 유형은 반드시 디폴트로하지 않는 것처럼 0
,도 bools
에 false
, 포인터는 항상로 설정되지 않습니다 NULL
. 그들이 때 이러한 모든 0 / 허위 / null로 설정되어 static
변수 나 (C ++ 전용) 직접 또는 간접 멤버 정적 객체의 변수 또는 염기, 또는 예를 들어 제로 초기화를 (겪게 new T();
및 new T(x, y, z);
포인터를 포함하여 T의 회원에 제로 초기화를 수행하는 반면, new T;
하지 않습니다).
당신이 할당 할 때 또한 0
, NULL
및 nullptr
포인터 포인터의 비트는 반드시 모든 리셋되지 않습니다 : 포인터가 하드웨어 수준에서 "0"을 포함, 또는 가상 주소 공간에 주소 0을 참조하지 않을 수 있습니다. 컴파일러는 다른이는 이유가있는 경우 저장하는 것을 허용하지만이하는대로된다 - 당신이 함께 와서 포인터를 비교하면 0
, NULL
, nullptr
또는 그 중 하나를 할당 된 다른 포인터는, 비교해야 작업이 예상대로. 따라서 컴파일러 수준의 소스 코드 아래에서 "NULL"은 C 및 C ++ 언어에서 약간 "마 법적"입니다.
보다 엄격하게, 초기화 된 포인터는 하나 NULL
또는 ( 가상 가상 ) 메모리 주소를 식별하는 비트 패턴을 저장합니다.
간단한 경우는 이것이 프로세스의 전체 가상 주소 공간에 대한 숫자 오프셋입니다. 보다 복잡한 경우에, 포인터는 특정 메모리 영역에 대해 상대적인 것일 수 있으며, CPU는 CPU "세그먼트"레지스터 또는 비트 패턴으로 인코딩 된 세그먼트 ID의 어떤 방식에 기초하여 선택하거나 및 / 또는 주소를 사용한 기계 코드 지침.
예를 들어, 변수 int*
를 가리 키도록 올바르게 초기화 된 int
경우 float*
- "GPU"메모리의 액세스 메모리로 int
캐스트 한 후 변수가 있는 메모리와는 상당히 다른 메모리에 캐스팅 된 후 함수 포인터로 한 번 캐스팅되어 추가로 사용될 수 있습니다. 프로그램을위한 별개의 메모리 홀딩 머신 연산 코드 ( int*
이러한 메모리 영역 내 에서 유효하고 임의의 유효하지 않은 포인터 의 숫자 값으로 ).
C 및 C ++와 같은 3GL 프로그래밍 언어는 이러한 복잡성을 숨기는 경향이 있습니다.
컴파일러가 변수 또는 함수에 대한 포인터를 제공하면 변수를 자유롭게 참조 해제 할 수 있으며 (변수가 파괴되거나 할당 해제되지 않는 한) 특정 CPU 세그먼트 레지스터를 미리 복원 해야하는지 여부는 컴파일러의 문제입니다. 사용 된 고유 한 기계 코드 명령
배열의 요소에 대한 포인터를 얻는 경우 포인터 산술을 사용하여 배열의 다른 곳으로 이동하거나 요소의 다른 포인터와 비교할 수있는 배열의 마지막 끝 주소를 구성 할 수 있습니다 배열에서 (또는 포인터 연산에 의해 같은 과거의 끝 값으로 유사하게 이동 한); C와 C ++에서 다시 한 번, "정상 작동"하는지 확인하는 것은 컴파일러의 책임입니다.
공유 메모리 매핑과 같은 특정 OS 기능은 포인터를 제공 할 수 있으며, 주소 범위 내에서 "작동"할 수 있습니다.
유효한 포인터를 이러한 경계를 넘어서 이동하거나 임의의 숫자를 포인터로 캐스트하거나 관련없는 유형으로 캐스트 된 포인터를 사용하려고하면 일반적으로 정의되지 않은 동작 이 있으므로 상위 레벨 라이브러리 및 애플리케이션에서는 피해야하지만 OS, 디바이스 드라이버 등의 코드는 피해야합니다. C 또는 C ++ 표준에 의해 정의되지 않은 동작에 의존해야 할 수도 있습니다. 그럼에도 불구하고 특정 구현이나 하드웨어에 의해 잘 정의되어 있습니다.
p[1]
과 *(p + 1)
동일 ? 즉, 동일한 지침을 생성 p[1]
하고 *(p + 1)
생성 합니까 ?
p
는 2000입니다. 다른 포인터가 p
있으면 2000을 4 또는 8 바이트로 저장해야합니다. 희망이 도움이됩니다! 건배.
u
에 배열이 포함 된 경우 arr
gcc와 clang은 lvalue u.arr[i]
가 다른 공용체 멤버와 동일한 스토리지에 액세스 할 수 있음을 인식 하지만 lvalue *(u.arr+i)
는 그렇게 할 수 있음을 인식하지 않습니다 . 나는 그 컴파일러의 저자가 후자가 UB를 호출한다고 생각하는지, 전자가 UB를 호출한다고 생각하는지 확실하지 않지만 어쨌든 유용하게 처리해야하지만 두 표현식이 다른 것으로 분명히 볼 수 있습니다.
포인터 역 참조는 포인터가 가리키는 메모리 위치에 저장된 값을 얻는 것을 의미합니다. 연산자 *를 사용하여 역 참조 연산자라고합니다.
int a = 10;
int* ptr = &a;
printf("%d", *ptr); // With *ptr I'm dereferencing the pointer.
// Which means, I am asking the value pointed at by the pointer.
// ptr is pointing to the location in memory of the variable a.
// In a's location, we have 10. So, dereferencing gives this value.
// Since we have indirect control over a's location, we can modify its content using the pointer. This is an indirect way to access a.
*ptr = 20; // Now a's content is no longer 10, and has been modified to 20.
[]
가 포인터를 역 참조 a[b]
한다는 의미를 추가하겠습니다 *(a + b)
.
포인터는 값에 대한 "참조"입니다. 라이브러리 호출 번호와 마찬가지로 책에 대한 참조입니다. 전화 번호를 "참조"하면 실제로 해당 책을 검색하고 검색하는 중입니다.
int a=4 ;
int *pA = &a ;
printf( "The REFERENCE/call number for the variable `a` is %p\n", pA ) ;
// The * causes pA to DEREFERENCE... `a` via "callnumber" `pA`.
printf( "%d\n", *pA ) ; // prints 4..
책이 없으면, 사서가 소리를 내기 시작하고 도서관을 닫고, 두 사람이없는 사람이 책을 찾게되는 원인을 조사하도록 설정됩니다.
포인터 기본의 코드 및 설명 :
역 참조 작업은 포인터에서 시작하여 화살표를 따라 해당 포인트에 액세스합니다. 목표는 포인트 상태를 보거나 포인트 상태를 변경하는 것입니다. 포인터에 대한 역 참조 작업은 포인터에 포인트가있는 경우에만 작동합니다. 포인트를 할당하고 포인터가 포인트를 가리 키도록 설정해야합니다. 포인터 코드에서 가장 일반적인 오류는 포인트를 설정하는 것을 잊고 있습니다. 코드에서 오류로 인해 가장 일반적인 런타임 충돌은 역 참조 작업 실패입니다. Java에서 런타임 시스템은 잘못된 역 참조를 정중하게 표시합니다. C, C ++ 및 Pascal과 같은 컴파일 된 언어에서 잘못된 역 참조는 때때로 중단되고 때로는 미묘하고 무작위적인 방식으로 메모리를 손상시킵니다.
void main() {
int* x; // Allocate the pointer x
x = malloc(sizeof(int)); // Allocate an int pointee,
// and set x to point to it
*x = 42; // Dereference x to store 42 in its pointee
}
역 참조가 실제 가치에 접근한다는 의미이므로 이전의 모든 대답이 잘못되었다고 생각합니다. Wikipedia는 대신 올바른 정의를 제공합니다. https://en.wikipedia.org/wiki/Dereference_operator
포인터 변수에서 작동하며 포인터 주소의 값과 동등한 l 값을 반환합니다. 이것을 포인터의 "역 참조"라고합니다.
즉, 포인터가 가리키는 값에 액세스하지 않고도 포인터를 역 참조 할 수 있습니다. 예를 들면 다음과 같습니다.
char *p = NULL;
*p;
값에 액세스하지 않고 NULL 포인터를 역 참조했습니다. 또는 우리는 할 수 있습니다 :
p1 = &(*p);
sz = sizeof(*p);
다시 참조하지만 값에 액세스하지는 않습니다. 이러한 코드는 충돌하지 않습니다. 실제로 잘못된 포인터로 데이터에 액세스 하면 충돌이 발생합니다 . 그러나 불행히도 표준에 따르면 유효하지 않은 포인터를 참조 해제하는 것은 실제 데이터를 터치하지 않더라도 정의되지 않은 동작입니다 (몇 가지 예외는 있음).
간단히 말해 : 포인터 역 참조는 역 참조 연산자를 적용하는 것을 의미합니다. 이 연산자는 나중에 사용하기 위해 l 값을 반환합니다.
*p;
정의되지 않은 동작을 유발합니다. 당신이 값에 액세스하지 않는 역 참조 바로 것을 있지만 그 자체로 , 코드는 *p;
않습니다 액세스에게 값입니다.