본문 바로가기

Programming/C/C++

C/Pointer/C포인터의 이해와 활용 - 1

반응형

※ 본 자료는 위 책을 읽고 개인적으로 정리한 포스트임을 알려드립니다.


#포인터와 메모리
- 정적(Static)/전역(Global)
  • 정적으로 선언된 변수는 정적/전역 메모리에 할당 -> Data영역이나 BSS영역
  • 시작될 때 할당되며 종료될 때까지 메모리에 남아있음.
  • 전역 변수는 모든 범위에서 접근, 정적 변수는 선언한 함수 범위내에서 접근
- 자동(Automatic)/로컬(Local)
  • 함수 안에서 선언되고 함수가 호출될 때 생성됨
  • 접근 범위는 선언한 함수 범위내
    • 블록문안의서에 선언은 범위가 블록문 안으로 좁혀짐
- 동적(Dynamic)
  • 동적 메모리는 힙 영역에 하당 되고 필요할 경우 해제됨
  • 포인터를 사용하여 메모리 영역을 참조
  • 해제하지 않는한 메모리 영역에 존재

# 포인터를 잘알아야 하는 이유
- 용도
  • 빠르고 효율적인 코드작성
  • 다양한 문제에 대한 효과적인 해결 방법 제공
  • 동적 메모리 할당 지원
  • 작고 간결한 표현의 사용
  • 큰 오버헤드 없이 데이터 구조를 포인터로 전달
  • 함수의 매개변수로 전달된 데이터 보호

# C 표준 문서

# 포인터를 이용할 때 발생하는 문제
- 배열이나 데이터 구조의 경계를 넘는 접근
- 소멸한 자동/로컬 변수의 참조
- 할당 해제된 힙 메모리의 참조
- 아직 할당되지 않은 포인터에 대한 역참조

# 포인터의 동작을 정의
- 구현 방법에 따라 정의된 행동(Implementation-defined behavior)
  • 동작에 대한 문서화된 정의
    • 정수에 대한 오른쪽 시프트 연산에서 상위 비트의 확장 방법
- 명시되지 않은 행동(Unspecified behavior)
  • 동작 구현을 제공하지만 문서화는 되어있지 않음
    • malloc 함수에 인자로 0을 주고, 실행할 떄 메모리가 얼마나 할당되는가 하는 것
- 정의되지 않은 행동(Undeifined behavior)
  • 포인터에 대한 동작을 자유롭게 쓰도록하여 어떠한 동작도 발생할 수 있는 것
- 이외
  • 로케일 (locale-specific)에 따라 다른 동작 : 컴파일러 제조사에 따라 다름
    • 컴파일러 제조사가 문서화

# 포인터 선언을 읽는 방법
- 뒤에서 부터 읽음
- const int *pci; :  단계별 분류
  • 변수 pci
  • 포인터 변수 pci
  • 정수를 가리키는 포인터 변수 pci
  • 상수 정수를 가리키는 포인터 변수 pci

# 값 출력
- %p 값을 구현된 것에 따라 출력
- %d 10진수 출력
- %x 16진수 출력
- %o 8진수 출력

# NULL 개념
- 개념
  • 널 개념
  • 널 포인터 상수
  • NULL 매크로
  • NUL 아스키 문자
  • 널 문자열
  • 널 문장

- 0이거나 0이 아닐수도 있다.
  • 실제 C개발자는 널 상수의 내부적인 표현에 대해 신경 쓰지 않아도됨.

- NULL 매크로 선언
  • #define NULL ((void *)0)
  • 헤더 파일에서 자주 선언됨을 볼 수 있음

- NUL 아스키 문자
  • 모두 0으로 채워진 바이트로 정의

# 포인터 널 검사
1
2
3
4
5
if(pi) {
    // 널이 아닌 경우
else {
    // 널인 경우
}


# NULL 또는 0
- 어떤것을 사용하는게 적절?
  • 뭘 사용하든 문제되지는 않으나 NULL의 사용을 선호
  • NULL의 사용은 포인터이외에는 사용하지 않는것을 권장
  • NUL 아스키 문자 선언은 '\0'과 같으며 십진수 값 0을 의미

# void 포인터
- 어떤 타입의 데이터도 참조할 수 있는 범용 포인터 : 캐스팅
  • void *pv;
  • void 포인터는 char 포인터와 같은 표현과 메모리 정렬 방법을 사용한다.
  • void 포인터는 다른 포인터와 절대 같지 않다. 하지만 NULL 값이 할당된 두개의 void 포인터는 서로 같다. void 포인터의 실제 동작은 시스템에 따라 다르다.
- 사용
1
2
3
4
5
6
7
8
9
10
11
12
int num;
int *pi = #
printf("Value of pi: %p\n", pi);
void *pv = pi;
pi = (int*) pv;
printf("Value of pi: %p\n", pi);
 
/* 출력 
Value of pi: 100
Value of pi: 100
*/
 

# void에 size_t 사용 
1
2
size_t size = sizeof(void*); // 유효
size_t size = sizeof(void); // 유효하지 않음

# 전역 포인터와 정적 포인터
1
2
3
4
5
6
7
8
9
10
int *globalpi;
 
void foo() {
    static int *staticpi;
    ...
}
 
int main(){
    ...
}
- 전역변수와 정적변수는 종종 스택과 힙이 위치한 데이터 세그먼트와 분리된 별도의 데이터 세그먼트에 위치

# 포인터의 크기와 데이터 타입
- 메모리 모델

- 사전 정의된 포인터 관련 데이터 타입
  • size_t : 안전한 크기 타입 제공을 위해 사용
  • ptrdiff_t : 포인터 연산을 처리하기 위해 사용
  • intptr_t와 uintptr_t : 포인터 주소를 저장하기 위해 사용

- size_t
  • C언어에서 임의의 객체가 가질 수 있는 최대 크기를 나타냄
    • malloc함수와 strlen 함수가 size_t 타입을 반환, 인자로 사용
    • 부호 없는 정수 사용
    • 반복문(loop)의 카운터나 배열의 참조 그리고 때로는 포인터 연산에 사용
    • size_t 선언은 구현방법에 따라 정의된 행동(implementation-defined ehavior)이며 stdio.h나 stdlib.h에서 찾을 수 있음
    • 양수 할당 목적 (음수는 양수로 표현됨)
  • 정의
  • 1
    2
    3
    4
    #ifndef __SIZE_T
    #define __SIZE_T
    typedef unsigned int size_t;
    #endif
  • 출력 지정자에 따른 출력
    • %zu : 변수를 부호없는 정수로 해석하여 최상위 비트가 2의 거듭제곱으로 해석
    • %d : 부호있는 정수로 해석하여 최상위 비트가 1로 설정되면 음수로 해석
  • 포인터 크기 : sizeof(char*) : 4

- intptr_t와 unitptr_t 사용
  • 두 타입은 포인터의 주소를 저장하는 데 사용
  • 안전한 포인터 선언 방법을 제공
  • 시스템 내부에서 사용하는 포인터와 같은 크기
  • 정수 표현 변환시 유용
  • 1
    2
    3
    4
    5
    6
    7
    int num;
    intptr_t *pi = #
     
    // 캐스팅
    intptr_t *pi = #
    uintptr_t *pu = (uintptr_t*)#
     
  • 포인터를 정수로 캐스팅할 경우 정수가 4바이트인 경우 64비트 포인터를 정수로 캐스팅하면 32비트를 넘어간 데이터는 데이터 손실이 발생할 수 있다.

# 상수와 포인터
- 상수에 대한 포인터
1
2
3
4
5
6
7
8
int num = 5;
const int limit = 500;
int *pi;            // 정수 포인터
const int *pci;        // (상수 정수) 포인터
 
pi = #
pci = &limit;
 
  • 포인터 값은 변경가능
  • 상수값 변경 x
  • 상수 값을 변경하기 위해 포인터 역참조를 할 수 없음 : *pci = 200;

- 비상수를 가리키는 상수 포인터
1
2
int num;
int *const cpi = #
  • cpi 변수는 비상수 변수로 초기화 되어야 한다.
  • cpi 변수는 변경될 수 없다.
  • cpi 포인터 변수는 가리키는 변수의 값은 변경할 수 있다.
  • cpi 변수를 역참조하여 값을 변경하는 것이 가능하다. : *cpi = 25;
  • cpi 변수를 상수 정수로 초기화하면 컴파일러가 경고 때림
  • 1
    2
    const int limit = 500;
    int *const cpi = &limit;

- 상수를 가리키는 상수 포인터
1
2
int num;
const int * const cpci = #
  • 변수 값도 변경 x
  • 포인터 값도 변경 x
  • 선언과 함께 초기화

- 다중 수준 상수 포인터
1
2
const int * const cpci = &limit;
const int * const * pcpci;



반응형