1. 포인터 연산
아래 예제를 보자.
1
2
3
4
5
6
7
8
9
10
11
12
|
int main(void) { //포인터 연산
int i = 10;
int* pi = &i;
printf("i = %d, pi = %d\n",i,pi);
(*pi)++; //주소가 가리키는 변수를 찾아가 증가
printf("i = %d, pi = %d\n", i, pi);
*pi++; //주소값을 증가
printf("i = %d, pi = %d\n",i,pi);
return 0;
}
|
cs |
(*pi)++ 같은 경우는 연산자 우선 순위에 따라 포인터 변수가 가리키는 주소에 있는 값을 조정한다. 선언부에서 pi는 정수형 변수 i의 주소값을 가져왔고, i의 값이 바뀌게 된다.
그러나 *pi++ 같은 경우 pi의 주소값 그 자체를 증가시킨다는 소리이다. pi는 정수형 포인터 변수이므로 정수의 데이터 크기, 즉 주소값이 4byte만큼 커지게 된다.
1
2
3
4
5
6
7
8
9
|
int main(void) { //포인터 연산
int a[] = { 10,20,30,40,50 };
printf("a \ %u\n",a); //배열 이름은 포인터 값, 포인터 그대로 출력
printf("a+1 \ %u\n",a+1);
printf("a \ %d\n",*a); //포인터가 가리키는 값 출력
printf("a+1 \ %d\n",*(a+1));
return 0;
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
int main(void) { //포인터 연산
char* pc;
int* pi;
double* pd;
pc = (char*)10000;
pi = (int*)10000;
pd = (double*)10000;
printf("증가 전 pc = %d, pi = %d, pd = %d\n", pc, pi, pd);
pc++;
pi++;
pd++;
printf("증가 후 pc = %d, pi = %d, pd = %d\n", pc, pi, pd);
printf("pc+2 = %d, pi+2 = %d, pd+2 = %d\n", pc+2, pi+2, pd+2);
return 0;
}
|
cs |
포인터 값을 직접 선언할 수도 있다. char형은 데이터의 크기가 1byte이고 int형은 4byte, double형은 8byte이기 때문에 그 값에 맞게 증가한 것을 확인할 수 있다.
2. 배열 포인터
이전 시간과 오늘까지 이어 포인터 변수에 연산은 입력된 값만큼 바뀌는 것이 아닌 메모리의 크기만큼 바뀐다는 것을 학습했다. 또한 배열 변수명이 해당 배열의 0번째 자리를 가리키는 포인터 변수라는 것도 학습했다. 이번 시간에는 저번 시간에 이어 배열 포인터를 사용하는 예제를 더 살펴보며 배열 포인터에 대해 자세히 익혀보도록 하자.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
int main(void){ //배열포인터1
int arr[] = { 1,2,3 };
int s, * p;
p = &arr; //p = &arr[0];
s = (*p);
for (int i = 0; i < 3; i++) {
s = s * *(p + i);
}
printf("\narr[0] * arr[1] * arr[2] = %d\n", s);
return 0;
}
|
cs |
일반적인 변수와 그 변수의 포인터 변수가 있다고 가정했을 때, 포인터 변수에서 연산을 할 경우 가리키는 주소값이 바뀌어 둘의 상관관계는 사라진다. 그러나 배열에서는 다르다. 한 배열의 0번째 자리를 받아온 포인터 변수에 1을 더할 경우, 그 배열의 1번째 자리를 가리킨다. 이때, 자료형의 크기에 따라 늘어나는 것은 동일하므로 포인터 변수와 배열의 자료형은 같아야 한다.
따라서 *(p+i) 은 반복문에 따라 i가 1일 경우는 배열의 1번째 자리를 가리켜 해당 위치의 값이 되고 2일 경우 2번째 자리의 값이 되는 것이다.
아래는 배열에서 홀수 번째 자리의 값만 더하는 프로그램이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
int main(void) { //배열포인터2
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
int w = 0, * p = arr;
for (int i = 0; i < sizeof(arr)/4; i++) {
printf(" > arr[%d] = %d\n", i, arr[i]);
}
for (int i = 1; i < sizeof(arr) / 4; i += 2) {
w += *(p + i);
}
printf("\n >> arr[1] + arr[3] + arr[5] + arr[7] + arr[9] = %d\n",w);
}
|
cs |
sizeof(arr)/4 의 경우 예전에도 사용한 적이 있다. 배열의 크기가 바뀔 경우 소스 코드를 일일이 수정할 필요가 없도록 코딩한 것인데, sizeoff 를 통해 배열 데이터의 크기를 구하면 전체 크기인 (배열 원소)*자료형의 크기 값이 나온다. 따라서 int 자료형의 크기인 4(byte)로 나누어 준 것이다. 그 외에는 이전 예제와 크게 다르지 않다. 배열 포인터의 사용 방법을 차근차근 익혀보자.
아래 예제는 위 두 예제보다는 살짝 난이도가 높다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
int main(void) { //배열포인터3
int arr[] = { 1,2,3,4,5,0 };
int* p, max = arr[0], min = arr[0];
int maxloc, minloc;
p = arr;
printf("arr[] = {");
for (int i = 0; i < sizeof(arr) / 4; i++) {
if (*(p + i) > max) {
max = *(p + i);
maxloc = i + 1;
}
if (*(p + i) < min) {
min = *(p + i);
minloc = i + 1;
}
printf(" %d ", *(p + i));
}
printf("}\n");
printf("최대값 : %d, 위치 : %d\n", max, maxloc);
printf("최소값 : %d, 위치 : %d\n", min, minloc);
return 0;
}
|
cs |
*(p + i)가 의미하는 바는 앞에서 공부했다. p는 arr배열의 0번째 원소의 주소 값을 가지고 있다. 따라서 더해지는 i 값에 따라 배열에서의 해당 위치를 가리키게 된다. 그 값을 불러와 비교를 해 배열에서의 최대값, 최소값 과 그 위치를 구하는 프로그램이다.
아래 예제는 라이브러리 함수를 사용하지 않고 입력받은 문자열의 대소문자를 바꿔주는 프로그램이다.
int main(void) { //라이브러리 함수 없이 대소문자 변경
char str[50],*ptr = str;
int len; //문자열 길이
printf("Input english string : ");
gets(str, 50);
len = strlen(ptr);
for (int i = 0; i < len; i++) {
if (*(ptr + i) < 97) {
if (*(ptr + i) != 32) {
*(ptr + i) = *(ptr + i) + 32;
}
}
else {
*(ptr + i) = *(ptr + i) - 32;
}
}
while (*ptr) {
putchar(*ptr);
ptr++;
}
return 0;
}
3. 함수 포인터
함수 역시 포인터에 담을 수 있다.
void add(double num1, double num2); //함수 포인터
int main(void) {
double x = 3.1, y = 5.1;
void(*fp)(double, double); //함수 포인터 선언
printf("add 함수의 주소 : %p\n", add);
printf("함수 포인터의 주소 : %p\n", &fp);
printf("함수 포인터가 가리키는 주소 : %p\n", fp);
fp = add;
printf("함수 포인터가 가리키는 주소 : %p\n", fp);
fp(x, y);
return 0;
}
void add(double num1, double num2) {
double result;
result = num1 + num2;
printf("%.3f + %.3f = %.3f 입니다.\n", num1, num2, result);
}
함수의 자료형(*함수 포인터 이름)(); 식으로 선언해 사용할 수 있다.
4. void형 포인터
포인터 변수의 자료형을 void형으로 선언할 수 있다. 이렇게 선언할 경우, 자료형에 구애받지 않고 주소를 저장할 수 있으나 내용 변경이 불가능하고 강제 형변환하여 읽어올 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
int main(void) { //void형 포인터(모든 자료형의 주소를 저장할 수 있다, 내용 변경이 불가능하다, 강제 형변환하여 사용한다)
char c = 3;
double d = 3.1;
void* vx = NULL; //보이드형 포인터 선언
vx = &c;
printf("vx의 주소값 : %p\n", vx);
printf("vx의 값 : %d\n", *(char*)vx); //보이드형 포인터의 사용 방법
vx = &d;
printf("vx의 주소값 : %p\n", vx);
printf("vx의 값 : %.3lf\n", *(double*)vx);
printf("\n");
vx = &c;
*(char*)vx = 5;
printf("vx의 주소값 : %p\n", vx);
printf("vx의 값 : %d\n", *(char*)vx);
vx = &d;
*(double*)vx = 5.1;
printf("vx의 주소값 : %p\n", vx);
printf("vx의 값 : %.3lf\n", *(double*)vx);
return 0;
}
|
cs |
'수업 복습 > C 복습 노트' 카테고리의 다른 글
[C++] C++ 수업 1차시 (0) | 2021.08.30 |
---|---|
[C언어] C언어 수업 13차시 (0) | 2021.06.08 |
[C언어] C언어 수업 11차시 (0) | 2021.05.25 |
[C언어] C언어 수업 10차시 (0) | 2021.05.18 |
[C언어] C언어 수업 9차시 (0) | 2021.05.11 |
댓글