c 공부 2024.04.01
포인터 & 배열
배열은 동일한 유형의 데이터 항목을 모은 것이고, 구조체(structure)는 서로 다른 유형의 데이터 항목을 묶은 것.
배열명을 주소로 활용
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
#include <stdio.h>
int main()
{
int ary[3]; // 3개의 숫자를 저장할 수 있는 '박스'를 만듭니다. 이 박스들을 'ary'라고 부릅니다.
int i; // 'i'라는 이름의 카운터를 만듭니다. 이 카운터는 나중에 숫자들을 하나씩 살펴볼 때 사용됩니다.
// 첫 번째 박스(ary[0])에 10을 넣습니다.
*(ary + 0) = 10;
// 두 번째 박스(ary[1])에 첫 번째 박스의 값에 10을 더한 값을 넣습니다.
// 첫 번째 박스에는 10이 들어 있으므로 두 번째 박스에는 20이 들어갑니다.
*(ary + 1) = *(ary + 0) + 10;
// 사용자에게 세 번째 박스(ary[2])에 넣을 숫자를 입력하라고 요청합니다.
printf("세 번째 배열 요소에 키보드 입력 : ");
scanf("%d", ary + 2);
// 모든 박스를 살펴보며 안에 들어 있는 값을 출력합니다.
// 'i'를 0에서 시작하여 2까지 (총 3번) 증가시키면서 반복합니다.
for (i = 0; i < 3; i++){
printf("%5d", *(ary + i)); // 각 박스의 값을 출력합니다.
}
return 0; // 프로그램이 끝났음을 알립니다.
}
포인터로 연산이 되나 안되나를 알려주기 위해 저자가 이런코드를 알려준것
%5d
%5d는 C언어에서 printf 함수를 사용할 때 정수 값을형식화하여 출력하기 위한 지시어(format specifier).
이지시어는 출력될 정수를 위해 최소한 5칸의 공간을확보하라는 뜻.
만약 출력할 숫자의 자릿수가 5보다 작다면,나머지 공간은공백으로 채워짐. 이는 출력 결과를 더 읽기쉽게 만들어주며, 특히 여러 값을 일렬로 출력할 때 각 값사이의 간격을일정하게 유지하고 싶을 때 유용.
*(ary + 1) vs (ary + 1)
*(ary + 0) = 10;
이라고 쓰면, 이것은 “0번째 칸의 주소에 가서, 그 칸에 10을 넣어줘”라는 뜻. 그런데, 만약 *
를 빼고 (ary + 0) = 10;
이라고만 쓰면, 이건 “0번째 칸의 주소에 10을 넣어줘”라고 하는 것. 하지만 주소 자체에 숫자를 넣을 수는 없음. 주소로 칸을 찾아가서 그 칸 안에 숫자를 넣어야 함. 그래서 *
를 안 쓰면 컴퓨터는 혼란스러워 하면서 “이건 못 하겠어!”라고 에러를 냄.
요약하자면 *은 값을 가져오는거고 *이 없으면 주소를 가져오는건데 그 주소안의 값은 + 을 할 수 잇지만 그 주소의 숫자를 + 할 수가 없음
배열명 역할을 하는 포인터
배열은 주소라서 포인터에 저장가능,이 경우 포인터로도 연산식이나 대괄호를 써서 배열 요소를 쉽게 사용할 수 잇음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h> // 프로그램이 컴퓨터와 '대화'할 수 있게 도와주는 도구를 가져옵니다.
int main() // 프로그램의 시작점입니다.
{
int ary[3]; // 컴퓨터 메모리에 3개의 칸을 만들고, 이 칸들을 'ary'라고 부릅니다.
int *pa = ary; // 'pa'라는 도우미를 만들어 'ary'의 첫 번째 칸을 가리키게 합니다.
int i; // 'i'라는 카운터를 만듭니다. 이 카운터는 칸들을 하나씩 살펴볼 때 사용됩니다.
*pa = 10; // 'pa'가 가리키는 첫 번째 칸에 10을 넣습니다.
*(pa + 1) = 20; // 'pa'가 가리키는 두 번째 칸에 20을 넣습니다.
pa[2] = pa[0] + pa[1]; // 'pa'가 가리키는 세 번째 칸에 첫 번째와 두 번째 칸의 숫자를 더한 값을 넣습니다.
// 모든 칸들을 하나씩 살펴보며 그 안에 들어 있는 숫자를 화면에 보여줍니다.
for (i = 0; i < 3; i++) {
printf("%5d", pa[i]); // 각 칸의 숫자를 화면에 보여줍니다.
}
return 0; // 프로그램이 끝났음을 알립니다.
}
마지막 출력을 pa[i] 가 아닌
1
printf("%5d", *(pa + i)); // 포인터 연산을 이용해 각 칸의 숫자를 화면에 보여줍니다.
이런식으로 포인터가 가르키는 위치를 증가 시키면서도 보여줄 수 있음
배열명과 포인터의 차이
- 차이1 : sizeof
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h> // 프로그램이 컴퓨터와 '대화'할 수 있게 도와주는 도구를 가져옵니다.
int main() // 프로그램의 시작점입니다.
{
int ary[3]; // 'int' 타입의 값을 저장할 수 있는 3개의 칸을 가진 배열을 선언합니다.
int *pa = ary; // 'pa'라는 포인터를 선언하고, 이를 배열 'ary'의 시작 주소로 초기화합니다.
// 'ary'의 전체 크기와 'pa' 포인터의 크기를 출력합니다.
printf("Size of ary: %zu bytes\n", sizeof(ary));
printf("Size of pa: %zu bytes\n", sizeof(pa));
return 0; // 프로그램이 끝났음을 알립니다.
}
- 차이2: 변수와 상수의 차이가 있음.
포인터 pa 에 1을 더해서 다시 pa에 저장은 가능 하지만 배열명 ary 는 1을 더하는건 가능하지만 그 값을 다시 저장하는것을 불가능함
포인터는 값 변경 가능
pa = pa + 1
pa++ (✔)
배열명은 값 변경 불가능 ary = ary + 1
ary++ (✘)
마무리
▶ 키워드로 끝내는 핵심 포인트
배열명은 첫 번째 요소의 주소이다.
포인터에 배열명을 저장하면 포인터를 배열명처럼 사용할 수 있다.
배열명의 정수 덧셈은 가리키는 자료형의 크기를 곱해서 더한다. ·포인터의 뺄셈 결과는 배열 요소 간의 간격 차이를 의미한다.
▶표로 정리하는 핵심 포인트
| 구분 | 사용 예 | 기능 | | — | — | — | | 배열명 | int ary[3] ary ==&ary[0]; | 배열명은 첫 번째 요소의 주소 | | 배열명 + 정수 | int ary[3]; ary + 1; | 가리키는 자료형의 크기를 곱해서 더한다. ary + (1 * sizeof(*ary)) | | 배열명과 포인터는 같음 | int ary[3]; int *pa = ary; pa[1] = 10; | 포인터가 배열명을 저장하면 배열명처럼 쓸 수 있다 두 번째 배열 요소에 10 대입 | | 배열명과 포인터는 다르다 | ary++; ( x ) pa++; (0) | 배열명은 상수이므로 그 값을 바꿀 수 없지만 포인터는 가능 하다. |
▶확인 문제
- 다음과 같이 배열과 포인터가 초기화되고 그림처럼 메모리에 할당되었다고 가정합니다(1번 ~3번).
1
2
3
double ary[5] = {1.2, 3.5, 7.4, 0.5, 10.0 };
double *pa = ary;
double *pb = ary + 2;
주소
100 = 1.2, 108 = 3.5, 116 = 7.4, 124 = 0.5, 132 = 10.0
다음 각 항목의 값
- ary = ?
- *(ary + 1) = ?
- pa + 2 = ?
- pa[3] = ?
- *pb = ?
- pb-pa = ?
다음 중 사용법이 맞는 것은 O표, 잘못된 표현은 X표
- ary[5] → ( )
- ary++ 3++(*ary) → ( )
- pb[-2] → ( )
- *(pb+ 3) → ( )
- *(++pa) → ( )
다음은 포인터 pb로 세 번째 배열 요소부터 마지막 배열 요소까지 출력하는 코드, 빈칸을 채우기.
1
2
3
4
for (i = 0; i<3;i++){
printf("%.1lf ",*pb);
( ? )
}
▶답
첫 번째 문제:
ary
→ 배열ary
의 시작 주소100
(ary + 1)
→ary
배열의 두 번째 요소의 값3.5
pa + 2
→pa
포인터가 가리키는 주소에서 두 칸 뒤의 주소116
pa[3]
→pa
포인터가 가리키는 주소에서 세 번째 칸 뒤의 요소의 값 (즉,ary
의 네 번째 요소).124
pb
→pb
포인터가 가리키는 주소의 값7.4
pb
는ary
의 시작 주소에서 두 칸 뒤로 설정되었기 때문에ary
의 세 번째 요소 값pb - pa
→pb
와pa
사이의 요소 수.pb
는pa
에서 두 칸 뒤로 설정되었으므로 값은2
2
두 번째 문제:
ary[5]
→ (✘)ary
의 정의된 크기는 5이므로 인덱스 5는 정의 범위를 벗어남.ary++
→ (✘) 배열의 이름은 포인터 상수이므로 증가 연산을 할 수 없음.++(*ary)
→ (✔) 배열 첫 번째 요소의 값을 증가시킴.pb[-2]
→ (✔)pb
가 가리키는 주소에서 두 칸 앞의 요소의 값을 나타냄.(pb + 3)
→ (✘)pb
가 가리키는 주소에서 세 칸 뒤의 값을 참조하려 하지만,pb
는 이미ary + 2
로 초기화되어 있으므로 이는ary
의 정의 범위를 벗어남.(++pa)
→ (✔)pa
를 증가시킨 후 그 주소의 값을 참조.
세 번째 문제:
1
2
3
4
for (i = 0; i < 3; i++) {
printf("%.1f ", *pb); // pb 포인터가 가리키는 주소의 값을 출력합니다.
pb+=i; // pb 포인터를 다음 요소로 이동시킵니다.
}
가상컴퓨터
- 위의 링크를 타고가서 운영체제에 맞는거 다운로드 (그냥 next 하면됨 계속)
- 우분투면 우분투, 칼리면 칼리 각각의 iso 이미지 다운로드 받기(LTS) (느릴시 미러링 사이트로 가서 다운, 혹은 너무 느리면 다운받는시간이.. 취소했다가 다시 다운로드) ubuntu iso link
- vmware에 iso 올리고 설치
- 한국어 자판, 언어 설정하기 (이때 파일명은 old 버전으로 ! 파일경로 한국어 되면 프로그램 만들때 에러남)
더 자세한 정보 : VMware 가상머신 환경 구축 & Ubuntu 리눅스 설치(2022. 03. 06 작성)