목차
A. 배열과 포인터의 관계 |
B. 배열을 처리하는 함수 |
C. 정리 하기 |
A. 배열과 포인터의 관계
-컴파일러는 첫 번째 배열의 주소를 쉽게 사용하도록 배열명을 컴파일 과정에서 첫 배열 요소 주소로 바꿈.
자료형에 따라 첫 주소에서 크기만큼 건너뛰며 요소에 접근 가능.
____1. 배열명으로 배열 요소 사용하기
- 주소는 정수처럼 보이지만 자료형 정보를 갖고 있는 특별한 값. = 자유로운 연산은 할 수 없고 정해진 연산만 가능
주소 + 정수 -> 주소 + (정수 * 주소를 구한 변수의 크기)
크기가 4바이트인 int형 변수 a의 주소 100에 1을 더하면 104가 됨.
&a 가100이면 &a + 1은 104(int사이즈가 4일 때)
*(ary + 0) == *(100) == ary[0], *(ary + 1) == *(104) == ary[1],
&ary[2] 는 ary + 2가 연산 과정을 줄일 수 있음.
즉 배열의 대괄호는 포인터 연산의 '간접참조, 괄호, 더하기' 연산기능을 함. (배열 요소의 대괄호는 연산식이다.)
배열 영역을 벗어나는 포인터 연산은 가능하지만 쓰면 안 된다.
____2. 배열명 역할을 하는 포인터
- 배열명은 주소이므로 포인터에 저장 가능.
int ary[3];
int *pa = ary; // 배열명은 주소다. 첫 번째 요소를 가리킨다.
*pa = 10;*(pa+1) = 20; 은 ary[0]=10; ary[1]=20;과 같다.
____3. 배열명과 포인터의 차이
sizeof 연산의 결과가 다름. 배열명에 사용하면 전체 배열의 크기를 구하고 포인터에 사용하면 포인터 하나의 크기를 구함.
- 번수와 상수 차이. 포인터는 가변 변수. 배열명은 상수. 더할 수 있지만 재 저장은 안됨.
- pa++는 되지만 arr++는 안 됨.
- 포인터의 변수적 특징을 활용하는 방법.
printf("%d", pa[0]); // pa를 배열명처럼 써서 첫 요소 출력
printf("%d", *(pa + 0)); // pa[0]를 그대로 포인터 연산식으로 바꾸는 방법
printf("%d", *pa); // 의미 없는 0과 괄호 제거하는 방법.
pa = pa + 1;이나 pa++;로 연속 엑세스 가능.
주의사항!!
포인터의 값이 변할 수 있어 값이 유효한지 확인 필요
포인테어 증가연산자와 간접 참조 연산자를 함께 쓸 때 전위 표현 사용하면 안됨. 첫 요소를 못 읽고 마지막에 쓰레기값 하나 읽음.
++(*pa)라고 쓰면 첫 번째 요소의 값이 1씩 증가.
____4. 포인터의 뺄셈과 관계 연산
- 포인터 빼기 포인터는 ? (값의 차 / 가리키는 자료형의 크기)
- 관계 연산자로 대소 비교도 가능.
#include <stdio.h>
int main(void)
{
int ary[5] = { 10, 20, 30, 40, 50 };
int* pa = ary; // 첫 번째 배열 요소 주소
int* pb = pa + 3; // 네 번째 배열 요소 주소
printf("pa : %u\n", pa);
printf("pb : %u\n", pb);
pa++; // pa를 다음 배열 요소로 이동
printf("pb - pa : %u\n", pb - pa); // 두 포인터의 뺄셈
printf("앞에 있는 배열 요소의 값 출력 : ");
if (pa < pb) printf("%d\n", *pa); // pa가 배열의 앞에 있으면 *pa 출력
else printf("%d\n", *pb); // pb가 배열의 앞에 있으면 *pb 출력
return 0;
}
printf("pb - pa : %u\n", pb - pa); // 두 포인터의 뺄셈
pb - pa → (40 - 32) / sizeof(int) → 8 / 4 → 2
관계 연산자로 순서 확인 가능.
B.배열을 처리하는 함수
- 함수로 배열을 처리하려면 포인터가 필요. 포인터로 첫 요소의 주소를 주면 함수가 모든 배열 요소 계산 가능.
(예전에 c++로 주변 환경 정보를 스캔해서 배열로 다루는 자율주행차를 만들 때 배열을 넘겨주는 방법을 알아보니 포인터로 넘겨야 한다고 해서 왜 이러는지 이해가 안 갔는데 이제는 이해가 간다.)
____1. 배열의 값을 출력하는 함수
- 배열의 값 확인을 위해 수시로 출력해야 할 때 모든 배열 내용을 인자로 넘기기보다 시작 주소를 주면 포인터 연산으로 해결 가능. 함수를 호출할 때 배열명을 주고 매개변수로포인터 선언. (함수 안에서 포인터를 배열명처럼 사용하면 된다.)
____2. 배열 요소의 개수가 다른 배열도 출력하는 함수
- 요소 수가 가변적일 때.
#include <stdio.h>
void print_ary(int* pa, int size); // 함수 선언, 매개변수 2개
int main(void)
{
int ary1[5] = { 10, 20, 30, 40, 50 }; // 배열 요소의 개수가 5개인 배열
int ary2[7] = { 10, 20, 30, 40, 50, 60, 70 }; // 요소의 개수가 7개인 배열
print_ary(ary1, 5); // ary1 배열 출력, 배열 요소의 개수 전달
printf("\n");
print_ary(ary2, 7); // ary2 배열 출력, 배열 요소의 개수 전달
return 0;
}
void print_ary(int* pa, int size) // 배열명과 배열 요소의 개수를 받는 매개변수 선언
{
int i;
for (i = 0; i < size; i++) // size의 값에 따라 반복 횟수 결정
{
printf("%d ", pa[i]);
}
}
함수에서 인자를 전달할 때 배열의 사이즈를 전달하거나 sizeof로 계산해서 전달한다.
함수 안에서는 sizeof로 배열의 크기를 알 수 없음. 함수 밖에서 정의된 배열명을 함수 안에서 쓸 수 없는데 포인터에 sizeof연산을 하면 배열의 크기가 아니라 포인터의 크기만 알려줌.
결론: 포인터를 배열명처럼 쓰고 배열 크기를 전달해야 한다.
____3. 배열에 값을 입력하는 함수
#include <stdio.h>
void input_ary(double* pa, int size);
double find_max(double* pa, int size);
int main(void)
{
double ary[5];
double max; // 최댓값을 저장할 변수
int size = sizeof(ary) / sizeof(ary[0]); // 배열 요소의 개수 계산
input_ary(ary, size); // 배열에 값 입력
max = find_max(ary, size); // 배열의 최댓값 반환
printf("배열의 최댓값 : %.1lf\n", max);
return 0;
}
void input_ary(double* pa, int size) // double 포인터를 매개변수로 선언
{
int i;
printf("%d개의 실수값 입력 : ", size);
for (i = 0; i < size; i++) // size의 값에 따라 반복 횟수 결정
{
scanf("%lf", pa + i); // &pa[i]도 가능, 입력할 배열 요소의 주소를 전달
}
}
double find_max(double* pa, int size)
{
double max;
int i;
max = pa[0]; // 첫 번째 배열 요소의 값을 최댓값으로 설정
for (i = 1; i < size; i++) // 두 번째 배열 요소부터 max와 비교
{
if (pa[i] > max) max = pa[i]; // 새로운 배열 요소의 값이 max보다 크면 대입
}
return max; // 최댓값 반환
}
입력 함수에는 저장할 배열의 위치가 필요해서 포인터를 함수 안에서 직접 사용.
____4. 함수의 매개변수 자리에 배열을 선언하는 경우
- 배열의 저장 공간이 할당되지 않으며 컴파일 과정에서 첫 요소를 가리키는 포인터로 바뀜.
= 즉, 배열의 개수는 의미가 없다.
void func(int pa[5]) { ... } | void func(int *pa) { ... } |
void func(int pa[10]) { ... } | void func(int *pa) { ... } |
void func(int pa[]) { ... } | void func(int *pa) { ... } |
void func(double pa[5]) { ... } | void func(double *pa) { ... } |
// 배열 선언과 함수 호출
int ary[5] = { 1, 2, 3, 4, 5 };
print_ary(ary); // 함수 호출
// 함수 정의
void print_ary(int pa[5]) // 메모리 주소에 ary 배열과 같은 값을 갖는 배열 선언
{
int i;
for (i = 0; i < 5; i++)
{
printf("%d ", pa[i]); // *(pa + i) 형식으로 배열 요소의 값 출력
}
}
C. 정리하기
____1. 요약
- 배열명은 첫 요소의 주소.
-포인터에 배열명을 저장하면 포인터를 배열명처럼 쓸 수 있음.
-배열명의 정수 덧셈은 자료형 크기를 곱해서 더함.
-포인터의 뺄셈은 빼서 자료형 크기로 나눈다.(요소 간 간격(인덱스 차)의미.)
- 배열을 인자로 받는 함수에는 배열명(첫 요소의 주소)
- 배열의 길이를 미리 알 수 없다면 요소의 개수도 인수로 전달해야 한다.
____2 . 활용
![]() |
|
#include <stdio.h>
void input_nums(int *lotto_nums);
void print_nums(int *lotto_nums);
int main(void) {
int lotto_nums[6]; // 로또 번호를 저장할 배열
input_nums(lotto_nums); // 입력 함수 호출
print_nums(lotto_nums); // 출력 함수 호출
return 0;
}
void input_nums(int *lotto_nums) {
// 로또 번호를 사용자로부터 입력받는 함수
printf("Enter 6 lotto numbers: ");
for (int i = 0; i < 6; i++) {
scanf("%d", &lotto_nums[i]);
}
}
void print_nums(int *lotto_nums) {
// 로또 번호를 출력하는 함수
printf("Lotto numbers are: ");
for (int i = 0; i < 6; i++) {
printf("%d ", lotto_nums[i]);
}
printf("\n");
}
____3. 궁금한 점
-
'Tech > Coding' 카테고리의 다른 글
C언어 고급]🚀문자 (1) | 2024.03.29 |
---|---|
🐨BOJ#1697 숨바꼭질]🙄BFS(feat. collections) (1) | 2024.03.29 |
🐨백준-파이썬] 2630번 색종이 만들기& 1780번 종이의 개수🤔분할 정복 알고리즘(Divide and conquer algorithm) (0) | 2024.03.27 |
🐨백준-파이썬]# 13305번 주유소(그리디 알고리즘) (0) | 2024.03.25 |
🐨백준 파이썬]# 1541번-잃어버린 괄호(그리디 알고리즘) (0) | 2024.03.24 |