1. 필수 기반 지식

더보기
필수 기반 지식

 

  • 2진수, bit, Byte, 16진수
  • 프로그래밍) 빌드, 컴파일, 링킹(함수) 
  • 컴퓨터) 실행 개념과 메모리, CPU 동작구조
  • 프로그래밍 언어, 어셈블리, 기계어
  • C 언어의 자료형과 함수
  • 스텍 메모리 동작 구조

 

배열과 포인터는 연산자 &  *  [ ] 만 이해하면 된다.
그리고, 배열과 포인터는 다르다.
하지만, 학습 초기에는 "같다" 고 가정하고 접근하여 익숙해지는게 먼저다.

 

 

연산자 우선순위

우선순위 연산자 유형 연산자 결합방향
높다 1 괄호, 배열, 구조체 ( )     [ ]    .     →  좌 → 우
  2 단항연산 -    !    ~    ++    --    (type)    &    *    sizeof 좌  우
3 산술 연산 승제 *    /    % 좌 → 우
4 가감 +    -
5 비트 이동 연산 >>    <<    
6 관계 연산 비교 <    <=    >    =>
7 등가 ==    !=
8 비트 논리 연산 &    |    ^
9 논리 연산 &&    ||
10 삼항(조건) 연산 ? : 좌  우
11 대입 연산 대입 =
복합 대입 +=    -=    /=    %=
축약 비트 대입 >>==    <<==
낮다 12 나열 연산     좌 → 우

 

2. 포인터 연산자 사용법

더보기

2.1 포인터 변수 선언, 메모리 주소값 할당

 

#include <stdio.h>

void main()
{
	int iNum = 90;

	printf("iNum 변수의 정수값: %d\n",  iNum);  // num 변수에 담긴 "정수 값 90" 출력
	printf("iNum 변수의 주소값: %p\n", &iNum);  // num 변수가 참조하는 첫번째 Byte "메모리 주소" 출력

    int* p_iNum = &iNum;                  // 실제로 p_iNum 같은 형태로 선언하지 않는다. 하나의 예시다.

	printf("p_iNum 포인터 변수 값: %p\n",  p_iNum);  // num 변수에 담긴 "정수 값 90" 출력
	printf("p_iNum 포인터 변수 역참조 값: %d\n", *p_iNum);  // num 변수가 참조하는 첫번째 Byte "메모리 주소 값" 출력
 }

 

 

 

 

2.2 포인터는 포인터다.

메모리 주소값은 주소값일 뿐이다. 변수는 변수다.

#include <stdio.h>

int main(void)
{
    int iNum2 = 89;                    // int형 변수를 선언
    int* p_iNum2 = &iNum2;             // &iNum2 int형 포인터 주소값을, char형 포인터로 변환 
    printf("p_iNum: %p\n",  p_iNum2);  // 포인터 변수명 p_iNum 만 사용한다. "변수명"만 사용한다. 
    printf("p_iNum: %d\n", *p_iNum2);  // 변수명 p_iNum 에 *역참조 연산자를 사용한다. -1, +10 

    return 0;
}

 

 

 

2.3 포인터 관련 연산자 상관관계 정리

 

포인터는 역참조를 위해 존재한다. 포인터의 역참조는 변수의 사용과 동일한 의미다.   

 

개념 예시 포인터는 "가르킨다"는 의미다.
변수 → 데이터 iNum → 90  일반 변수 사용 방법
변수 + & → 포인터 + * → 데이터 iNum + & → p_iNum + * → 90  
&변수 → 포인터 (&iNum) → p_iNum 메모리 주소
*포인터 → 데이터 *p_iNum → 90 메모리 주소의 데이터를 포인터한다.
*(&변수) → 데이터 *(&iNum) → 90 포인터 역참조는
변수의 사용과 동일한 방법이다.
변수 → 데이터 iNum → 90

 

3. 포인터 연산

더보기

*포인터 선언할 때 사용한 자료형 크기만큼, (자료형*숫자) Byte 만큼 연산한다.

*예시의 변수들은 순차적으로 저장된다 가정하고, 배열은 연속되고 순차적인 특성을 가진다.

 

 

3.1 예시 1

#include <stdio.h>

int main(void)
{
    char    cNum0 = 65,        cNum1 = 66,        cNum2 = 67;
    char *p_cNum0 = &cNum0, *p_cNum1 = &cNum1, *p_cNum2 = &cNum2;

                                                    // 변수명 : 주소값, 정수값
    printf("cNum0 : %p,  %d\n", p_cNum0, *p_cNum0); // cNum0  : 0x6F20,  65
    printf("cNum1 : %p,  %d\n", p_cNum1, *p_cNum1); // cNum1  : 0x6F21,  66
    printf("cNum2 : %p,  %d\n", p_cNum2, *p_cNum2); // cNum2  : 0x6F22,  67
    printf("\n");

    // 메모리에 순차적으로 저장된다고 가정                          // 포인터연산 : 주소값, 역참조
    printf("p_cNum0+0 : %p,  %d\n", p_cNum0+0,  *(p_cNum0+0)); // p_cNum0+0 : 0x6F20,  65
    printf("p_cNum0+1 : %p,  %d\n", p_cNum0+1,  *(p_cNum0+1)); // p_cNum0+1 : 0x6F21,  66
    printf("p_cNum0+2 : %p,  %d\n", p_cNum0+2,  *(p_cNum0+2)); // p_cNum0+2 : 0x6F22,  67

    return 0;
}

 


3.2 예시 2

#include <stdio.h>

int main(void)
{
    int    cNum0 = 97,        cNum1 = 98,        cNum2 = 99;
    int *p_cNum0 = &cNum0, *p_cNum1 = &cNum1, *p_cNum2 = &cNum2;

                                                      // 변수명 : 주소값, 정수값
    printf("p_cNum0: %p,  %d\n", p_cNum0, *p_cNum0);  // p_cNum0: 0x6F20,  97
    printf("p_cNum1: %p,  %d\n", p_cNum1, *p_cNum1);  // p_cNum1: 0x6F24,  98
    printf("p_cNum2: %p,  %d\n", p_cNum2, *p_cNum2);  // p_cNum2: 0x6F28,  99
    printf("\n");


    // 메모리에 순차적으로 저장된다고 가정                         // 포인터연산 : 주소값, 역참조
    printf("p_cNum0+0: %p,  %d\n", p_cNum0+0  *(p_cNum0+0));  // p_cNum0+0: 0x6F20,  97
    printf("p_cNum0+1: %p,  %d\n", p_cNum0+1, *(p_cNum0+1));  // p_cNum0+1: 0x6F24,  98
    printf("p_cNum0+2: %p,  %d\n", p_cNum0+2, *(p_cNum0+2));  // p_cNum0+2: 0x6F28,  99

    return 0;
}

 

int 포인터의 연산은 int 자료형의 크기인 4byte 기준으로 연산된다.

 

4. 배열의 포인터 연산

더보기

4.1 char 변수, char 배열, char 포인터 역참조

#include <stdio.h>

int main(void)
{
    char    cNum1 = 65,        cNum2 = 66,        cNum3 = 67;
    char *p_cNum1 = &cNum1, *p_cNum2 = &cNum2, *p_cNum3 = &cNum3;

                                                   // 변수명: 주소값, 정수값
    printf("cNum1: %p,  %d\n", p_cNum1, *p_cNum1); // cNum1: 0x7ff0,  70
    printf("cNum2: %p,  %d\n", p_cNum2, *p_cNum2); // cNum2: 0x7ff1,  71
    printf("cNum3: %p,  %d\n", p_cNum3, *p_cNum3); // cNum3: 0x7ff2,  72
    printf("\n");

    char cArr[3]  = {65, 66, 67};
    printf("cArr[0]: %p,  %d,  %d\n", cArr+0, cArr[0], *(cArr + 0));  // cArr[0] : 0xaaf0,  70,  70
    printf("cArr[1]: %p,  %d,  %d\n", cArr+1, cArr[1], *(cArr + 1));  // cArr[1] : 0xaaf1,  71,  71
    printf("cArr[2]: %p,  %d,  %d\n", cArr+2, cArr[2], *(cArr + 2));  // cArr[2] : 0xaaf2,  72,  72
    printf("\n");

    return 0;
}

 

*(cArr + 0) == cArr[0]
*(cArr + 1) == cArr[0+1]
*(cArr + 2) == cArr[0+2]  // 배열로 사용하면 * 역참조 연산자가 생략되고, [ ] 연산자가 사용된 형태다.

 

arr  <<<< 포인터, 주소값이 들어있는 배열명인 주소 값은 변경이 불가능한, 상수다.
arr 포인터 상수, 즉 포인터 변수처럼 메모리 주소가 들어있다.
arr[0] 의 주소값(첫번째 바이트 메모리 주소)이다.

 

 

 

 

4.2 int 변수, int 배열, int  포인터 역참조

#include <stdio.h>

int main(void)
{
    int    iNum0 = 97,        iNum1 = 98,        iNum2 = 99;
    int *p_iNum0 = &iNum0, *p_iNum1 = &iNum1, *p_iNum2 = &iNum2;

                                                      // 변수명 : 주소값, 정수값
    printf("p_iNum0: %p,  %d\n", p_iNum0, *p_iNum0);  // p_iNum0: 0x6F20,  97
    printf("p_iNum1: %p,  %d\n", p_iNum1, *p_iNum1);  // p_iNum1: 0x6F24,  98
    printf("p_iNum2: %p,  %d\n", p_iNum2, *p_iNum2);  // p_iNum2: 0x6F28,  99
    printf("\n");

    int iArr[3]  = {97, 98, 99}; 
    printf("iArr[0]: %p,  %d,  %d\n", iArr+0, iArr[0], *(iArr + 0));  // iArr[0]: 0x6F20,  97,  97
    printf("iArr[1]: %p,  %d,  %d\n", iArr+1, iArr[1], *(iArr + 1));  // iArr[1]: 0x6F24,  98,  98
    printf("iArr[2]: %p,  %d,  %d\n", iArr+2, iArr[2], *(iArr + 2));  // iArr[2]: 0x6F28,  99,  99
    printf("\n");

    return 0;
}

 

5. 함수와 배열

더보기
#include <stdio.h>
 
int main(void)
{
    int arr_1d[4] = {
        0,
    };
 
    //1 
    printf("       arr_1d    : %p\n",  arr_1d);
    printf("      *arr_1d    : %d\n", *arr_1d);
 
    //2
    printf("sizeof(arr_1d)   : %ld\n", sizeof(arr_1d));
 
    //3
    int *ptr = &(arr_1d[0]);
    printf("     &(arr_1d[0]): %p\n", ptr);
 
    return 0;
}

//        arr_1d    : 0x7fff8f21f4d0
//       *arr_1d    : 0
// sizeof(arr_1d)   : 16
//      &(arr_1d[0]): 0x7fff8f21f4d0

 

#include <stdio.h>

int change_Arr_1d(int *arr)
{
    printf("%p\n", arr);
    printf("입력하세요 >> ");
    scanf("%d", arr +0);  // arr[0]
    printf("입력하세요 >> ");
    scanf("%d", arr + 1); // arr[1]
    printf("입력하세요 >> ");
    scanf("%d", arr + 2); // arr[2]
    printf("입력하세요 >> ");
    scanf("%d", arr + 3); // arr[3]

    return 0;
}

int print_Arr_1d(int *arr_1d)
{
    for (size_t j = 0; j < 4; j++)
    {
        printf("%d ", arr_1d[j]);
    }   
    printf("\n");

    return 0;
}

int main(void)
{
    int arr_1d[4] = {
        0,
    };
    
    printf("%p\n", arr_1d);

    change_Arr_1d(arr_1d);
    print_Arr_1d(arr_1d);

    return 0;
}