ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CS50 2019 (4)사용자정의함수(vs루프), 추상화, 프로토타입, 오버플로우
    CS50 2021. 8. 9. 20:33

    ===

    www.boostcourse.org/cs112

     

    모두를 위한 컴퓨터 과학 (CS50 2019)

    부스트코스 무료 강의

    www.boostcourse.org

    1.컴퓨팅사고

    2.C언어

    3.배열

    4.알고리즘

    5.메모리

    6.자료구조

    =====================

    *C99 : 1999년에 나온 C버전

    5)사용자 정의 함수, 중첩 루프

    프로그래밍을 하다보면 가끔 반복적으로 작성해야하는 코드가 있음 //조상님들 그래왔겠지

    이런 코드를 여러 번 쓰지 않고 함수 형태로 저장해두면 코드를 간결하고 이해하기 쉽게 만들 수 있음.

    ▽생각해보면 반복이긴 한데 루프는 그 자체가 한 단위고 이건 적재적소에 한 단위씩 필요한 경우 + 생성자로 세팅

     

    @사용자 정의 함수

    추상화의 개념을 구현해봅시다(스크래치에서 기침을 위한 조각이 따로 없어서 새로운 퍼즐 조각 구현했었음).

    @cough0.c

    #include <stdio.h>

     

    int main(void)

    {

    printf(“cough\n”);

    printf(“cough\n”);

    printf(“cough\n”);

    }

    ->근데 코드 짤 때 복사 후 붙여넣기는 참아야 한다. 필요 이상으로 길어져서. 루프로 해보자 for

    #include <stdio.h>

     

    int main(void)

    {

    for(int i=0; i<3; i++) : i=1로 하고 i <= 3해도 됨(C에서 <=라고 써도 되는군)

    {

    printf(“cough\n”);

    }

    }

     

    @프로그램을 작성할 때 코드가 길어진다면 그걸 저만의 함수로 새롭게 지정하는 게 좋겠죠 우리가 위에서 get_string 같은걸 불러왔던 것처럼 우리도 그걸 해보자 이거

    @cough1.c

    #include <stdio.h>

     

    void cough(void) : 일단 넘어가, 이게 cough라는 내 함수를 만드는 건가봐

    {

    printf(“cough\n”); : 이건 또 하나 대 하나 여서 그렇게 뭘 치환하는 느낌은 안나지만

    }

    int main(void)

    {

    for(int i=0; i<3; i++)

    {

    cough(); : i 0부터 3까지 세어가며 cough 3번 반복한다 라는 말.

    // ()가 뭐야?? : 밑에 설명

    }

    }

     = 추상화! cough함수가 print로 구현된 것을 몰라도 cough(걍함수이름)라는 함수가 있다는 것만 알면 됨.

     

    개선의여지 : 위에 코드는 안 좋은게 저런 맞춤 함수가 많아질수록 여러분 프로그램의 가장 중요한 부분은 아래로 내려감. main함수는 위에 있을수록 좋다. 파일을 열었을 때 main 함수가 바로 나오면 좋으니까요.

     

    #include <stdio.h>

     

    int main(void)

    {

    for(int i=0; i<3; i++)

    {

    cough();

    }

    }

     

    void cough(void) : 그래서 아래로 내려봤음.

    {

    printf(“cough\n”);

    }

    ->근데 이럼 에러가 뜬다

    ->C는 시키는대로만 하고, 반드시 위에서 아래로, 왼쪽에서 오른쪽으로 시켜야 한다. 그래서 처음에 stdio.h를 추가, 다음 main의 시작 부분을 입력했던 것.

     

    영원히 새로운 맞춤 함수 구현 코드를 계속 위에 올려야 하나? + main함수를 맨 위에 둘 수 없나?

    ->마치 이전에 cough를 봤던 것처럼 C를 속이는 방법이 있다. 전부 본 적은 없어도 이름은 본적 있게

     

    #include <stdio.h>

     

    void cough(void); : 이렇게! 여기에 밑에거 복사해서(잘라내기아님) 한줄 추가해 세미콜론이랑 함께

    : 이렇게 하면 일단 읽는다.

    int main(void)

    {

    for(int i=0; i<3; i++)

    {

    cough();

    }

    }

     

    void cough(void) : 뒤에서 설명을 해주면 된다.

    {

    printf(“cough\n”);

    }

    ->이러면 컴파일 됨

     

    ===

    @cough3.c : 여러분의 맞춤 함수가 입력을 받을 수 있습니다.

    cough함수를 더 다재다능하게 만들기, 원하는 횟수만큼 cough를 출력할 수 있도록 해봅시다

     

    ===

    #include <stdio.h>

     

    void cough(void);

     

    int main(void)

    {

    for(int i=0; i<3; i++)

    {

    cough();

    }

    }

     

    void cough(void) : void get_int get_string처럼 값을 반환하지 않는다(다음에 다시 다룸)

    : 괄호안 void의 뜻은 입력을 받지 않는다는 뜻. 입력을 n과 같은 값으로바꾸면?

    {

    printf(“cough\n”);

    }

     

    ---그렇게 다시 써보면

    ===

    #include <stdio.h>

     

    void cough(int n); : (아래에서 한 행동들로 인해)여기도 바꿔야 함. 참고로 이걸 함수 프로토타입(시제품)이라 함.

     

    int main(void)

    {

    for(int i=0; i<3; i++)

    {

    cough();

    } :  for~부분이 필요없어짐. 아래에 n을 보내는 걸로 바꿔서.

    cough(3); : 이게 3번 실행하는 거래. 3이라는 값을 아래의 cough 함수에 전달

    위 파랑부분이랑 이거 똑같은 의미네 ()안에 비워도 되는거군

    }

     

    : 여기까지만 봐도 됨. cough함수가 어떻게 정의되었는지 몰라도 됨. 잘 디자인된 코드다. 필요한 내용을 설명하고 있음, 파일을 아래로 내리면 세부정보를 확인할 수 있는 것. 세부 정의 라고 부름. 누군가는 cough 함수를 어떻게 정의했는지 궁금해할 수 있지만 적어도 우리는 함수를 어떻게 정의했는지 정수를 어떻게 받아오는지 printf를 어떻게 사용하는지 전혀 알 필요가 없다. 누군가가 구현해준 기능을 그대로 활용해서 여러분에게 더 흥미로운 프로그램을 만들면 되는 것.

     

    void cough(int n) : 입력을 넣는 공간이라 했었지. 입력값을 위에cough()에서 받아서 int형식 n이란 변수에 저장

    {

    for (int i = 0; i < n; i++)

    {

    printf(“cough\n”); : 위에있던 for를 여기로 가져와서 printf for안으로 가져옴.

    }

    }

     

    ==================================================

    @positive.c

     : CS50에 있는 get_int로 양의정수를 받아오는 함수 get_positive_int를 만들자(<->negative잖아)

      

    // Abstraction and scope : cs50 사이트의 week2 C에서 복붙해옴

    #include <cs50.h>

    #include <stdio.h>

     

    int get_positive_int(void); : 프로토타입

     

    int main(void)

    {

    int i = get_positive_int();

    printf("%i\n", i);

    }

     

    // Prompt(촉발하다, 즉각적인) user for positive integer

     

    알아보기 쉽게 한눈에 쓰면

    int get_positive_int(void)

    {

        int n;

        do

        {

            n = get_int("Positive Integer: ");

        }

        while (n < 1);

        return n;

    }

    아래는 저거 해설, 참고 positive는 양수다

     

    int get_positive_int(void)

    ->앞이 void 아니고 int인 이유 : 이 함수가 뭔가를 반환하게 하려고(받아서 변수에저장)(get_string처럼)(답변1,2)

    ->(void) : 입력을 받지 않는다. 받는건 이 안에서 get_int로 받으니까

     

    함수 왼쪽에 있는 단어는 출력의 종류를 의미하고 괄호안의 단어 입력의 종류

    만약 입출력이 없다면 void를 적어주면 됨

     

    get_int 마지막에 return n;이 있는 무엇일 것

    //retun n; 이게 함수마지막에 있다면 그 함수를 써놓으면 n으로 바뀌는거네(함수안의 조건 만족시)

     

    좀 많이 위에서 배웠듯이 C에선 변수가 저장하는 데이터의 종류를 아주 정확하게 명시해줘야 함, 변수명의 왼쪽에

     

    답변1:함수를 선언할 때 int ~~로 정의하는 것은 함수의 반환값이 int type이라는 것을 알려주기 위해서입니다. 다시 말하면, 함수를 정의할 떄 마지막에 return 문장이 보이는데, 이 문장에서 반환하는 반환값이 정수 형태여야 한다는 것이지요. , int i = get_int()에서 get_int()는 정수값을 반환하고 이 값이 i에 저장됩니다.

     

    함수프로토타입이 함수선언 인가봐. 선언에서는 함수 매개 변수의 자료형만 명시해도 됩니다

     

    답변2 : void는 크기가 0 아무것도 아닌 상태를 기억해두는 것이기 떄문에, 반환이 불가능 합니다.(그래도 안에 함수들은 다 실행이 됩니다.-> int void로 바꿨을 경우, return n에서 컴파일에러가 발생합니다.)

     

    {

    int n;  : 컴퓨터에게 n이라고 하는 변수를 달라는 일종의 힌트래, 그 안에 어떤 값 저장할지 아직 모르기 때문에 아직 할당하지 않고 이것만 적음. = 쓰레기값이라고 부르는 값을 갖게 됨. 나중에 제대로 넣으면 됨

     

    (->do는 함수 전에 int로 해줘야 하는건가? 저기 get_int 앞에다 하면 안됨?하다보면 알겠지)

    ->답변 : 변수의 scope가 다릅니다. int n do while문 안에서 선언하는 경우 아래의 for문에서 n이 선언되어 있지 않다고 인식할 겁니다. n do-while문 안에서만 사용된다면 안에 선언해도 관계없지만, do-while밖에서도 사용되어야한다면 밖에 선언해주어야합니다. ( google검색 키워드 : C언어 변수 영역 )

    do : do-while루프, 한번 한 뒤 while ()안의 불리언 표현이 참일 때 반복 수행하라는 뜻.

    {

    n = get_int("Positive Integer: "); : 양의정수를 물어봐서 정수값을 받아 n에 저장.

    }

    while (n < 1); 양의정수가 아니면 계속 반복해서 물어봄.

    return n;

    }

    do-while루프의 좋은 점. 적어도 한번 수행한 뒤 사용자의 협조 여부에 따라 다시 할지 여부를 결정한다는 것.

    ->while루프는 조건을 먼저 확인했었음, do-while은 무조건 한 번은 먼저 수행함. 그 다음 조건 확인. 이상한 값 입력하면 다시 물어봄!

     

    ===

    함수의 선언과 함수의 실행을 분리해서 생각해주시면 편할겁니다.

    함수의 선언때는 int(리턴값) get_positive_int(void(파라미터))와 같이 리턴값, 파라미터가 지정되야하지만

    함수의 실행때는 리턴값은 별도로 표현하지 않으며 ( 필요시 리턴값을 새로운 변수에 저장 ), 

    파라미터는 함수의 선언때 지정된 값만(자료형 말고?)을 넘겨주시면 되겠습니다.

     

    지금까지 했던 모든 것들은 while루프나 for루프로도 똑같이 구현할 수 있다.

     

    ===

    @mario.c : 물음표 4개 나타내기

    (1)그냥 printf

    (2)for루프로 - mario2.c

    // Prints a row of n question marks with a loop

    #include <cs50.h>

    #include <stdio.h>

     

    int main(void)

    {

    // Get positive integer from user

    int n; : main에서는 n이라는 정수를 입력받는데(밑에보면 앎)

    (위의 int main 에서의 int 는 출력  --찾아보니 return 0가 함수 잘 마무리됐다는 뜻이었네)

     

    do는 함수 전에 int로 해줘야 하는건가? --for에서 써야되니까요

     

    do

    {

    n = get_int("Width: "); : 출력하고 싶은 블록의 폭이 얼만지를 묻는 부분

    }

    while (n < 1);

    //여기까지 do while이잖아

     

    // // Print out that many question marks

    for (int i = 0; i < n; i++)

    {

    printf("?");

    }

    printf("\n"); : 가장 마지막에는 새로운 줄을 출력합니다. 위에 ????는 했고, 얘는 그냥 엔터로 넘어간단 얘긴듯

    }

     

    ===

    @루프를 활용해 2차원구조 만들기, mario8.c

    //Prints a n-by-n grid of bricks with nested loops

     

    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        int n;
    
        do
        {
            n = get_int("Size: ");
        }
        while (n < 1);
    
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                printf("#");
            }
            printf("\n");
        }
    }

     

    #include <cs50.h> #include <stdio.h> int main(void) 그 다음 dowhile이용해서 Size: n 값을 받고~~ 그 다음에

    {

     

    for (int i = 0; i < n; i++) : 다음을 n번 반복하라(0이고,  < n 이라고 돼 있으면). 는 말을 어렵게 써놓은 것일 뿐, 이렇게 직관화

    {

     

    for (int j = 0; j < n; j++) : 다음을 n번 반복하라. 정확히 같은 뜻

    {

    printf("#");

    }

     

    printf("\n");

    }

     

    }

    행과 열을 출력하라는 말과 같다! 여러블록들을 여러줄에 출력하라는 말.

    //하난 세로고 하난 가로네 아 이래서 n차원이 되는구나

    사용된 개념은 같다, 0부터 어떤 값까지 세어나가며 뭔가를 계속 반복하는 for 루프를 사용했을 뿐.

     

    =====================

    6)하드웨어의 한계

    메모리, 오버플로우

    지금까지의 수업에서는 항상 사람의 코딩 실수를 봤지만 컴퓨터의 문제도 있을 수 있다.

    RAM은 모든 프로그램이 실행 중 저장되는 곳. 모든 파일들이 열려있는 동안 저장되는 곳. 컴퓨터가 여러 일들을 한번에 할 때 기억하기 위해 사용하는 것. 성능이 유한함. = 컴퓨터가 할 수 있는 일에는 근본적인 한계가 있다. 저장공간이 유한하므로 저장가능한 숫자 역시 유한하고, 무한대까지 셀 수도 없다. 연산에도 한계가 있다.

     

    @floats.c : 사용자에게 몇 개의 실수값을 받아오는 프로그램

    #include <cs50.h>

    #include <stdio.h>

     

    int main(void)

    {

    float x = get_float(“x: ”);

    float y = get_float(“y: ”);

     

    printf(“x/y = %f\n”, x/y); : 컴퓨터는 계산은 할줄 앎, x/y로 표현이 가능하구나.

    }

     

    터미널에서 키보드 방향키 위아래 누르면 명령어 입력 기록을 찾아줌

     

    소수점 50자리까지 1/10을 하면 이상한 숫자가 막 나옴 왜?

    *float 32비트 사용, double 64비트 사용

    컴퓨터는 완벽하지 않다. 저장할 수 있는 정보는 유한해서 계산할 수 있는 값들 중 1/10에 가장 가까운 값을 저장함. 유한한 정보를 사용해서는 무한한 숫자들을 100% 정확하게 저장할 수 없기 때문.

     

    @overflow.c

    #include <stdio.h>

    #include <unistd.h>

     

    int main(void)

    {

    for(int i=1; ; i *= 2) : 불리언표현 자리인데 빈칸이나 true나 같은 의미

    {

    printf(“%i\n”, i);

    sleep(1); : 사이에 1초를 쉬는 함수 unistd.h를 추가해야함.

    }

    }

    와 이 함수 저거 실행하면 1부터 시작하네?? for 세 번째 조건은 printf 한번 거쳐온다음 마지막순서였어!
    for문 문법에 대해 질문 주셨는데, for(시작 조건; 유지 조건; 증감식) 으로 되어있습니다.

     부동소수점 부정확성이나 정수 오버플로우 float의 정확성이나 정수의 크기에 한계가 있다는 것을 의미

    ->숫자를 세 개밖에 못 쓴다면, 999에서 +1하면 1000인데 세 개밖에 못쓰니까 앞의 1을 잃은 000이 됨 그래서 위의 코딩에서 *2를 계속해도 일정이상 진행하면 0만 뜨는 것. 이게 오버플로우가 생긴다는 것이고 이진법에서도 111에서 +1하면 오버플로우 돼서 앞으로 가져온 1이 사라지고 000만 남음.

     

    Y2K문제 : 컴퓨터가 처음 나왔을 1900년대 중반엔 저장하는게 비싸서 1970년도 등을 끝의 두자리만 저장함. 때문에 2000년에 오버플로우를 겪지않으려고 나중에 프로그래머들에게 수백만달러를 주고 문제를 해결해야했다

    보잉787 : 전원을 킨지 248일 후에 오버플로우가 발생해 비행기내 전력이 끊기게(강제로안전모드) 설계됨. 변수를 0으로 다시 리셋하기 위해 248일마다 재시동했대.

     

    이런 문제는 오늘날에도 발생.

Designed by Tistory.