1. 포인터의 기본
포인터란
포인터
- 일반 변수의 메모리 내 주소번지를 갖는 자료형
- 포인터를 사용하면 변수명을 통하지 않고 사용하고자 하는 대상에 직접 접근을 할 수 있어 프로그램을 간결하고 효율적으로 제어할 수 있음
포인터의 자료형
- 주소 연산자 (&) - 일반 변수명에 사용. 해당 변수의 메모리 주소를 가리킴
- 간접 연산자 (*) - 포인터 변수명에 사용. 주소가 저장되어 있는 메모리 위치를 가리킴
1. 포인터 선언
1) 선언하는 방법
자료형 * 변수명;
int * ptr;
- *연산자가 1개면 1차원 포인터
- 1차원 포인터는 일반변수의 주소를 값으로 가짐
int i = 3;
int * ptr = &i;
여기서 변수 ptr은 1차원 포인터로, 일반 변수인 i의 주소값을 가리킨다. 그리고 int *은 변수 ptr에 정수형으로 선언된 변수의 주소가 들어올 수 있음을 의미한다.
2) 포인터 변수의 크기 = 4바이트
포인터 변수 ptr 자체의 크기는 4바이트로 char*로 선언되든 double*로 선언되든 상관없다. 다음 포인터 변수 3개는 크기가 모두 4바이트이다.
char* ptr1;
int* ptr2;
double* ptr3;
3) *의 위치: Anywhere
*는 선언하려는 자료형과 포인터 변수명 사이에만 위치하면 된다.
int* ptr1;
int * ptr2;
int *ptr3;
2. 포인터를 이용한 일반 변수로의 접근
int a = 10; 처럼 변수 a에 변수명으로 직접 접근할 수도 있지만, 포인터를 이용할 수도 있다.
int a = 10;
int *ptr = &i;
printf("ptr의 주소값: %p\n", &ptr);
printf("i의 주소값: %p\n", &i);
printf("i의 주소값: %p\n\n", ptr);
printf("i의 값: %d\n", i);
printf("i의 값: %d\n, *ptr);
- ptr은 변수 i의 주소값이다 (= &i)
- *ptr은 포인터가 가리키는 주소에 저장된 값이다 (= i)
- 포인터로 선언한 변수(=ptr)에 * 연산자를 붙이면, 포인터가 가리키는 주소에 저장된 값을 의미한다. 따라서 위에서 *ptr의 값은 10(= i)이다.
포인터를 이용하여 변수값 변경하기
int i = 3;
int *ptr = &i;
*ptr = i+2; //포인터를 통해 i값을 바꿈
위와 같이 포인터를 이용하여 포인터가 가리키는 곳의 값을 바꾸고 연산도 할 수 있다.
ex)
int i = 10;
int *ptr = &i;
*ptr = i+20;
printf("i값 : %d\n", i); // ➡️ i값: 30
i = i+20;
printf("i값 : %d\n", *ptr); // ➡️ i값: 50
따라서, int b = a;와 달리, 포인터를 이용하면 i와 ptr값이 서로 연결이 된다.
포인터의 유용성
위처럼 포인터를 이용하여 변수값 변경하기를 함수에 이용하자.
원래 함수를 사용하면 한 함수 속에서 사용한 지역변수는 함수 밖에서 그 결과값이 반영되지 않는다. 왜냐하면 말그대로 지역변수이기 때문에 함수 안에서만 사용하는 값이기 때문이다. 물론 return 값을 사용하여 밖으로 내보낼 수는 있지만, return을 사용하지 않고도 포인터를 이용하면 함수속에서도 변수의 값을 바로 변경할 수 있게 된다.
포인터를 함수의 인자로 사용하면 주소값을 전달할 수 있으므로 변수 자체가 함수에 전달된다.
(ex)
Swap(&x, &y);
…
void Swap(int* a, int* b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
위와 같은 함수를 작성하게 되면, 두 수를 바꾸기 위해 x와 y의 주소를 Swap()함수에 넘겨주면 Swap()함수에서 처리된 결과가 x와 y의 값에 반영된다.
2. 인자 전달 방법
인자(매개변수)의 전달 방법?
: 함수에 사용될 데이터를 보내는 방법
1. 인자를 값으로 전달(=Call by value)
C언어의 가장 대표적인 인자 전달 방식.
함수가 호출되면 인자값을 스택(Stack)이라는 기억장소에 복사한다. 값을 복사하기 때문에 호출된 함수에서 인자값을 바꾸더라도 main()함수는 전혀 영향 받지 않으므로 두 함수는 독립적이며 안전하다.
2. 인자를 주소로 전달(=Call by reference)
- 전달하려는 변수의 주소를 함수에 전달하는 방식.
- 포인터를 이용한 일반 변수로의 접근을 함수에 응용한 방법
함수에 변수 자체를 전달해야 할 때 사용하며, C언어에서는 포인터를 사용한다. 변수의 주소값을 주소 연산자(&)를 이용해 함수에 넘겨준다. 그 주소값을 받는 함수에서는 간접 연산자(*)를 이용해 이 주소값을 가리키는 값을 읽고 저장한다. 앞에서 다루었던 Swap()함수 예제가 여기에 해당한다.ㅜ주소를 이용해서 인자를 전달하면 여러 가지로 유용하다.
(ex)
int main(void) {
…
CountIncrement(&a);
}
void CountIncrement(int* n) {
(*n)++;
}
여기서, *n++;를 입력하게 되면 증가연산자가 더 우선순위가 높으므로 *(n+1)값을 출력하게 된다(n 다음 주소값이 가지고 있는 변수값을 출력하게 됨). (*n)++;로 입력하게 되면, 우선순위가 높은 ()괄호 속을 먼저 진행하게 되므로, n의 주소값에 들어있는 변수값을 +1한 값을 출력하게 된다.
3. 포인터와 배열
1. 포인터와 배열의 관계
배열명 : 해당 데이터가 있는 시작 주소를 의미하는 포인터 상수.
- 상수이므로, 배열명의 값을 바꾸면 안된다. 즉, 배열명에 증감연산자(++,--) 등을 이용해 배열명의 값을 바꿀 수 없다.
- ex) 배열명이 a라면, a = a+1처럼 배열명으로 연산할 수 없다.
- 하지만 포인터는 변수이므로 포인터에 증감연산자 등을 이용해 값을 바꿔도 된다.
즉, 그 배열의 첫 번째 원소가 저장된 주소를 의미한다. 따라서
int a[4] = [10, 20, 30, 40];
int *p = &a[0]
일 때, p = a와 p = &a[0]은 동일한 번지를 가리킨다.
배열 a의 주소를 표현하는 방법 3가지
구분 | 1 | 2 | 3 |
방법1(배열첨자) | &a[0] | &a[1] | &a[2] |
방법2(배열명) | a | a+1 | a+2 |
방법3(포인터) | p | p+1 | p+2 |
- 배열명: a = 배열의 시작 주소, a+1 = 배열 a의 두 번쨰 주소
- 따라서, +1은 산술적인 의미의 숫자가 아닌 그 다음 주소를 의미한다.
- 포인터 변수 p도 같은 방법으로 배열의 주소를 표시할 수 있다.
배열 a의 값을 포현하는 방법 4가지
구분 | 1 | 2 | 3 | 4 |
방법1 | a[0] | a[1] | a[2] | a[3] |
방법2 | *a | *(a+1) | *(a+2) | *(a+3) |
방법3 | *p | *(p+1) | *(p+2) | *(p+3) |
방법4 | p[0] | p[1] | p[2] | p[3] |
int *p = a;
*a = *p = a[0] = p[0]
a = 배열의 시작 주소, *a = 배열 a의 시작 주소의 값. 즉 첫 번째 값
4. 포인터와 문자열
포인터를 이용한 문자열 표현
선언 방법
char *포인터명 = "문자열";
(ex)
char *p = "MOON";
p = "M"문자의 주소값(배열의 첫 번째 주소값)
p+1 = "O"문자의 주소값
p+2 = "O"문자의 주소값
p+3 = "N"문자의 주소값
p+4 = \0 널문자 : 문자의 마지막을 나타냄
출력방법 2가지
예제1
char *pC = "C programming";
printf("%s\n", pC); => 문자열을 변환기호를 이용해서 출력
while(*pC)
printf("%c", *pC++); => 반복문을 이용해 한 글자씩 출력
*pC = pC의 첫 번째 주소값에 들어있는 값
*(pC+1) = pC의 첫 번째 주소값의 다음 주소값에 들어있는 값
*pC++는 문자 1개를 출력한 후 다음 위치로 이동하기 때문에 다음 문자를 출력하게 되는 것임
예제2
char *pStr[3] = {"english", "math", "korean"}
여기서 pStr[0] = "e"의 주소값, pStr[1] = "m"의 주소값, pStr[2] = "k"의 주소값
따라서 이와 같이 [0], [1], [2],는 각 문자열의 시작 주소를 가리킨다.
char *pStr[] = {"english", "math", "korean"}
위와 같이 배열의 크기를 초기에 지정하지 않아도 된다. 미리 지정하게 되면 불필요한 메모리의 낭비를 막을 수 있다.
'etc' 카테고리의 다른 글
VSCODE에서 한글로 작성된 부분만 검색하기 (0) | 2024.03.17 |
---|---|
맥북에서 원화(₩) 대신 백틱(`)만 뜨도록 설정하기 (0) | 2024.03.10 |
파이썬 설치 시 오류: 0x80070652 다른 설치가 이미 진행 중입니다. (0) | 2020.09.29 |