/* 포인터 — & 는 주소를 꺼내고, * 는 그 주소로 찾아간다
   컴파일:  gcc c-pointer.c -o ptr -Wall
   사고 추적:  gcc c-pointer.c -o ptr -Wall -fsanitize=address  */

#include <stdio.h>

void 바꿔주기(int *p, int 새값);

int main(void)
{
    int 나이 = 20;

    /* 선언의 * 는 "포인터다" 라는 표시입니다 */
    int *p = &나이;            /* 나이가 있는 자리의 번호를 담는다 */

    printf("값:          %d\n", 나이);
    printf("포인터로:    %d\n", *p);      /* * 는 "찾아가라" */
    printf("나이의 주소: %p\n", (void *)&나이);
    printf("p 가 담은 값:%p\n", (void *)p);   /* 위와 같은 주소 */

    /* p 가 가리키는 자리를 고치면 원본이 바뀝니다 */
    *p = 30;
    printf("바꾼 뒤 나이: %d\n", 나이);       /* 30 */

    /* 함수에서 원본을 고치려면 주소를 넘깁니다 — scanf 가 & 를 요구하는 이유 */
    바꿔주기(&나이, 45);
    printf("함수가 바꾼 뒤: %d\n", 나이);     /* 45 */

    /* ── 배열 이름은 그 자체가 첫 원소의 주소입니다 ── */
    int 값들[5] = {10, 20, 30, 40, 50};
    int *q = 값들;              /* &값들[0] 과 같습니다 */

    printf("값들[2]     = %d\n", 값들[2]);
    printf("*(q + 2)    = %d\n", *(q + 2));   /* 같은 값입니다 */
    printf("q[2]        = %d\n", q[2]);       /* 포인터에도 [] 를 씁니다 */

    /* 포인터를 옮겨 가며 훑기 */
    for (int i = 0; i < 5; i++) {
        printf("%d ", *(값들 + i));
    }
    printf("\n");

    /* ── 포인터는 NULL 로 초기화하고 쓰기 전에 확인합니다 ── */
    int *아직없음 = NULL;
    if (아직없음 != NULL) {
        printf("%d\n", *아직없음);
    } else {
        printf("아직 아무 데도 가리키지 않습니다.\n");
    }

    /* 이 줄의 주석을 풀면 프로그램이 죽습니다 (Segmentation fault)
    printf("%d\n", *아직없음);
    */

    /* const 를 붙이면 실수로 고치는 것을 컴파일러가 막아 줍니다 */
    const int *읽기전용 = &나이;
    printf("읽기는 됩니다: %d\n", *읽기전용);
    /* *읽기전용 = 1;   ← 이 줄은 컴파일 오류가 납니다 */

    return 0;
}

void 바꿔주기(int *p, int 새값)
{
    if (p == NULL) {           /* 받은 주소부터 확인하는 습관 */
        return;
    }
    *p = 새값;
}
