2012-04-21 2 views
1

저는 최근에 C++에서 참조와 포인터를 이해하려고 노력했습니다. 조금 혼란스러워지고 있습니다. 각각 *& 연산자는 주소에서 값을 가져올 수 있으며 값의 주소를 얻을 수 있지만이 두 가지를 기본 유형 인 int과 같이 간단하게 사용할 수없는 이유는 무엇입니까?포인터 변수 사용이란 무엇입니까?

string x = "Hello"; 
int y = &x; //Set 'y' to the memory address of 'x' 
cout << *y; //Output the value at the address 'y' (which is the memory address of 'x') 

위의 코드해야, 이론적으로 내 마음 속에, 출력 : 당신은, 예를 들어, 다음과 같은 것을하지 어떤 이상한 포인터 변수 생성을 사용할 수없는 이유

이해가 안 돼요 'x'의 값 'y'는 'x'의 메모리 주소를 포함하므로 '* y'는 'x'이어야합니다. 이것이 작동한다면 (컴파일 할 때 부수적으로 쓰는 것이지, 문자열에서 int로 변환 할 수 없다는 것을 말해 준다. 메모리 주소가 int 벌금에 저장).

특수 포인터 변수 선언 (예 : string *y = &x)을 사용해야하는 이유는 무엇입니까? 그리고 내부에서 위의 예에서 문자 그대로 포인터 선언에 * 연산자를 사용하면 'y'값을 'x'의 메모리 주소로 설정하지만 나중에 액세스하려는 경우 메모리 주소 ('& x')의 값은 이전에 메모리 주소로 설정 한 '* y'와 동일하게 사용할 수 있습니다.

+2

'int' 변수를 역 참조 할 수 없으므로'* y'가 정의되지 않습니다. –

+1

64 비트 플랫폼 (64 비트 주소 공간 포함)에서 'int'는 32 비트로 정의되어 있기 때문에 메모리 주소가 'int'에 저장 될 수 있다고 믿는 이유는 흥미로울 것입니다 정수. –

+2

@Damien_The_Unbeliever : 그런 다음 질문을 "어디에서나 'intptr_t'를 사용하지 않는 이유는 무엇입니까?"라고 다시 말해주십시오. 또는 "왜 모든 곳에서'void *'를 사용하지 않을까요?". 그는 왜 모든 다른 포인터 유형이 필요한지 알고 싶어합니다. –

답변

1

기계 언어와 같은 일부 저급 언어는 사용자가 설명하는대로 정확하게 작동합니다. 숫자는 숫자이며, 프로그래머가 자신의 머리 속에 그 숫자를 나타내는 것은 개발자의 몫입니다. 일반적으로 말하자면, 더 높은 수준의 언어의 희망은 그 개발 스타일에서 오는 오류에 대한 우려와 잠재력에서 당신을 지키기위한 것입니다.

당신은 실제로 C++의 타입 안전을 무시할 수 있습니다. 내가 실행할 때 예를 들어, 내가 가진 32 비트 시스템에서 GCC는 "안녕하세요"인쇄합니다이 :

string x = "Hello"; 
int y = reinterpret_cast<int>(&x); 
cout << *reinterpret_cast<string*>(y) << endl; 

그러나 같은 거의 모든 다른 답변자가 지적한, 그것은 다른 컴퓨터에서 작동합니다 보장이 없습니다 .

error: cast from ‘std::string*’ to ‘int’ loses precision

어느 나는 long로 변경하여 해결할 수 있습니다 : 나는 64 비트 컴퓨터에서이 작업을하려고하면, 내가 얻을

string x = "Hello"; 
long y = reinterpret_cast<long>(&x); 
cout << *reinterpret_cast<string*>(y) << endl; 

C++ 표준은 이러한 유형의 최소값을 지정하지만 최대 값이 아니므로 새 컴파일러를 사용할 때 상대 할 대상을 실제로 알지 못합니다. 참조 : What does the C++ standard state the size of int, long type to be?

이 경로를 시작하고 해당 언어의 안전을 캐스팅하면 이동성이없는 코드를 작성할 가능성이 높아집니다. reinterpret_cast은 ...

When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?

캐스팅의 가장 위험한 유형입니다 그러나 그것은 단지 기술적 경우에 당신이 관심, 아래로, 특히 "왜 int로하지"부분에 드릴입니다. 아래의 주석에서 @BenVoight가 지적하고있는 바와 같이, 어떤 poniter를 보유하도록 보장 된 intptr_t이라는 C99의 정수 유형이 있습니다. 실수로 잘못된 유형으로 캐스팅하는 것과 같이 정밀도를 잃는 것보다 유형 정보를 버리는 경우 훨씬 더 큰 문제가 있습니다!

+0

우수 답변 - 고마워요! –

+1

'intptr_t'는 올바른 크기로되어 있습니다. 실제 문제는 정밀도의 손실이 아니라 컴파일 타임 형식 정보의 손실입니다. –

+0

@BenVoigt 물론입니다. 그러나 나는 다른 사람들이 그 점에서 말한 모든 것을 다룰 수있는 답을하지 않으려 고하지 않았으며 다른 누구도 가지고 있지 않았기 때문에 캐스팅을 보여주었습니다. 'intptr_t'는 다른 곳의 프로그램에서 흔히 볼 수있는 타입이 아니기 때문에 언급하지 않았습니다 ... 당신이 발견 할 수있는 루틴은 거의 매개 변수로 사용하지 않을 것입니다. 뭔가를 intptr_t로 변환하면 올바른 용량을 가지지 만, "char *"일 수있는 "freak"값을 얻을 수 있습니다. – HostileFork

2

요약하면 C는 유형이 지정된 언어입니다. 변수에 임의의 것을 저장할 수 없습니다.

위키 백과의 type safety 문서를 확인하십시오. C/C++은 피연산자 및 함수 매개 변수의 유형을 확인하여 불완전한 시간에 문제가있는 작업 및 함수 호출을 방지합니다 (단, 명시 적 형변환을 사용하면 표현식의 유형을 변경할 수 있음).

문자열을 정수로 저장하는 것은 의미가 없습니다.> 포인터를 저장할 때와 같은 방식입니다.

+0

포인터를 정수형 (캐스트 포함)에 저장할 수 있다는 점을 제외하고는. 당신이 할 때 단지 그것의 타입 정보를 잃어 버린다. –

+0

음 .. 나는 당신이 * 포인터를 정수형 (타입을 잃어 버림)과 * 다음에 * 포인터로 저장할 수 있다고 말할 것이다. (그러나 포인터를 이미 저장하고있다.) –

3

C 및 C++은 런타임이 아닌 컴파일 타임에 유형 정보를 확인합니다. 런타임 다형성조차도 컴파일 타임에 고정 된 오프셋을 가진 함수 포인터 테이블을 구성하는 컴파일러에 의존합니다.

그 이유는 프로그램에서 cout << *y;이 문자열을 인쇄하고 있다는 것을 알 수있는 유일한 방법은 y이 문자열 포인터 (std::string*)로 강력하게 입력 되었기 때문입니다. 프로그램은 주소만으로 주소 y에 저장된 객체가 std::string임을 결정할 수 없습니다. (심지어 C++ RTTI는 이것을 허용하지 않는다. 다형성 기본 클래스를 식별하기위한 충분한 타입 정보가 필요하다.)

+0

그래서 생각은 포인터 전체 "보기, 우리가 지금 알지 못하는 특정 데이터 구조를 시작하는 메모리 주소를 처리하는 좋은 방법 일뿐입니다!" 문제? –

+0

@ Joesavage1 : 데이터에서 찾을 수 없으므로 데이터의 구조 (유형)를 선험적으로 파악하는 것이 좋습니다. 런타임시 포인터는 주소 만 포함하지만 컴파일시 포인터 유형의 변수에도 컴파일러에게 가리키는 데이터의 유형을 알리는 정보가 있습니다. 이것이''std :: string * '이기 때문에''cout << * y;'는 (숫자 대신에) 문자열을 출력하는 방법을 알고 있습니다. HostileFork의 대답을 살펴보면, 정수형을 포인터로 다시 변환하기 위해 컴파일러에게 무엇을 가리키고 있는지 말해야합니다. 그 정보는 사물과 함께 보관되지 않습니다. –

2

간단히 말해서, 메모리 주소는 포인터 인 타입을 가진다. 포인터는 int가 아니므로 int 변수에 포인터를 저장할 수 없습니다. int와 포인터가 대체 할 수없는 이유가 궁금하다면, 각각의 크기가 특정 제한 사항으로 정의 된 구현이며 크기가 같을 것이라는 보장이 없기 때문입니다.

예를 들어, @Damien_The_Unbeliever가 지적했듯이 64 비트 시스템의 포인터는 64 비트 길이 여야합니다. 그러나 int가 32 비트가 될 수는 있지만 길이가 길지 않아야 짧은 것도 아니고 짧은 것도 아닙니다.

각 데이터 유형이 고유 한 포인터 유형 인 이유는 각 유형 (특히 사용자 정의 유형)이 메모리에서 다르게 구조화되기 때문입니다. 유형이없는 (또는 void) 포인터를 참조 해제하려면 해당 데이터를 해석하는 방법을 나타내는 정보가 없습니다.반면에 보편적 인 포인터를 만들고 유형을 지정하는 "불편 함"을 없애려면 메모리의 각 엔티티를 해당 유형 정보와 함께 저장해야합니다. 이 작업은 수행 가능하지만 효율성이 떨어지며 효율성은 C++의 설계 목표에 달려 있습니다.

+0

이것은 왜'std :: string *'이'intptr_t'와 별개의 타입인지 설명하지 못합니다. 크기는 이유의 아주 작은 부분입니다. –

+0

@BenVoigt 나머지 질문에 희망을 두는 몇 가지 추가 사항을 작성했습니다. – jpm

+0

기본적으로 기술은 이론적으로 다른 방법을 통해 수행 될 수 있으므로 포인터는 필수적이지 않습니다. 그러나 오류 검출, 아키텍처 적응, 메모리에서 올바른 비트 수 읽기, 읽는 것은 (타입 현명한) 것인가? –

0

이 언어는 서로 다른 두 개념의 충돌을 방지하기 위해 노력하고 있습니다. 하드웨어 수준에서 두 비트의 집합이기는하지만.

디버거의 여러 부분에서 수동으로 값을 전달할 필요가 없으므로 숫자 값을 알 필요가 없습니다.

배열의 오래된 용도 외에도 포인터에 "10을 더하는"것은 의미가 없으므로 숫자 값으로 취급해서는 안됩니다. 유형 정보를 유지 컴파일러 으로

, 또한 실수를하지 못하도록 - 모든 포인터가 같은한다면, 다음 컴파일러는, 유용하게, 당신은 int으로 역 참조하려고하는지는 점을 지적 할 수 없습니다 string에 대한 포인터입니다.

+1

* 기침 * 포인터 산술 같은 종류의 폐허. – chris

+0

이것이 이유 인 경우 모든 포인터에 대해 단 하나의 유형 'ptr'만있을 수 있습니다. –

+0

@chris - I * am * 매우 녹슬지 만 - 이해할 수 있듯이 포인터 산술은 배열에서만 작동하며, 내가 이해하기 때문에 배열은 현대 C++에서 많이 사용되지 않습니다. 날 바로 잡을 수 있니? –

1

C++은 강력한 형식의 언어이며 포인터와 정수는 다른 형식입니다. 이러한 개별 유형을 작성함으로써 컴파일러는 오용을 감지하고 현재 수행중인 작업이 올바르지 않다고 말할 수 있습니다.

동시에 포인터 유형은 뾰족한 객체의 유형에 대한 정보를 유지합니다. 이중 주소를 얻는 경우 double*에 저장해야하며 컴파일러는 해당 포인터를 역 참조한다는 것을 알고 있습니다. double에 도착하십시오. 예제 코드에서 int y = &x; cout << *y; 컴파일러는 y을 가리키는 정보를 잃어 버리 겠지만 *y 식의 형식은 알 수 없으므로 operator<<의 다양한 오버로드 중 어느 것을 호출할지 결정할 수 없습니다. 컴파일러에서 과 비교하면 컴파일러에서 std::string*이라는 것을 알 수 있으며 컴파일러에서 y이 포함 된 모든 식을 정적으로 검사 할 수 있도록 std::string (이중형 또는 다른 형식이 아님)으로 역 참조한다는 것을 알고 있습니다. 항상 그런 것은 아니다 당신이 포인터가 개체의 단지 주소인지 생각 그리고 그 (int64보다는 int해야 할 64 비트 아키텍처에있는) 핵심 유형별로 표현할 수 있어야 동안 마지막으로

, . 포인터가 실제로 정수 값으로 표현할 수없는 다른 아키텍처가 있습니다. 예를 들어, 세그먼트 화 된 메모리를 가진 아키텍처에서, 객체의 주소는 세그먼트 (정수 값)와 세그먼트 (다른 ​​정수 값)에 대한 오프셋을 모두 포함 할 수 있습니다. 다른 아키텍처에서는 포인터의 크기가 모든 정수 유형의 크기와 다릅니다.

+0

두 개의 정수를 더 큰 정수로 저장할 수 있습니다.) –

+0

@BenVoigt :하지만 꼭 같은 의미는 아닙니다. 세그먼트가 겹칠 수 있고, 16 비트 x86의 실제 주소는 (IIRC)'세그먼트 << 2 + offset'이며, 세그먼트와 오프셋은 16 비트입니다. 실제 주소가 10bits 일지라도 세그먼트 정보는 중요하므로 전체 32 비트를 저장 한 다음 역 참조하기 전에 서로 다른 레지스터에 개별적으로로드해야합니다 ... 불가능하지는 않지만 컴파일러는 유형 정보를 실제로 잃어 버리는 장점이있는 기능을 제공합니다. –

+0

하지만 그건 정확히 어떻게 포인터가 작동 ... –

관련 문제