티스토리 뷰

목차

    반응형

    C언어를 다룰 때 포인터는 피할 수 없는 부분입니다. 능숙하게 사용할 수 있다면 프로그래밍 기술 폭이 넓어져 실력 올리기 좋습니다.


    이 포스트에선 C언어 포인터의 기초부터 응용 방법까지 소개합니다.


    포인터란

    포인터는

    C언어 포인터를 한마디로 표현하면 변수의 주소를 저장하는 변수입니다. 주소가 무엇이냐면 변수의 메모리상 위치(번지)를 의미합니다.


    변수를 선언할 때 사용하는 컴퓨터의 메모리가 변수 크기만큼 할당됩니다. 이 메모리상의 위치를 나타내는 말이 주소입니다.


    아래 그림에서 변수 "a" pointer는 10024입니다.





    위의 예시에선 주소를 10 진수로 표기했지만, 일반적으론 16 진수로 표시합니다.

    포인터 작성 방법

    C언어 포인터 변수 선언 방법은 변수 이름 앞에 "*"(별표)를 붙이는 것으로, 이렇게 선언하면 변수가 pointer임을 의미합니다.


    또한, 일반 변수의 주소를 얻으려면 변수 이름 앞에 "&"(앰퍼샌드)를 넣습니다.


    간단한 예는 다음과 같습니다.



    1
    2
    3
    4
    int number;     // 일반적인 변수 선언
    int *p_number;  // 포인터 변수 선언
     
    p_number = &number; // 변수 number 주소를 p_number에 대입
    cs



    위의 예제는 포인터 변수 p_number에 변수 number의 주소를 할당하는 것입니다. 또한 변수의 형식으로 int를 사용하고 있습니다만, 모든 데이터 타입으로도 사용할 수 있습니다.


    예를 들어, 구조체 typedef에서 선언한 구조체에도 적용할 수 있습니다. 구조체의 포인터에 대해서는 다음 부분에서 다룹니다.


    포인터 사용

    포인터와 배열

    배열로 선언된 변수는 포인터로 처리할 수 있습니다.


    선언 방법은 다른 포인터와 마찬가지로 "*"를 앞에 추가하며, 사용할 때도 앞에 &를 추가합니다.



    1
    2
    3
    4
    char  c_str1[5]; // 일반적인 변수 선언
    char *p_str1;    // 포인터 변수 선언
     
    p_str1 = &c_str1[0]; // 변수 c_str1의 소를 p_str1에 대입
    cs



    여기까지는 배열이 아닌 포인터와 비슷하지만, 배열의 경우 배열 요소 수를 나타내는 대괄호([n])를 생략하면 주소(즉 배열 변수의 포인터)를 가리키게 됩니다.


    그래서 다음과 같이 작성해도 똑같은 의미가 됩니다.



    1
    2
    3
    4
    char  c_str1[5]; // 일반적인 변수 선언
    char *p_str1;    // 포인터 변수 선언
     
    p_str1 = c_str1; // 변수 c_str1의 주소를 p_str1에 대입
    cs



    여기서 주의할 점은 [n]을 생략하면 반드시 첫 번째 요소를 가리킵니다. 즉, 배열의 n번째를 지정하고 싶은 경우는 생략할 수 없습니다.


    또한, strcpy, strcmp 등 문자열을 조작하는 표준 함수를 이용할 때는 주의할 점이 있습니다. 이러한 함수 인수에 사용할 때 형식은 char형 pointer가 됩니다.



    1
    2
    3
    4
    5
    6
    7
    char buf1[4], buf2[4];
    int result;
     
    strcpy(buf1, “AAA”); // char형 배열 변수에 "AAA"라는 문자열을 입
    strcpy(buf2, “AAB”); // char형 배열 변수에 "AAB"라는 문자열을 입
     
    out = strcmp(buf1, buf2); // buf1과 buf2를 비교. 비교 결과를 out에 
    cs



    포인터와 구조체

    위에서 조금 언급했지만, 구조체도 포인터로 사용할 수 있습니다. 구조체 포인터와 일반 구조체의 다른 점은 멤버변수에 접근하기 위한 연산자입니다.


    일반 C언어 포인터 멤버 변수에 접근할 경우 "." (도트 연산자)를 사용하지만, 포인터로 선언된 구조체의 멤버 변수에 접근하려면 "->" (화살표 연산자)를 사용합니다.


    예를 들면 이렇습니다.



    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // 구조체 정의
    typedef struct {
      int    m_num1;
      int    m_num2;
    } struct_sample;
     
    void func1(struct_sample *inout)
    {
      struct_sample * p_struct; // 구조체 포인터 선언
     
      if(inout == NULL)
      {
        return;
      }
     
      // 포인터 대입
      p_struct = (struct_sample*)inout;
     
      if(p_struct->m_num1%10 < 5// 멤버 변수 m_num1과 10의 나머지가 5미만인 경우
      {                           // 포인터 구조체의 멤버 변수에 대한 접근에는 화살표 사용
                                  // m_num1의 3배 값을 m_num2에 입
        p_struct->m_num2 = p_struct->m_num1 * 3;
      }
    }
     
    int main()
    {
      struct_sample  strct_sample;  // 구조체 선언
     
      // strct_sample 구조체를 0으로 초기화 (각 멤버에 0을 대입)
      memset(strct_sample, 0x00sizeof(strct_sample));
     
      strct_sample.m_num1 = 102;  // 구조체 멤버 변수 m_num1에 102 대입
      // 멤버 변수 접근하기 위해 도트 연산자 사용
     
      func1(&strct_sample); // 구조체를 포인터에서 함수에 전달
     
      printf(“m_num1의 값은%d입니다\n”, strct_saple.m_num1);
      printf(“m_num2의 값은%d\n”, strct_saple.m_num2);
    cs


    [출력 결과]


    1
    2
    m_num1의 값은 102입니다
    m_num2의 값은 306입니다
    cs



    위의 구조체를 사용한 예제는 main에서 선언한 구조체를 pointer로 함수 func1에 전달합니다. func1 함수 내부에서 추가 가공이 이뤄집니다.


    포인터는 구조체의 위치를 가리키므로 양방향으로 변수를 편집할 수 있습니다.

    함수 포인터

    함수 포인터는 함수가 포함된 주소입니다.


    함수도 변수와 마찬가지로 C언어 포인터로 사용하는 것이 가능합니다. 이것이 의미하는 바는 함수 포인터를 변경하는 것으로, 같은 함수 포인터에서 다른 함수를 호출할 수 있다는 것입니다. 이때 함수의 프로토타입을 맞추는 것이 필요합니다.


    방법을 살펴봅니다.



    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
    28
    29
    30
    31
    typedef double (*func)(doubledouble); // 함수 포인터 타입 선언
     
    // 함수 포인터에 등록하는 함수 1
    int rectangle(double num1, double num2)
    {
      // 인수1과 인수2로 직사각형 면적을 구함
      return(num1 * num2);
    }
     
    // 함수 포인터에 등록하는 함수 2
    double circle(double  num1, double num2)
    {
      // 인수 1을 이용하여 원의 면적을 구함
      num1 * num1 * 3.14;
    }
     
    int main()
    {
      double  num1, num2, result;
      func *p_func;
     
      num1 = 10;
      num2 = 7;
     
      p_func = &rectangle; // 함수 포인터에 rectangle 포인터 대입
      result = p_func(num1, num2);
      printf("함수 포인터 p_func()의 값은 %.2f입니다.\n", result);
     
      p_func = &circle;    // 함수 포인터에 circle 포인터 대입
      result = p_func(num1, num2);
      printf("함수 포인터 p_func()의 값은 %.2f입니다\n", result);
    cs


    [출력 결과]


    1
    2
    함수 포인터 p_func()의 값은 70.00입니다.
    함수 포인터 p_func()의 값은 314.00입니다.
    cs



    위의 예제에서 두 번째, 원의 면적을 구하는 함수는 인수2를 사용하지만 문제는 없습니다.


    함수 포인터를 사용하기 위해서는 형식을 맞춰야 하므로 사용하지 않는 인수가 있어도 문제는 없습니다.

    즉, 여러 함수를 함수 포인터로 사용하는 경우 인수가 적던지, 많던지 상관없이 함수 포인터로 활용할 수 있습니다.



    마지막 정리


    이상 C언어 포인터 기본적인 사용법을 정리했습니다. C언어 다룰 때 pointer는 필수이며, 포인터를 기억하면 코딩하기 매우 편리합니다.


    C언어 고급 수준으로 다룰 때 메모리와의 관련성 등을 이해하는 데 도움을 줍니다. 그 때문에 C언어 포인터를 이해하는 것이 C언어 고수가 되는 길이기도 합니다.


    또한, 이 포스트에서 예제로 사용된 소스 코드는 어디까지나 사용 방법을 보여주기 위한 것이라 실무에선 알맞지 않을 수도 있습니다.


    C언어 관련 글

    정수형 변수 int 공부하기

    https://codingcoding.tistory.com/704


    순차탐색, 이진탐색

    https://codingcoding.tistory.com/831


    동적 메모리 할당

    https://codingcoding.tistory.com/645


    C언어와 C++ 차이점

    https://codingcoding.tistory.com/287

    반응형