포인터 변수
- 주소 값 만을 저장할 수 있는 변수
- 팬 핸들을 통한 변수 접근
- 즉, 변수명을 통한 접근
포인터 선언
int Date, Month; <- Date Month 변수는 정수 타입
int *p <- p가 가리키는 것은 정수 타입
float *q <- q가 가리키는 것은 부동소수 타입
동적 변수 공간 할당
- p = (int*) malloc(sizeof(int));
- *p = 15;
- *p 는 힙 메모리 변수, p는 스택 메모리 변수
C/C++의 동적 변수 할당
C | C++ | |
동적변수 할당 | p = (int *)malloc(sizeof(int)); | p = new int; |
동적변수 해제 | free p; | delete p; |
동적 변수 공간의 반납
- *p 공간은 반납
- p 공간은 그대로 유지
-> p 안에 있는 값도 그대로 유지
-> 결국, p를 따라가는 것은 문제가 됨
널 포인터
- '현재 아무것도 가리키지 않고 있다' 는 의미
- 값이 정의가 안된(Undefined) 포인터와는 의미가 다름
- 포인터를 통하여 가지 말아야 할 곳을 가지 않도록 조치하는 역할을 함 - free(p); p가 가리키는 변수 공간을 반납
- p = NULL; p를 따라가지 않도록
주소 연산자 앰퍼샌드(Ampersand, &)
int Date, month;
int *p;
p = &Date;
- &Date <- 'Address of Date'
- p = &Date; <- 변수 Date의 주소 값을 포인터 변수 p에 할당
- 변수 Date에 대한 두 가지 접근 방법: Date = 12 또는 *p=12;
Reference, Dereference
- *&Date = 15;
- *(&Date)는 '변수 date의 주소 값이 가리키는 곳의 내용(가리키는 곳의 공간)'
- 변수 Date의 주소 값이 가리키는 것이라는 것은 변수 Date 그 자체 - 변수를 주소 값으로 매핑(Mapping)시키는 함수가 앰퍼샌드(&)
- 주소 값을 그 주소가 가리키는 변수로 매핑시키는 함수가 애스터리스크(*)
- 역함수 이므로 *&Date = Date와 동일한 의미
저네릭 포인터(Generic Pointer)
- 포인터는 그것이 가리키는 변수의 타입에 따라 분류
- 가리키는 변수에 따라 읽어와야 할 데이터의 분량이 달라짐
- void *Ptr;
- 선언 시에는 가리키는 대상을 명시하지 않음
Ptr = (float*)malloc(sizeof(float));
- 타입변환 연산자에 의해 실행 시 포인터의 타입을 할당
- float *Ptr; 로 선언되었다면 (float*)는 없어도 된다.
- 단, 주석 효과를 기대하기 위해 사용할 수는 있다.
댕글링 포인터(Dangling Pointer)
int *p,*q;
p = (int*)malloc(sizeof(int));
*p = 15;
q = p;
free(p);
p = NULL;
*q = 30;
가비지(Garbage)
반납도 접근도 할 수 없는 공간
int *p, *q;
p = (int*)malloc(sizeof(int));
q = (int*)malloc(sizeof(int));
*p = 5; *q = 20;
p = q;
상수 변수, 상수 포인터, 배열
상수 변수
- int a = 24;
- const int* Ptr = &a;
상수 포인터
- 포인터가 항상 일정한 메모리 주소를 가리킴
- int* const Ptr = new int; *Ptr = 25;
- int a;
- Ptr = &a (이것은 오류)
배열
- 배열 이름은 배열의 시작 주소 값을 지님. 즉 포인터에 해당
- 배열은 프로그램 시작 시에 메모리 위치가 고정
- 따라서 배열 변수는 일종의 상수 포인터에 해당
참조 호출, 값 호출 (Call by Reference, Call by Value)
참조 호출
- Call by Reference
- 원본 전달 호출
값 호출
- Call by Value
- 사본 전달 호출
실제 파라미터, 형식 파라미터
- CallMe(int a); 가 있을 때, 호출 함수가 CallMe(n);으로 호출
- 호출 함수의 n = 실제 파라미터(Actual Parameter)
- 피호출 함수의 a = 형식 파라미터(Formal Parameter) - 참조 호출에서는 실제 파라미터와 형식 파라미터는 동일한 객체
- 값 호출에서는 실제 파라미터와 형식 파라미터가 서로 다른 독립적인 객체
에일리어스
앰퍼샌드(&)
- Address 연산자가 아님
- 에일리어스(Alias, Reference, 별명)을 의미
- 호출함수에서 던져준 변수 n을 '나로서는 일명 pi'로 부르겠다
- 참조호출의 효과(본명이나 별명이나 동일한 대상)
왜 에일리어스를 사용하는가?
- 포인터 기호 없이 간단하게 참조 호출
- 호출함수에서는 변수 자체를 전달
- 피호출 함수에서 에일리어스로 받으면 참조 호출, 그냥 받으면 값 호출
- 즉, 호출함수 입장에서는 피호출함수가 참조 호출을 지원하는 지, 값 호출을 지원하는지를 인식하지 않아도 됨
두 가지 스왑
void swap (int *x, int *y)
{ int temp;
temp = *x; *x = *y; *y = temp;
}
void main( )
{ int a = 20;
int b = 40;
swap(&a, &b); 변수 a, b의 주소 값 전달
cout << a; cout << b;
}
void swap (int& x, int& y) int& x와 int &x는 동일한 기호임
{ int temp;
temp = x; x = y; y = temp;
}
void main( )
{ int a = 20;
int b = 40;
swap(a, b); 변수 a, b를 그대로 전달
cout << a; cout << b;
}
C++의 복사 생성자
세 가지 경우 호출됨
- 함수 호출 시 객체가 파라미터로 전달될 때
- triangleClass T1; Call(T1); - 객체를 선언하면서 바로 다른 객체 값으로 초기화 했을 때
- triangleClass T1; T1.Base = 10; T1.Height = 20; triangleClass T2 = T1; - 피호출함수가 호출함수에게 객체를 리턴 값으로 전달할 때
- triangleClass T1; ..., return T1;
기본 복사 생성자
- 시스템이 자동으로 적용하는 디폴트 복사 생성자
- 프로그래머가 직접 관리하는 사용자 복사 생성자를 사용하는 것이 좋음
얕은 복사, 깊은 복사
- 사용자 복사 생성자 : 깊은 복사(Deep Copy)
- 디폴트 복사 생성자 : 얕은 복사(Shallow Copy)
복사 생성자와 할당 연산자
복사 생성자 | 할당 연산자 |
triangleClass T1; T1.Base = 10; T1.Height = 20; triangleClass T2 = T1; |
triangleClass T1, T2; T1.Base = 10; T1.Height = 20; T2 = T1; |
배열
직접 접근
- 인덱스 계산에 의해 해당 요소의 위치를 바로 알 수 있음
- &A[i] = &A[0] + i * Sizeof(Element Type)
배열과 포인터
배열의 정체는 다름 아닌 포인터
- 배열 요소를 포인터로 표현할 수 있음
- 포인터 산술연산(Pointer Arithmetic)
- A + 1 = A + 1 * Sizeof(Array Element)
배열의 전달
- 단순타입과 달리 값 호출로 배열요소가 복사되지는 않음
- 배열변수 이름, 즉 포인터 값만 값 호출로 복사되어 넘어감
- 따라서 피호출함수에서 작업을 가하면 이는 원본을 건드리는 참조호출 효과
동적 배열
정적 배열과 동적 배열
void Fuction1(int Size)
{
int MyArray[Size]; 정적 배열 선언
}
voic Function2(int Size)
{
int *MyArrayPtr = new int[Size]; 동적 배열 선언
...
delete[ ] MyArrayPtr;
}
- 정적 배열은 스택에(변수 선언에 의함) 생성됨
- 동적배열은 힙에 (malloc, new 등 함수 실행에 의함) 생성됨
- 정적 배열 크기는 컴파일 시에, 동적 배열 크기는 실행 중에 결정
- 동적 배열도 실행 중에 그 크기를 늘릴 수는 없음
구조체 - 구조체 타입선언
typedef struct
{
char Title[12];
int Year;
int Price;
} carType;
MyCar.Year = 2001;
carType MyCar {"RedSportCar", 2005, 2000};
Title | RedSportCar\0 | 200 |
Year | 2005 | 212 |
Price | 1200 | 216 |
구조체 배열
구조체 포인터
(*p).Year와 *p.Year는 다르다.
(*p).Year와 p->Year는 같다.
활성화 레코드 - 메모리 구성
High Address(높은 번지) | 힙(Heap) ò |
미사용 공간(Available) | |
스택(Stack) ñ | |
전역변수(Global Variables) | |
Low Address(낮은 번지) | 기계코드(Machine Code) |
메인함수 호출에 따른 활성화 레코드
int a; 전역 변수
void main()
{
int b; char *s; 지역변수(스택)
s = malloc(10); 10 바이트짜리 동적변수(힙)
b =fcn(5);
free(s);
}
힙 | *s | |
미사용 공간 | ||
스택 (main 함수의 활성화 레코드) |
Local Variables | b, s |
Return Address | 204 | |
Value Parameters | ||
Return Value | ||
전역변수 | a | |
기계코드 | main( ) fcn1( ) fcn2( ) |
fcn 호출에 따른 활성화 레코드
int fcn(int p) 파라미터(스택)
{
int b = 2;
b = b+p;
return (b);
}
힙 | *s | |
미사용 공간 | ||
스택 (fcn 함수의 활성화 레코드) |
Local Variables | b |
Return Address | 5번 | |
Value Parameters | p: 5 | |
Return Value | 7 | |
스택 (main 함수의 활성화 레코드) |
Local Variables | b, s |
Return Address | 204 | |
Value Parameters | ||
Return Value | ||
전역변수 | a | |
기계코드 | main( ) fcn1( ) |
활성화 레코드 스택
함수 호출
- 새로운 활성화 레코드 생성
- 연속적인 함수호출에 따라 활성화 레코드 스택이 생성
- 함수 종료시 제일 위의 활성화 레코드가 사라짐으로써 직전의 상황을 복원
무한 루프
- 스택 오버플로우(Stack Overflow)
표준 라이브러리 헤더와 프로그래밍 - C/C++ 명령 비교
C | C++ | |
동적변수 할당 | p = (int *)malloc(sizeof(int)); | p = new int; |
동적변수 해제 | free p; | delete p; |
화면출력 명령 | printf("%d\n", x); | cout << x << endl; |
'자료구조' 카테고리의 다른 글
리스트 - 2 (0) | 2023.03.19 |
---|---|
리스트 - 1 (0) | 2023.03.19 |
재귀 호출 (0) | 2023.03.19 |
추상 자료형 (0) | 2023.03.18 |
객체지향 방법론 (0) | 2023.03.17 |