사자자리
[C언어] 5주차 포인터 본문
컴퓨터 메모리
- 메모리 셀(cell)이 연속적으로 나열된 형태
- 하나의 메모리 셀에 1 byte 데이터가 저장 (1 byte = 8 bits)
- 각 메모리 셀에는 주소가 부여된다.
- 데이터는 각 데이터 크기에 필요한 만큼 메모리 셀을 차지한다. ex) short: 2 byte, int: 4 byte, double: 8 byte
- 한 데이터가 차지하는 메모리 셀의 개수는 sizeof( )로 알 수 있다.
변수와 메모리 주소
#include <stdio.h>
int main(){
int num = 10;
printf("변수 num의 주소: %p", &num);
return 0;
}
<실행 결과>
변수 num의 주소: 000000000062FE1C
- 생성된 변수는 메모리에 저장된다.
- & : 주소 연산자. 변수명 앞에서만 사용할 수 있다.
- %p : 메모리 주소의 서식 지정자. pointer의 p를 사용한다.
- 메모리 주소는 컴퓨터마다, 실행할 때마다 달라진다.
포인터 변수(포인터)
- 특정 변수의 메모리 주소를 저장하는 변수
- 주소를 이용해서 특정 변수에 접근할 수 있게 한다.
- 포인터 변수의 데이터형과 포인터 변수가 가리키는 변수의 데이터형은 일치해야 한다.
- 포인터 변수를 선언할 때, 자료형 뒤에 * (asterisk)를 붙인다. 위치는 상관없다.
int* pointer;
int *pointer;
int * pointer;
- * : 간접 참조 연산자. 포인터 변수가 가리키는 변수에 접근해서 값을 읽거나 변경한다.
#include <stdio.h>
int main(){
int *pointer; //포인터 변수 선언
int num = 10;
pointer = # //변수 num의 주소를 포인터 변수에 저장
printf("변수 num의 주소: %p\n", pointer);
printf("변수 num의 값: %d\n", *pointer); //간접참조연산자 사용
return 0;
}
<실행 결과>
변수 num의 주소: 000000000062FE14
변수 num의 값: 10
- 포인터 변수의 크기는 가리키는 변수의 데이터형에 상관없이 항상 같다.
#include <stdio.h>
int main(){
char *a; //char 변수의 크기: 1 바이트
int *b; //int 변수의 크기: 4 바이트
double *c; //double 변수의 크기: 8 바이트
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(b));
printf("%d\n", sizeof(c));
return 0;
}
<실행 결과>
8
8
8
포인터 변수의 초기화
- 포인터 변수를 초기화하지 않고 사용하면 실행 에러가 발생한다.
int *p; //쓰레기값을 가진다.
*p = 10; //실행 에러
- NULL 포인터: 포인터가 다른 변수를 가리키지 않을 때는 null(0)로 초기화한다.
#include <stdio.h>
int main(){
int *p = NULL;
printf("%p", p);
return 0;
}
<실행 결과>
0000000000000000
포인터의 연산
- 포인터에 연산을 취하면, 포인터가 가리키는 자료의 크기 단위로 증감한다.
- 포인터 변수 + n: 포인터가 가리키는 데이터형 n개 크기만큼 증가된 주소가 연산의 결과
- 포인터 변수 – n: 포인터가 가리키는 데이터형 n개 크기만큼 감소된 주소가 연산의 결과
- 포인터 변수끼리도 연산할 수 있다.
- 포인터의 증감 연산
#include <stdio.h>
int main(){
int arr[5] = {1, 2, 3, 4, 5};
int *p = &arr[0];
for (int i = 0; i < 5; i++, p++){
printf("%d ", *p);
}
return 0;
}
<실행 결과>
1 2 3 4 5
포인터와 배열
- 인덱스 없는 배열명 = 배열의 시작 주소
- 배열명은 포인터처럼 사용할 수 있다.
- arr = &arr[0]
#include <stdio.h>
int main(){
int arr[3] = {1, 2, 3};
printf("배열의 시작 주소: %p\n", arr);
printf("배열의 시작 주소: %p\n", &arr[0]);
return 0;
}
<실행 결과>
배열의 시작 주소: 000000000062FE10
배열의 시작 주소: 000000000062FE10
- *(arr + i) = arr[i]
#include <stdio.h>
int main(){
int arr[3] = {1, 2, 3};
printf("%d\n", *(arr + 2));
printf("%d\n", arr[2]);
return 0;
}
<실행 결과>
3
3
- 배열의 시작 주소로 초기화된 포인터 변수를 이용해서, 배열의 모든 원소에 접근할 수 있다.
- p + i = &p[i]
- *(p + i) = p[i]
#include <stdio.h>
int main(){
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
printf("배열명을 사용하는 경우\n");
for (int i = 0; i < 5; i++) printf("%d번째 원소: %d, 메모리 주소: %p\n", i, arr[i], arr + i);
printf("\n");
printf("포인터를 사용하는 경우\n");
for (int i = 0; i < 5; i++) printf("%d번째 원소: %d, 메모리 주소: %p\n", i, *(p + i), p + i);
return 0;
}
<실행 결과>
배열명을 사용하는 경우
0번째 원소: 1, 메모리 주소: 000000000062FDF0
1번째 원소: 2, 메모리 주소: 000000000062FDF4
2번째 원소: 3, 메모리 주소: 000000000062FDF8
3번째 원소: 4, 메모리 주소: 000000000062FDFC
4번째 원소: 5, 메모리 주소: 000000000062FE00
포인터를 사용하는 경우
0번째 원소: 1, 메모리 주소: 000000000062FDF0
1번째 원소: 2, 메모리 주소: 000000000062FDF4
2번째 원소: 3, 메모리 주소: 000000000062FDF8
3번째 원소: 4, 메모리 주소: 000000000062FDFC
4번째 원소: 5, 메모리 주소: 000000000062FE00
포인터와 문자열 상수
- 문자열 리터럴: 큰따옴표("")로 둘러싼 문자의 연속체
- 문자열 리터럴은 메모리에 보관해두고 사용하기 때문에, 문자열 리터럴은 문자열 리터럴의 주소를 의미한다.
#include <stdio.h>
int main(){
char *p = "regulus"; //p에 "regulus"의 주소 저장
printf("문자열 리터럴: %s\n", p);
printf("문자열 리터럴의 주소: %p\n", p);
return 0;
}
<실행 결과>
문자열 리터럴: regulus
문자열 리터럴의 주소: 0000000000404000
- 문자열 리터럴은 메모리에 보관하지만, 변수처럼 값을 변경할 수 없다.
#include <stdio.h>
#include <string.h>
int main(){
char *p = "regulus";
strcpy(p, "black"); //실행 에러
printf("%s", p);
return 0;
}
- char*형 변수에 다른 문자열 리터럴의 주소를 대입할 수는 있다.
문자열 포인터
- char*형 변수에는 문자열의 주소와 문자열 리터럴의 주소를 저장할 수 있다.
#include <stdio.h>
#include <string.h>
int main(){
//문자열을 가리키는 경우
char str[20] = "Sirius Black";
char *p_str = str;
printf("%s\n", p_str);
//포인터로 문자열의 내용을 변경할 수 있다.
p_str[0] = 'G';
printf("%s\n", p_str);
strcpy(p_str, "Sirius White");
printf("%s\n", p_str);
//문자열 리터럴을 가리키는 경우
char *p_literal = "Regulus Black";
printf("%s\n", p_literal);
//포인터로 문자열 리터럴의 내용을 변경할 수 없다.
}
<실행 결과>
Sirius Black
Girius Black
Sirius White
Regulus Black
- 변경할 수 없는 문자열을 가리킬 때 const char*형을 사용한다.
const char *p = "abcde"
const 포인터
포인터가 가리키는 변수의 값 | 포인터 변수 자신의 값 (주소) | ||
const 데이터형 * 포인터명 | 읽기 O | 변경 X | 변경 O |
데이터형 * const 포인터명 | 읽기 O | 변경 O | 변경 X |
const 데이터형 * const 포인터명 | 읽기 O | 변경 X | 변경 X |
void 포인터
- 범용 포인터: 자료형이 정해지지 않은 포인터
- 어떤 자료형으로 된 포인터든 모두 저장할 수 있다. 암시적으로 자료형이 변환된다.
#include <stdio.h>
int main(){
int n = 10;
char c = 'A';
int *p_int = &n;
char *p_char = &c;
void *p_void;
//포인터 자료형이 달라도 컴파일 경고가 발생하지 않음
p_void = p_int;
p_void = p_char;
p_int = p_void;
p_char = p_void;
return 0;
}
- 단, 간접 참조를 하려면 명시적으로 형변환을 해야한다.
#include <stdio.h>
int main(){
int n = 1;
char c = 'A';
void *p;
p = &n;
printf("%d\n", *(int *)p); //void 포인터를 int 포인터로 형변환하고 간접 참조
p = &c;
printf("%c\n", *(char *)p); //void 포인터를 char 포인터로 형변환하고 간접 참조
return 0;
}
<실행 결과>
1
A
함수 포인터
- 함수를 저장하는 포인터: 함수를 호출할 수 있다.
#include <stdio.h>
float average(int a, int b){
return (float)(a+b)/2;
}
int main(){
printf("%p", average); //함수 이름도 포인터이므로 메모리 주소가 있다.
return 0;
}
<실행 결과>
0000000000401530
함수 포인터의 선언
- 함수의 반환값의 자료형, 매개변수의 자료형과 개수가 일치해야 한다. 매개변수가 없으면 ()만 붙인다.
반환값의 자료형 (*포인터명) (매개변수의 자료형);
float (*avg) (int, int);
- 함수를 호출할 때 간접 참조하지 않아도 된다.
#include <stdio.h>
float average(int a, int b){
return (float)(a+b)/2;
}
int main(){
int a = 1, b = 2;
float (*avg)(int, int); //함수 포인터 선언
avg = average; //함수 포인터에 average 함수의 메모리 주소 저장
printf(avg(a, b));
return 0;
}
<실행 결과>
1.500000
[코딩도장] 34.9 연습문제: 포인터와 주소 연산자 사용하기
다음 소스 코드를 완성하여 10과 20이 각 줄에 출력되게 만드세요.
#include <stdio.h>
int main()
{
int *numPtr;
int num1 = 10;
int num2 = 20;
① ________________
printf("%d\n", *numPtr);
②_________________
printf("%d\n", *numPtr);
return 0;
}
답
#include <stdio.h>
int main()
{
int *numPtr;
int num1 = 10;
int num2 = 20;
numPtr = &num1;
printf("%d\n", *numPtr);
numPtr = &num2;
printf("%d\n", *numPtr);
return 0;
}
'C언어 > C언어 이론' 카테고리의 다른 글
[C언어] 6주차 함수의 활용 (0) | 2022.06.25 |
---|---|
[C언어] 6주차 구조체 (0) | 2022.05.27 |
[C언어] 5주차 배열과 문자열 (0) | 2022.05.20 |
[C언어] 4주차 함수 (0) | 2022.05.14 |
[C언어] 4주차 제어문 (0) | 2022.05.14 |