Post

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

다음 각 항목의 값

  1. ary = ?
  2. *(ary + 1) = ?
  3. pa + 2 = ?
  4. pa[3] = ?
  5. *pb = ?
  6. pb-pa = ?

다음 중 사용법이 맞는 것은 O표, 잘못된 표현은 X표

  1. ary[5] → ( )
  2. ary++ 3++(*ary) → ( )
  3. pb[-2] → ( )
  4. *(pb+ 3) → ( )
  5. *(++pa) → ( )

다음은 포인터 pb로 세 번째 배열 요소부터 마지막 배열 요소까지 출력하는 코드, 빈칸을 채우기.

1
2
3
4
for (i = 0; i<3;i++){
  printf("%.1lf ",*pb);
  ( ? )
}

▶답

첫 번째 문제:

  1. ary → 배열 ary의 시작 주소

    100

  2. (ary + 1)ary 배열의 두 번째 요소의

    3.5

  3. pa + 2pa 포인터가 가리키는 주소에서 두 칸 뒤의 주소

    116

  4. pa[3]pa 포인터가 가리키는 주소에서 세 번째 칸 뒤의 요소의 (즉, ary의 네 번째 요소).

    124

  5. pbpb 포인터가 가리키는 주소의 값

    7.4

    pbary의 시작 주소에서 두 칸 뒤로 설정되었기 때문에 ary의 세 번째 요소

  6. pb - papbpa 사이의 요소 수. pbpa에서 두 칸 뒤로 설정되었으므로 값은 2

    2

두 번째 문제:

  1. ary[5] → (✘) ary의 정의된 크기는 5이므로 인덱스 5는 정의 범위를 벗어남.
  2. ary++ → (✘) 배열의 이름은 포인터 상수이므로 증가 연산을 할 수 없음.
  3. ++(*ary) → (✔) 배열 첫 번째 요소의 값을 증가시킴.
  4. pb[-2] → (✔) pb가 가리키는 주소에서 두 칸 앞의 요소의 값을 나타냄.
  5. (pb + 3) → (✘) pb가 가리키는 주소에서 세 칸 뒤의 값을 참조하려 하지만, pb는 이미 ary + 2로 초기화되어 있으므로 이는 ary의 정의 범위를 벗어남.
  6. (++pa) → (✔) pa를 증가시킨 후 그 주소의 값을 참조.

세 번째 문제:

1
2
3
4
for (i = 0; i < 3; i++) {
    printf("%.1f ", *pb); // pb 포인터가 가리키는 주소의 값을 출력합니다.
    pb+=i; // pb 포인터를 다음 요소로 이동시킵니다.
}

가상컴퓨터

vmware

  1. 위의 링크를 타고가서 운영체제에 맞는거 다운로드 (그냥 next 하면됨 계속)
  2. 우분투면 우분투, 칼리면 칼리 각각의 iso 이미지 다운로드 받기(LTS) (느릴시 미러링 사이트로 가서 다운, 혹은 너무 느리면 다운받는시간이.. 취소했다가 다시 다운로드) ubuntu iso link
  3. vmware에 iso 올리고 설치
  4. 한국어 자판, 언어 설정하기 (이때 파일명은 old 버전으로 ! 파일경로 한국어 되면 프로그램 만들때 에러남)

더 자세한 정보 : VMware 가상머신 환경 구축 & Ubuntu 리눅스 설치(2022. 03. 06 작성)

This post is licensed under CC BY 4.0 by the author.