반응형
※ 본 자료는 위 책을 읽고 개인적으로 정리한 포스트임을 알려드립니다.
#포인터와 메모리
- 정적(Static)/전역(Global)
- 정적으로 선언된 변수는 정적/전역 메모리에 할당 -> Data영역이나 BSS영역
- 시작될 때 할당되며 종료될 때까지 메모리에 남아있음.
- 전역 변수는 모든 범위에서 접근, 정적 변수는 선언한 함수 범위내에서 접근
- 자동(Automatic)/로컬(Local)
- 함수 안에서 선언되고 함수가 호출될 때 생성됨
- 접근 범위는 선언한 함수 범위내
- 블록문안의서에 선언은 범위가 블록문 안으로 좁혀짐
- 동적(Dynamic)
- 동적 메모리는 힙 영역에 하당 되고 필요할 경우 해제됨
- 포인터를 사용하여 메모리 영역을 참조
- 해제하지 않는한 메모리 영역에 존재
# 포인터를 잘알아야 하는 이유
- 용도
- 빠르고 효율적인 코드작성
- 다양한 문제에 대한 효과적인 해결 방법 제공
- 동적 메모리 할당 지원
- 작고 간결한 표현의 사용
- 큰 오버헤드 없이 데이터 구조를 포인터로 전달
- 함수의 매개변수로 전달된 데이터 보호
# C 표준 문서
# 포인터를 이용할 때 발생하는 문제
- 배열이나 데이터 구조의 경계를 넘는 접근
- 소멸한 자동/로컬 변수의 참조
- 할당 해제된 힙 메모리의 참조
- 아직 할당되지 않은 포인터에 대한 역참조
# 포인터의 동작을 정의
- 구현 방법에 따라 정의된 행동(Implementation-defined behavior)
- 동작에 대한 문서화된 정의
- 정수에 대한 오른쪽 시프트 연산에서 상위 비트의 확장 방법
- 명시되지 않은 행동(Unspecified behavior)
- 동작 구현을 제공하지만 문서화는 되어있지 않음
- malloc 함수에 인자로 0을 주고, 실행할 떄 메모리가 얼마나 할당되는가 하는 것
- 정의되지 않은 행동(Undeifined behavior)
- 포인터에 대한 동작을 자유롭게 쓰도록하여 어떠한 동작도 발생할 수 있는 것
- 정의되지 않은 행동 목록 : CERT Secure Coding Appendix CC
- 이외
- 로케일 (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(){ ... } |
- 전역변수와 정적변수는 종종 스택과 힙이 위치한 데이터 세그먼트와 분리된 별도의 데이터 세그먼트에 위치
# 포인터의 크기와 데이터 타입
- 메모리 모델
- I In L Ln LL LLn P Pn
- size_t
- IL32P64
- I32LP64
- 출처 : https://en.wikipedia.org/wiki/64-bit_computing
Data model | short (integer) | int | long (integer) | long long | pointers, | Sample operating systems | |
LLP64, | 16 | 32 | 32 | 64 | 64 | ||
LP64, | 16 | 32 | 64 | 64 | 64 | ||
ILP64 | 16 | 64 | 64 | 64 | 64 | ||
SILP64 | 64 | 64 | 64 | 64 | 64 |
- 사전 정의된 포인터 관련 데이터 타입
- 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에서 찾을 수 있음
- 양수 할당 목적 (음수는 양수로 표현됨)
- 정의
- 출력 지정자에 따른 출력
- %zu : 변수를 부호없는 정수로 해석하여 최상위 비트가 2의 거듭제곱으로 해석
- %d : 부호있는 정수로 해석하여 최상위 비트가 1로 설정되면 음수로 해석
- 포인터 크기 : sizeof(char*) : 4
1 2 3 4 | #ifndef __SIZE_T #define __SIZE_T typedef unsigned int size_t; #endif |
- intptr_t와 unitptr_t 사용
- 두 타입은 포인터의 주소를 저장하는 데 사용
- 안전한 포인터 선언 방법을 제공
- 시스템 내부에서 사용하는 포인터와 같은 크기
- 정수 표현 변환시 유용
- 포인터를 정수로 캐스팅할 경우 정수가 4바이트인 경우 64비트 포인터를 정수로 캐스팅하면 32비트를 넘어간 데이터는 데이터 손실이 발생할 수 있다.
1 2 3 4 5 6 7 | int num; intptr_t *pi = # // 캐스팅 intptr_t *pi = # uintptr_t *pu = (uintptr_t*)# |
# 상수와 포인터
- 상수에 대한 포인터
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; |
반응형
'Programming > C/C++' 카테고리의 다른 글
C/Pointer/C포인터의 이해와 활용 - 3 (0) | 2018.03.13 |
---|---|
C/Pointer/C포인터의 이해와 활용 - 2 (0) | 2018.03.12 |
C/Symbolic Execution/KLEE Reference (0) | 2018.02.06 |
C/Symbolic Execution/KLEE Tutorials 1-2 Anaylsis (0) | 2018.01.17 |
C/리눅스 어셈블리 프로그래밍 - 1 (0) | 2017.12.15 |