2024.01.24(수)

🪄함수

코드의 묶음

  • 코드 가독성 향상
  • 코드의 유지 보수 및 확장 용이
  • C언어 = 함수 기반 언어!

  • 표준함수: 그 기능과 사용법이 C 언어의 표준으로 정의되어 있으며 라이브러리로서 포함되어 사용자가 불러서 쓸 수 있는 함수
  • 사용자 정의 함수: 사용자가 직접 정의해서 사용하는 함수
  • 함수의 기본 형태
    반환형(return type) 함수명(매개변수 목록(parameters)) {
    	함수 내용
    }
    • 매개 변수의 자료형도 명시해야 함: int Add(int a, int b) { return a + b; }
    • return 값이 없으면 void 타입을 사용

🔍변수의 범위

🏡지역변수(local variable)

블록({})의 내부 공간에 선언되는 변수 → 해당 블록 안에서만 사용 가능

  • 스택 메모리에 존재
  • 함수 호출 순서와 소멸 순서는 반대

🌐전역변수(global variable)

블록({})의 외부 공간에 선언되는 변수 → 프로그램이 실행되는 동안 사라지지 않음

  • 데이터 메모리에 존재
  • 프로그램이 시작하자마자 메모리에 올라가서 프로그램이 종료될 때 메모리에서 소멸

🧊static(정적) 변수

블록({})의 내·외부 공간에서 선언할 수 있는 변수로 변수의 범위를 파일 범위로 제한 → 프로그램이 실행되는 동안 사라지지 않음

  • 데이터 메모리에 존재
  • 초기화할 때 반드시 상수로 초기화 (초깃값을 지정하지 않으면 default 0으로 자동 초기화됨)
  • 전역변수와 달리 초기화는 최초 한 번만 진행됨
  • 함수의 매개변수로 사용 불가능

🧊🏡정적 지역변수(static local variable)

블록({}) 내부에서만 사용할 수 있고, 한 번 초기화 되면 이후에 함수 호출 시 값의 초기화를 무시

🧊🌐정적 전역변수(static global variable)

자신이 선언된 소스 파일에서만 사용할 수 있고, 외부에서는 가져다 쓸 수 없음

📚배열

같은 속성을 가진 것들을 나열해 놓은 것

  • 배열 선언

    자료형 배열명[크기];    // index 0 ~ (크기 - 1)
    int scores[10];    // int 형 scores 변수 10개 선언 (index 0 ~ 9)
  • 배열 초기화

    자료형 배열명[크기] = {,,};
    int scores[] = {100, 90, 80};    // 배열 크기는 자동적으로 3이 됨
    int grades[10] = {0};            // 전체를 0으로 초기화
    int avg[5] = {80, 70};           // avg[0]은 80으로, avg[1]은 70으로, 나머지는 모두 0으로 초기화
  • 배열 접근 및 할당

    배열명[index] =;
  • 배열 복사

    • 같은 타입의 배열끼리 복사 가능
    • 변수 복사하듯이 복사하면 에러 발생
      int arr1[5] = {1, 2, 3, 4, 5};
      int arr2[5];
      
      arr2 = arr1;    // 직접 대입하면 에러 발생
    • 배열은 요소끼리 복사해야 함
      #include <stdio.h>
      
      int main() {
          int arr1[5] = {1, 2, 3, 4, 5};
          int arr2[5];
      
          for (int i = 0; i < 5; i++) {
              arr2[i] = arr1[i];
          }
      
          return 0;
      }
    • memory 영역을 복사하는 memcpy를 사용할 수도 있음
      #include <string.h>
      
      int main() {
          int arr1[5] = {1, 2, 3, 4, 5};
          int arr2[5];
      
          memcpy(arr2, arr1, sizeof(arr1));
      
          return 0;
      }
  • 문자열에 이름을 붙여주면 변수로 사용 가능

    char 배열명[크기] = "문자열";
    char s1[10] = "Hello";

    • 선언과 동시에 할당하면 크기 생락 가능

    • 문자열 길이 + 1(NULL 문자(\0)) 만큼의 크기로 자동 지정됨

      char s1[] = "Hello";    // 선언과 동시에 할당하면 크기 생락 가능

    • 배열이므로 문자열을 바로 할당하면 에러가 발생하고 요소를 하나씩 지정해줘야함

    • 또는 strcpy 사용

      #define _CRT_SECURE_NO_WARNINGS    // strcpy 보안 경고로 인한 컴파일 에러 방지
      #include <stdio.h>
      #include <string.h>    // strcpy 함수가 선언된 헤더 파일
      
      int main()
      {
          char s1[10] = "Hello";    // 크기가 10인 char형 배열을 선언하고 문자열 할당
          char s2[10];              // 크기가 10인 char형 배열을 선언
      
          strcpy(s2, s1);        // s1의 문자열을 s2로 복사
      
          return 0;
      }

👉포인터

포인터 변수의 줄임말로 메모리의 주소값을 저장하고 있는 변수 또는 메모리를 가리키는 변수

  • 참고 자료
  • 일반 변수명 앞에 * 기호를 붙여 포인터 변수 선언 (주소값만 저장하겠다는 의미)
    int *p;    /* p는 int형을 갖는 객체(object)를 가리키는 포인터 변수 */
    int* p;    /* 포인터는 이름 앞에 붙기 보다는 형에 붙는 것이 좀 더 가독성에 도움이 된다 */
  • 주소 연산자 &를 통해 변수의 주소값을 얻어올 수 있음
  • 포인터 변수가 객체를 가리키고 있다면 참조 연산자 *를 통해 개체의 값에 접근 가능
    int i = 1;
    int* p = &i;
    
    printf("%d\n", i);  /* 1 출력 */
    printf("%d\n", *p); /* 1 출력 */
  • 참조 연산자는 주소 연산자의 역 개념이라고 볼 수 있음
    j = *&i; // j = i;와 같은 구문

⭐포인터와 배열

배열의 이름은 해당 배열의 첫 번째 요소의 주소값을 가짐
(arr = &arr[0] 또는 *arr = arr[0])

  • arr + i = &arr[i] 또는 *(arr + i) = arr[i]
    #include <stdio.h>
    
    int main()
    {
        int arr[] = {1, 2, 3, 4, 5};
        
        printf("%p\n", arr);        // 0x7ffebad7f0e0
        printf("%p\n", &arr[0]);    // 0x7ffebad7f0e0
        
        printf("%d\n", *(arr + 4)); // 5
        printf("%d\n", arr[4]);     // 5
    
        return 0;
    }

💡 배열의 이름은 주소값을 갖는 포인터이지만 상수이므로 주소값 변경 불가능!
배열의 이름 = 포인터 상수 ≠ 포인터 변수

  • 문자형 배열과 문자형 포인터
    • 배열로 선언
      char date[8] = "June 14";
      /*
      
      "June 14"는 리터럴이 아니다!
      다음과 같은 표현이다.
      char date[8] = {'J', 'u', 'n', 'e', ' ', '1', '4', '\0'};
      
          [J][u][n][e][ ][1][4][\0]
      date⬏
      
      */
    • 포인터로 선언
      char* date = "June 14";    // date [·] –→ [J][u][n][e][ ][1][4][\0]
  • 문자열 포인터들의 배열
    char* planets[] = {"Mercury",  "Venus", "Earth", 
                       "Mars", "Jupiter",  "Saturn", 
                       "Uranus", "Neptune", "Pluto"};

⭐함수와 포인터

  • 기본적인 함수의 인자 전달 형태는 복사 → 값에 의한 복사(call by value)
    • 값만 전달해서 메모리를 공유하는 형태가 아님!
    • 실제로 전달된 값도 메모리를 차지함


  • 배열형의 인자는 포인터형으로 받음 → 참조에 의한 복사(call by reference)
    #include <stdio.h>
    
    void func(int* pArr) {
        for (int i = 0; i < 5; i++) {
            printf("함수 안의 배열 : %d\n", *(pArr + i));
        }
    }
    
    int main()
    {
        int arr[] = {1, 2, 3, 4, 5};
        
        func(arr);
    
        return 0;
    }
  • 함수에 포인터를 전달할 수 있을 뿐 아니라 포인터를 반환할 수도 있음
    반환형(return type)* 함수명(매개변수 목록(parameters)) {
    	함수 내용
    }
    int* get_max(int* left, int* right)
    {
        if (*left > *right) {
            return left;
        } else {
            return right;
        }
    }
  • 함수 포인터

    함수의 주소값을 저장하는 포인터

    반환형(return type) (*함수(포인터))(매개변수 목록(parameters))
    • 함수 이름은 함수의 주소값을 가짐 (func = &func)
      #include <stdio.h>
      
      int sum(int a, int b) { 
      		return a + b;
      }
      
      int main() {
          int (*ptrSum)(int a,int b);
      
          ptrSum = sum;    //&sum = sum
      
          printf("sum의 주소: %p\n", &sum);                    // 0x00081023
          printf("ptrSum의 값: %p\n", ptrSum);                 // 0x00081023
          printf("ptrSum의 주소: %p\n", &ptrSum);              // 0x0026FB04
          printf("ptrSum(%d, %d) = %d\n", 3, 4, ptrSum(3, 4)); // ptrSum(3, 4) = 7
      
      		return 0;
      }
    • 함수 포인터를 이용하여 함수를 매개 변수로 사용할 수도 있음
      #include <stdio.h>
      
      int sum(int a, int b) { 
          return a + b;
      }
      
      int func(int a, int (*funcPointer)(int, int)) {
          return a + funcPointer(3, 4);
      }
      
      int main() {
          printf("%d", func(10, sum));    // 17
          
      	return 0;
      }

📝평가 문제

  • 강사님이 오늘 강의에서 내준 평가 문제이다.
  • online c compiler를 사용했다.
  1. 사각형의 넓이를 구하는 함수를 작성해보자. 사용자로부터 두 정수(가로의 길이, 세로의 길이)를 각각 입력 받아 함수의 전달인자로 전달하고, 사각형의 넓이를 출력하는 함수를 작성하자. 그리고 이 함수를 main에서 호출하여 출력해보자.

    #include <stdio.h>
    
    int get_rectangle_area(int width, int height)
    {
        return width * height;
    }
    
    int main()
    {
        int width, height;
    
        printf("가로, 세로 길이를 입력하세요: ");
        scanf("%d %d", &width, &height);
    
        printf("사각형의 넓이는 %d입니다.\n", get_rectangle_area(width, height));
    
        return 0;
    }
  2. 사용자로부터 두 수를 입력 받아, 두 수를 비교하여 최대값과 최소값을 구하는 함수를 정의하라. 그리고 main 함수에서 이 함수들을 호출하여 결과값을 출력하도록 하라.

    /******************************************************************************
    
    출력 예)
    두 정수를 입력하시오 : 5 3
    최대값 : 5 최소값 : 3
    
    *******************************************************************************/
    
    #include <stdio.h>
    
    int min(int a, int b)
    {
        return a <= b ? a : b;
    }
    
    int max(int a, int b)
    {
        return a >= b ? a : b;
    }
    
    int main()
    {
        int num1, num2;
    
        printf("두 정수를 입력하시오 : ");
        scanf("%d %d", &num1, &num2);
    
        printf("최대값 : %d 최소값 : %d\n", max(num1, num2), min(num1, num2));
    
        return 0;
    }
  3. 커피 자판기가 있다. 100원 넣으면 '블랙커피', 200원 넣으면 '밀크커피'가 나온다. 자판기가 함수와 같은 블랙박스라고 했었다. 자판기를 함수로 구현해보자. 즉, 사용자로부터 정수형 가격(100, 200)을 입력 받아 100을 입력 받으면 '블랙커피'를 출력하고, 200을 입력 받으면 '밀크커피'를 출력하면 된다.

    #include <stdio.h>
    
    void coffee_vending_machine(int price)
    {
        if (price == 100) {
            printf("블랙커피\n");
        } else if (price == 200) {
            printf("밀크커피\n");
        } else {
            printf("올바른 가격을 입력하세요.\n");
        }
    }
    	
    	
    int main()
    {
        int money;
    
        printf("자판기에 넣을 돈을 입력하세요 (100 또는 200): ");
        scanf("%d", &money);
    
        coffee_vending_machine(money);
    
        return 0;
    }
  4. 책 읽기 마라톤 기능을 가진 프로그램을 구현해보자. 책 읽기 마라톤이란 내가 읽은 책들의 페이지 수를 누적 계산하는 기능이다. 그날 그날 읽은 책들의 페이지 수를 사용자로부터 입력 받으면 최종 누적된 페이지 수에 새로 입력된 페이지 수가 추가로 더해지고, 다시 갱신된 최종 페이지 수가 출력되는 것이다. 한 번의 출력이 끝나면 다시 읽은 책의 페이지 수를 사용자로부터 입력 받고, 누적된 최종 페이지 수를 출력한다. 이 과정을 사용자가 -1을 입력할 때까지 계속 반복한다. 이 기능을 함수로 구현하되, 페이지의 누적 결과를 저장하는 변수를 전역 변수로도 구현해보고, static 변수로도 구현해보도록 한다.

    /******************************************************************************
    
    출력 예)
    읽은 책의 페이지 수를 입력하시오: 30
    최종 누적 페이지: 30
    읽은 책의 페이지 수를 입력하시오: 20
    최종 누적 페이지: 50
    읽은 책의 페이지 수를 입력하시오: -1
    더 분발하세요.
    
    *******************************************************************************/
    	
    #include <stdio.h>
    	
    int total_pages = 0;
    	
    void page_accumulator(int pages) {
        total_pages += pages;
    }
    	
    int main()
    {
        int pages;
    
        while (1) {
            printf("읽은 책의 페이지 수를 입력하시오: ");
            scanf("%d", &pages);
    
            if (pages == -1) {
                break;
            }
    	
            page_accumulator(pages);
            
            printf("최종 누적 페이지: %d\n", total_pages);
        }
    	
        printf("더 분발하세요.\n");
    	
        return 0;
    }
    #include <stdio.h>
    	
    int page_accumulator(int pages) {
        static int total_pages = 0;
        total_pages += pages;
        return total_pages;
    }
    	
    int main()
    {
        int pages;
    
        while (1) {
            printf("읽은 책의 페이지 수를 입력하시오: ");
            scanf("%d", &pages);
    
            if (pages == -1) {
                break;
            }
    
            printf("최종 누적 페이지: %d\n", page_accumulator(pages));
        }
    
        printf("더 분발하세요.\n");
    
        return 0;
    }
  5. 배열 arr1의 값을 배열 arr2에 복사하되, 배열 요소를 역순으로 저장하도록 하고, 복사된 arr2의 요소값들을 출력하도록 하라.

    /******************************************************************************
    
    int arr1[5] = {1, 2, 3, 4, 5}일 경우
    출력 예)
    배열 arr2[0] = 5
    배열 arr2[1] = 4
    배열 arr2[2] = 3
    배열 arr2[3] = 2
    배열 arr2[4] = 1
    
    *******************************************************************************/
    
    #include <stdio.h>
    
    int main()
    {
        int arr1[5] = {1, 2, 3, 4, 5};
        int arr2[5];
    
        for (int i = 0; i < 5; i++) {
            arr2[4 - i] = arr1[i];
        }
    
        for (int i = 0; i < 5; i++) {
            printf("배열 arr2[%d] = %d\n", i, arr2[i]);
        }
    
        return 0;
    }

포인터 오랜만에 다시 보니 이해가 잘된다. 옛날에는 왜 어렵게 느껴졌을까..

profile
이것저것 관심 많은 개발자👩‍💻

0개의 댓글