[10주차 Day2] 프론트엔드 기초 - 프로그래밍의 기본 원리

반 히·2024년 5월 1일

데브코스

목록 보기
21/58
post-thumbnail

💡 break문과 continue 문


📌 멈추거나 계속하거나

  • continue : 고 (계속 한다)
  • break : 스톱 (멈춘다)
  • break는 반복문을 빠져나가고, continue는 다시 반복 조건으로 올라감.

📌 break문의 기본 원리

  • 반복문 while문 내부에서 특정 조건이 되면 break를 만나게 되는데, break문은 while문을 빠져나가게 된다.
#include <stdio.h>

int main()
{
    int a = 0;
    while (1) {
        if (a > 100) {
            break;
        }
        printf("a의 값은 %d 입니다.\n", a);
        a++;
    }
    printf("a는 100보다 크다\n");
    
    return 0;
}

📌 continue문의 기본 원리

  • 반복문 while문 내부에서 특정 조건이 되면 continue문을 만나게 되는데, 이때 continue문 이하의 수행은 무시하고, 다시 반복의 시작점으로 간다.
  • 일종의 skip하는 기능
#include <stdio.h>

int main()
{
    int a = 0;
    while (a < 100) {
        a++;
        if (a > 80 && a < 90) {
            continue;
        }
        printf("a의 값 : %d\n", a);
    }
    
    return 0;
}

📌 평가 문제

  • 구구단을 출력하되, 짝수단(2단, 4단, 6단, 8단)만 출력하는 프로그램을 작성하라. 단, continue문을 사용하여 작성할 것.
#include <stdio.h>

int main()
{
    int dan = 1;
    int i = 1;
    
    while (dan <= 9) {
        i = 1;
        if (dan % 2 != 0) {
            dan++;
            continue;
        }
        while (i <= 9) {
            printf("%d X %d = %d\n", dan, i, dan * i);
            i++;
        }
        dan++;
    }
    return 0;
}

💡 함수


📌 함수의 정의

  • 하나의 특별한 목적의 작업을 수행하기 위해 독립적으로 설계된 프로그램 코드의 집합

📌 함수는 나누어서 처리하기 위한 목적

  • 코드의 가독성 향상
  • 코드의 유지 보수 및 확장 용이

📌 함수형 기반 언어의 동작 구조

  • 대부분의 프로그래밍 언어는 함수의 집합체이다.
  • 함수들이 서로 연동하여 유기적으로 동작한다.

📌 함수의 종류

  • 표준 함수
    • 언어에서 기본적으로 제공해주는 함수를 표준함수라고 한다.
    • 함수를 라이브러리화 시켜서 편리하게 사용할 수 있게 한다.
    • 코드의 기본 기능을 제공해주어 생산성을 높여줌
    • ex) printf
  • 사용자 정의 함수
    • 사용자가 자신이 원하는 기능을 직접 만들 수 있다.
    • 표준 함수의 기능에는 한계가 있다.
    • 표준 함수의 기능을 기반으로 나만의 새로운 기능을 만들어낼 수 있다.
    • 우리가 작성하는 대부분의 함수들이 사용자 정의 함수임.
    • 정형화된 완구(표준함수)와 가변적인 레고(사용자 정의 함수)같은 완구의 차이

📌 함수의 기본 형태

  • 데이터 타입(자료형) : 함수가 리턴하는 값의 타입
  • 함수 이름 : 함수 기능과 밀접한 이름으로 만드는 것이 좋음
  • 인수(=매개변수=전달인자=파라미터) 목록 : 함수에 필요한 값을 전달할 때 사용
  • 함수의 내용 : 중괄호 {} 사이의 영역 안에서 작성
자료형 함수이름(인수목록)
{
	함수의 내용
}

🔗 더하기 Add 기능 함수 작성

#include <stdio.h>
int Add(int a, int b) {
    return a + b;
}
int main()
{
    int a = 10;
    int b = 20;
    int sum;
    
    sum = Add(a, b);
    printf("%d", sum); // 30 출력 
    
    return 0;
}

📌 void 타입

  • 결과값을 리턴하지 않는 함수 (= 타입이 없다)
#include <stdio.h>
int Add(int i, int j);
void print_Start() {
    printf("========Programming Start======\n");
    printf("두 개의 정수를 입력하시오 : ");
}
void print_Sum(int result) {
    printf("두 수의 합은 %d입니다.\n", result);
    printf("========Programming End======\n")
}

📌 평가 문제

  • 사각형의 넓이를 구하는 함수를 작성해보자. 사각형의 넓이는 가로의 길이 세로의 길이를 구하면 된다. 사용자로부터 두 정수(가로의 길이, 세로의 길이)를 각각 입력 받아 함수의 전달인자로 전달하고, 사각형의 넓이를 출력하는 함수를 작성하자. 그리고 이 함수를 main에서 호출하여 출력해보자.
    함수 작성 예)
    void 함수이름 (int width, int height) {...}
void getArea (int width, int height) {
    int area = width * height;
    printf("사각형의 넓이 : %d\n", area);
}
int main()
{
    int w, h;
    
    scanf("%d %d", &w, &h);
    getArea(w, h);
    
    return 0;
}

  • 사용자로부터 두 수를 입력 받아, 두 수를 비교하여 최대값과 최소값을 구하는 함수를 정의하라. 그리고 main 함수에서 이 함수들을 호출하여 결과값을 출력하도록 하라.
    출력 예)
    두 정수를 입력하시오 : 5 3
    `최대값 : 5, 최소값 : 3
#include <stdio.h>
int Max(int a, int b) {
    if (a >= b) 
        return a;
    else
        return b;
}
int Min(int a, int b) {
    if (a <= b)
        return a;
    else 
        return b;
}
int main()
{
    int a, b;
    int max, min;
    
    printf("두 정수를 입력하시오 : ");
    scanf("%d %d", &a, &b);
    
    max = Max(a, b);
    min = Min(a, b);
    printf("최대값 : %d, 최소값 : %d\n", max, min);
    
    return 0;
}

  • 커피 자판기가 있다. 100원 넣으며 블랙커피', 200원을 넣으면 '밀크커피'가 나온다. 자판기가 함수와 같은 블랙박스라고 했었다. 자판기를 함수로 구현해보자. 즉, 사용자로부터 정수형 가격(100, 200)을 입력 받아 100을 입력 받으면 '블랙커피'를 출력하고, 200을 입력 받으면 '밀크커피'를 출력하면 된다.
    함수 작성 예)
    void 함수 이름 (int price)
    {
        if (price == 100)
            ...
        else if (price == 200)
            ...
    }
#include <stdio.h>
void coffeeMachine(int price) {
    if (price == 100) {
        printf("블랙커피\n");
    } else if (price == 200) {
        printf("밀크커피\n");
    }
}
int main()
{
    int price;
    
    scanf("%d", &price);
    coffeeMachine(price);
    
    return 0;
}

💡 변수의 범위


📌 지역 변수

  • 코드에서 지역의 의미 : 함수의 내부, 중괄호 내부
  • 지역 변수 : 중괄호 내부, 함수의 매개변수에서 사용되는 변수
  • 같은 이름이라도 다른 영역의 함수에 속해있으므로 독립된 다른 변수이다.
  • 아랫부분은 막혀있는 stack 메모리에 지역 변수들이 쌓임
  • 스택 메모리에 존재하는 시간 - 언제 생성되고 언제 소멸되는가?
    • 해당 함수가 종료되는 순간에 그 안에 선언된 변수는 소멸됨.
    • 함수 호출 순서와 소멸 순서는 반대이다.
  • 함수 매개변수도 스택 메모리에 할당되는 지역 변수이다.

📌 전역 변수

  • 함수 바깥쪽에 선언된 변수 : 모든 함수에서 다 접근 가능
  • 관리가 어려움
  • 전역변수는 프로그램 시작하자마자 메모리 상에 올라가서 프로그램이 종료될 때 메모리 상에서 소멸된다.
  • 데이터 영역에 저장됨
#include <stdio.h>
int global;     // 전역 변수 
void func1() {
    int local = 10; // 지역 변수
}
void main(void) {
    int val = 0;    // 지역 변수 
    func1();
}
#include <stdio.h>
int global;

void Add(int i, int j) {
    global = i + j;
}
int main()
{
    int a = 10;
    int b = 20;
    
    Add(a, b);
    printf("%d\n", global);
    
    return 0;
}

📌 static 변수 (정적 변수)

  • 사전적 의미로 '정지된', '고정상태의' 뜻을 갖는다.
  • 지역 변수처럼 중괄호 영역에서 선언되지만, 중괄호를 벗어나도 메모리 상에 고정되어 소멸하지 않는다.
  • 지역 변수와 전역 변수를 반 반 섞어놓은 변수
    • 선언(생성)할 때는 지역 변수처럼 함수 안에서 선언됨.
    • 한 번 생성한 변수는 해당 함수가 소멸이 되어도 변수는 소멸되지 않음. (전역 변수가 소멸될 때 같이 소멸됨)
    • 즉, 프로그램이 종료될 때 소멸됨.
  • 전역 변수와 동일하게 데이터 영역에서 관리됨.
  • 실습 예제
#include <stdio.h>
void func(void);
int main(void)
{
    int i = 0;
    while (i < 5) {
        func();
        i++;
    }
    return 0;
}
void func(void) {
    int value = 0;
    value++;
    printf("%d\n", value);
}

#include <stdio.h>
void func(void);
int main(void)
{
    int i = 0;
    while (i < 5) {
        func();
        i++;
    }
    return 0;
}
void func(void) {
    static int value = 0;
    value++;
    printf("%d\n", value);
}

📌 평가 문제

  • 책읽기 마라톤 기능을 가진 프로그램을 구현해보자. 책읽기 마라톤이란 내가 읽은 책들의 페이지 수를 누적 계산하는 기능이다. 그날 그날 읽은 책들의 페이지 수를 사용자로부터 입력 받으면 최종 누적된 페이지 수에 새로 입력된 페이지 수가 추가로 더해지고, 다시 갱 신된 최종 페이지 수가 출력되는 것이다. 한번의 출력이 끝나면 다시 읽은 책의 페이지 수 를 사용자로부터 입력 받고, 누적된 최종 페이지 수를 출력한다. 이 과정을 사용자가 -1을 입력할 때까지 계속 반복한다. 이 기능을 함수로 구현하되, 페이지의 누적 결과를 저장하 는 변수를 전역 변수로도 구현 해보고, static 변수로도 구현해보도록 한다.
    출력 예)
    읽은 책의 페이지 수를 입력하시오 : 30
    최종 누적 페이지 : 30
    읽은 책의 페이지 수를 입력하시오 : 20
    최종 누적 페이지 : 50
    읽은 책의 페이지 수를 입력하시오: 40
    최종 누적 페이지 : 90
    읽은 책의 페이지 수를 입력하시오 : -1
    더 분발하세요.
// 전역변수
#include <stdio.h>
int totalPage = 0;
int main(void)
{
    int page = 0;
    while(1) {
        printf("읽은 책의 페이지 수를 입력하시오 : ");
        scanf("%d", &page);
        
        if (page == -1) {
            printf("더 분발하세요.\n");
            break;
        }
        
        totalPage += page;
        printf("최종 누적 페이지 : %d\n", totalPage);
    }
    
    return 0;
}
// 정적(static) 변수
#include <stdio.h>

int main(void)
{
    static int totalPage = 0;
    int page = 0;
    while(1) {
        printf("읽은 책의 페이지 수를 입력하시오 : ");
        scanf("%d", &page);
        
        if (page == -1) {
            printf("더 분발하세요.\n");
            break;
        }
        
        totalPage += page;
        printf("최종 누적 페이지 : %d\n", totalPage);
    }
    
    return 0;
}

💡 배열


📌 배열의 개념

  • 배열이란 같은 속성을 가진 것들을 나열해 놓은 것을 말한다.
  • 배열의 요소가 순서대로 여러 개 모인 것이다.
  • 배열의 요소는 같은 속성을 지니고 있어야 한다.

📌 왜 배열을 사용해야 하나

  • 정수형 변수 1개 선언 : int a;
  • 정수형 변수 2개 선언 : int a; int b;
  • 정수형 변수 10개 선언도 가능하다.
  • 정수형 변수 100개도 선언은 가능하나 직접 선언하여 해결할 문제는 아니다.
  • 배열을 사용하면 변수가 100개든 1억개든 간단하게 선언할 수 있다.

    int a[100];

📌 배열의 선언 구조

  • 배열의 탑입 : 배열 요소들의 타입을 나타낸다.
  • 배열 이름 : 각 배열 요소에 접근하기 위해 배열 이름을 나타낸다.
  • 배열 길이 : 변수의 개수를 나타낸다.
  • int array[5]의 경우 배열의 타입은 int, 배열의 이름은 array, 길이는 5
  • 배열의 첫 번째 인덱스는 무조건 0부터 시작
int array[3];	// 배열 선언
array[0] = 1;	// 배열의 첫 번째 요소 접근
array[1] = 2;	// 배열의 두 번째 요소 접근
array[2] = 3;	// 배열의 세 번째 요소 접근

📌 배열의 초기화

  • 배열의 초기화 형태
int array[5] = {1, 2, 3, 4, 5};
#include <stdio.h>

int main(void)
{
    int total = 0;
    int array[5] = {1, 2, 3, 4, 5};
    
    total=array[0]+array[1]+array[2]+array[3]+array[4];
    printf("%d", total);
    
    return 0;
}
  • 배열의 길이 생략
  • 초기값의 개수를 보고 컴파일러는 배열의 길이를 계산한다.
int array[] = {1, 3, 5, 7, 9};

📌 배열의 복사

  • 같은 타입끼리 복사가 가능하다
  • 배열도 배열끼리 복사가 가능하다
  • 다음과 같이 변수 복사하듯이 복사하면 에러가 발생함

    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5];
    arr2 = arr1

  • error C2106 에러 : 배열은 상수이기 때문에 대입 연산자를 통해 값을 넘겨 받을 수 없다.
  • 배열은 요소끼리 복사해야 한다. (즉, 값만 복사하기)
#include <stdio.h>

int main(void)
{
    int i;
    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5];
    
    for (i = 0; i < 5; i++) {
        arr2[i] = arr1[i];
    }
    
    for (i = 0; i < 5; i++) {
        printf("%d\n", arr2[i]);
    }
    
    return 0;
}

📌 평가 문제

  • 앞에서 작성한 예제를 참고하여 배열 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(void)
{
    int i;
    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5];
    
    for (i = 0; i < 5; i++) {
        arr2[4 - i] = arr1[i];
    }
    
    for (i = 0; i < 5; i++) {
        printf("%d\n", arr2[i]);
    }
    
    return 0;
}

📌 문자열 변수

  • 지금까지 우리가 사용했던 printf("Hello World")와 같은 문자열은 모두 상수
  • 문자열에 이름을 붙여주면 변수로 사용 가능

    char str[12] = "Hello World";

  • 문자열을 세어보면 총 11개인데 11+1로 1개 더 설정한 이유는 문자열의 끝에 null 문자가 추가되기 때문이다.
#include <stdio.h>

int main()
{
    char str[12] = "Hello World";
    printf("%s\n", str);

    return 0;
}

📌 null 문자에 관하여

  • 문자열 끝에는 null 문자가 반드시 추가된다

    char str[100] = "Beautiful";

  • null 문자가 왜 필요한가?
    • 문자수는 9개, 배열의 개수는 100개, 91개의 공간이 빈다.
    • 91개의 공간에는 쓰레기 값으로 채워져있다.
    • 사람은 문자와 쓰레기 값을 구분하지만, 컴퓨터는 구분이 불가능하다.
    • 컴퓨터가 문자열의 끝을 인식하기 위해 null을 표시한다.
    • "Beautiful"의 공간은 9개가 아니라 null을 포함하여 10개이다.
  • 실습 예제
#include <stdio.h>

int main()
{
    int i;
    char str1[100] = "Beautiful";
    
    for (i=0; i<10; i++)
        printf("%c ", str1[i]);
    printf("\n");
    printf("str1 = %s\n", str1);

    return 0;
}

💡 포인터


📌 우리가 알고 있는 변수 선언 시 메모리 구조

  • 메모리는 주소를 통해 메모리에 접근하여 값을 읽고 쓸 수 있다.
  • 문자 타입 1바이트, 정수 타입 4바이트, 실수 타입 8바이트

📌 포인터의 개념

  • 포인터는 포인터 변수의 줄임말로 메모리의 주소값을 저장하고 있는 변수이다.
  • 보통 주소값을 저장한다는 표현을 반대로 해당 메모리를 가리킨다라고도 표현한다.
  • 어느 특정 메모리 주소를 가리키거나 향하고 있다는 뜻이다.
  • 직접적으로 접근하는 것이 아니라 해당 메모리의 주소값만 참조하는 것이기 때문에 간접 참조라고도 한다.
  • 원격 데스크톱 기능과 비유하면 이해하기 쉬움 !

📌 포인터 사용 방법

  • 포인터 변수 선언 시 일반 변수명 앞에 * 기호만 붙여주면 된다. 주소값만 저장하겠다는 의미

    char *pA;
    int *pB;
    double *pC;

  • & 기호를 통해 변수의 주소값을 얻어올 수 있다.

    int b = 100;
    int *pB = &b;

  • 포인터 변수 pB가 변수 b의 주소값을 저장하고, 변수 b를 가리키고 있음.
  • 실습 예제
#include <stdio.h>

int main()
{
    int b = 100;
    int *pB = &b;
    
    printf("b = %d\n", b);
    printf("&b = %p\n", &b);
    printf("*pB = %d\n", *pB);
    printf("pB = %p\n", pB);

    return 0;
}
  • 주소값을 나타낼 때는 %p 사용함.
  • 선언 시 사용하는 *와 그 후에 사용하는 *는 의미가 다르다.
    • 선언 시에는 포인터 변수라는 것을 표현
    • 그 이하에 사용될 때는 포인터 변수가 가리키고 있는 변수를 의미
  • 예제의 메모리 구조
    • 변수 pB는 주소값 0x7ffeae2f497c를 저장하고 있는 포인터 변수
    • 이 주소값에 해당하는 메모리를 가진 변수는 int b
    • pB는 변수 b의 주소 및 실제 값을 참조 가능

💡 포인터와 배열


📌 배열 이름의 의미

  • 배열의 이름은 해당 배열의 첫 번째 요소의 주소값을 갖는다. 즉 첫 번째 요소의 주소값을 가리키는(저장하는) 포인터라는 의미이다.
  • arr은 이 배열을 가리키고 있는 포인터임을 알 수 있다.
  • arr은 &arr[0]의 값과 일치한다.
#include <stdio.h>

int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    
    for (int i = 0; i < 5; i++){
        printf("arr[%d] = %d\n", i, arr[i]);
        printf("&arr[%d] = %p\n", i, &arr[i]);
    }
    printf("배열의 이름 : %p\n", arr);

    return 0;
}

  • 정수는 4바이트 이므로, 주소값이 4씩 커지는 것을 볼 수 있음

  • 배열의 이름 == 포인터

  • 배열의 이름이 포인터이지만 일반 포인터와 결정적인 차이점이 있다.

    • 배열의 이름은 주소값을 갖는 포인터이지만 상수이므로 주소값을 변경할 수 없다.
    • 배열의 이름은 포인터 상수임.
#include <stdio.h>

int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    int temp = 10;
    
    arr = temp; // 오류 발생 

    return 0;
}

💡 함수와 포인터


📌 기본적인 함수의 인자 전달 형태

  • 기본적인 함수의 인자 전달 형태는 복사이다.
    • 값에 의한 복사
    • 참조에 의한 복사
int main(void)
{
    int a = 10;
    Temp(a);

    return 0;
}
void Temo(int b) {
}
  • a에서 b로 복사시 a에도 값이 존재하고 b에도 값이 존재한다.
    • 실인수 : 전달하는 변수
    • 형식인수 : 전달받는 변수

📌 배열형의 인자는 포인터형으로 받는다.

  • 1~5개 정도까지는 전달인자로 사용할 수 있다. 하지만 그 이상 사용하면 난잡해보인다.
  • 배열을 전달인자로 사용하면 여러 개의 값을 넘길 수 있다.
  • 어떻게 배열을 전달하고 받을 것인가? 바로 포인터이다.
    • 포인터 변수로 배열을 받음.
    • 참조에 의한 복사
#include <stdio.h>
void func(int *pArr) {
    for (int i = 0; i < 5; i++){
        printf("pArr[%d] = %d\n", i, *(pArr + i));
    }
}
int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    
    func(arr);
    
    for (int i = 0; i < 5; i++){
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    return 0;
}

  • 함수의 전달인자 arr은 주소값을 넘기고 있다.
  • 형식 인수의 형태가 int *pArr임을 볼 수 있다. 주소값을 넘겨받기 위해서는 포인터 형태로 받아야 하기 때문이다.
    • 값에 의한 복사에 비해 메모리 절감 가능 (같은 메모리를 참조하기 때문에)
    • 성능 향상 가능

0개의 댓글