관리 메뉴

흰둥씨의 개발장

[모두를 위한 컴퓨터 과학(CS50 2019)] C언어 본문

[오늘의 공부]/CS

[모두를 위한 컴퓨터 과학(CS50 2019)] C언어

돈워리비해삐 2023. 6. 15. 03:33

https://sandbox.cs50.io/

1) C 기초

//hello.c 

#include <stdio.h>

int main(void)
{
    printf("hello world\n"); //    \n 줄바꿈의미
}

스크래치에서 블록은 시작하는 역할

; (세미콜론)은 코드를 끝내는 의미

C에서는 스크래치의 블록과 같은 함수를 사용하기 위해 컴퓨터에게 그 함수가 어디에 있는지 알려줘야 함
ㄴ> printf같은 함수는 stdio.h안에 있다는 것을 알려주기위해 #include <stdio.h>작성함 (~함수쓰려면 stdio.h파일 보라고 하는것)

source code(**언어로 입력한 코드)  =>  컴파일러(번역기)  => machine code(컴퓨터가 이해할수 있는 2진수코드)

프롬프트에 $clang hello.c 라고 입력후 엔터치면 a.out이라는 파일이 생성됨 = a.out은 hello.c를 머신코드로 변환한 파일

$clang 변환하고자 하는 파일명 //컴파일명령어
$clang -o hello hello.c. //hello.c를 hello 파일안에 컴파일해라
$./a.out    //현재 디렉토리에 있는 a.out파일을 실행해라

 

hello.c의 코드를 수정하면 다시 컴파일 명령어를 프롬프트에 입력하고, 머신코드를 실행시켜야함 

$ls    //현재 디렉토리에 있는 파일들 리스트 알려줌
$rm 파일명 // 입력한 파일명 삭제요청
$mkdir 무슨폴더  //무슨폴더라는 디랙토리 생성
$rmdir 무슨폴더  //무슨폴더라는 디랙토리 삭제

2) 문자열 

스크래치의 ask함수와 같은 기능을 C에서는 get_string 함수가 가장 유사하게 수행함

문자열은 "쌍따옴표"안에 들어간 0개이상의 문자, 알파벳이 들어간 것을 의미 
ㄴ하지만 C에는 문자열이라는 것이 없음 

#include <stdio.h>

int main(void)
{
    string answer = get_string("네 이름이 무엇이냐?\n");  //입력값을 받아서 활용하기 위해 변수에 저장
	//변수명 answer 왼쪽에 타입지정 string으로 함 

	printf("hello, %s\n",answer); // %s는 형식지정자이고, answer가 들어갈 위치를 지정함
	//printf("hello, %s\n %s",answer, answer2); %s가 여러개이면 1번째 자리에 1번째 변수대입되어 처리됨
}

ㄴ위와 같이 입력후 컴파일하면 에러발생 => string을 해석하지 못하는 중 

=> 문자열은 강의가 제공하는 CS50 라이브러리안에 있어서 아래와 같이 코드 수정 하고 컴파일시 터미널에 아래 명령어 수행 

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string answer = get_string("네 이름이 무엇이냐?\n"); 

	printf("hello, %s\n",answer); 

}
 $clang -o string string.c -lcs50   //-lcs50는 cs50랑 string.c랑 link in하기위해 작성

make

-lcs50와 같은 명령어를 추가하는 것이 어렵다면 컴파일시 터미널에 clang...말고 "make 생성하고자하는파일명"을 입력하기 

make string   // string.c파일을 알아서찾고 필요한 것들 연결하여 컴파일한 파일 string으로 생성됨

ㄴ근데 뫄뫄.c라는 파일을 컴파일하고 싶은데 make 모모라고 입력하면 못 찾음  =>make 뫄뫄 라고 입력해야 해당파일을 컴파일 함


3) 조건문과 루프 

{}블록문 뒤에는 세미콜론 불필요 

스크래치에서의 forever는 C에서 while과 유사

int i = 0;

while(i<50)  //i 가 50보다 작을때까지 아래 문을 반복함 
{   
	printf("hello\n");
	i++;
}

 

반복할때는 for루프를 더 많이 사용함  (아래 코드는 위 코드와 같음)

for(int i = 0; i<50 ; i++)
{
	printf("hello\n");
}

 


4) 자료형, 형식 지정자, 연산자

- 데이터 타입

  • bool: 불리언 표현, (예) True, False, 1, 0, yes, no
  • char: 문자 하나 (예) 'a', 'Z', '?'
  • string: 문자열
  • int: 특정 크기 또는 특정 비트까지의 정수 (예) 5, 28, -3, 0
  • long: 더 큰 크기의 정수
  • float: 부동소수점을 갖는 실수 (예) 3.14, 0.0, -28.56
  • double: 부동소수점을 포함한 더 큰 실수

** int는 대략 40억까지 셀 수 있기 때문에
    40억게 이상의 데이터를 가진 일부 거대 기업과 같은 상황이 아닌
    일반 사용자들은 대부분 정수에 int를 사용합니다.

 

- 형식 지정자

  • %c : char
  • %f : float, double
  • %i : int
  • %li : long
  • %s : string

 

- 기타 연산자 

  • +:  더하기
  • -: 빼기
  • *: 곱하기
  • /: 나누기
  • %: 나머지
  • &&: 그리고
  • ||: 또는

 

- 주석

  • // 주석
  • /* 주석 */
//int.c
#include <cs50.h>
#include <stdio.h>

int main(void)
{
    int age = get_int("What's your age?\n"); //get_int는 cs50라이브러리에 있는 함수
    int days = age*365;
    printf("You are at least %i days old. \n",days);
}
#include <cs50.h>
#include <stdio.h>

int main(void)
{
    int price = get_float("얼마에요?");
    printf("세금 포함 총액은 %.2f입니다. \n", price*1.1);  //%.2f는 소숫점 2자리까지 나타내겠다는 의미
}
#include <cs50.h>
#include <stdio.h>

//어떤 수가 홀수인지 짝수인지 알려줌
int main(void)
{
    int n = get_int("n:");  //프롬프트에 n: 을 입력받음 
    if(n % 2 == 0)         //입력값이 짝수이면
    {
        printf("even\n");
    }
    else                   //입력값이 짝수아니면
    {
        printf("odd\n");
    }
}
#include <cs50.h>
#include <stdio.h>

//
int main(void)
{
   char answer = get_char("Do you agree?\n");
   if(answer=='Y'|| answer =='y') //char는 한개의 문자열만 허용해서 answer =='yes'같은 조건은 불가
   {
       printf("Agreed.\n");
   }
   else if(answer=='N'|| answer =='n')
   {
      printf("Not agreed.\n"); 
   }

}

 

 


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

#include <stdio.h>

//기침 함 => 근데 똑같은 코드 여러번 있는건 보기에 좋지 않다고 함
int main(void)
{
    printf("cough\n");
    printf("cough\n");
    printf("cough\n");
}

중복코드를 for문으로 고침 


#include <stdio.h>

//기침 3번함
int main(void)
{
    for(int i = 0; i < 3 ; i++)
    printf("cough\n");
}

기침하는 함수 + for문으로 호출함


#include <stdio.h>

//기침 3번함
void cough(void)
{
	printf("cough\n");
}

int main(void)
{
    for(int i = 0; i < 3 ; i++)
    cough();
}

호출할 cough함수를 main 함수보다 아래에 위치시키면 메인함수안에서 호출된 cough()를 인지 하지 못함 


#include <stdio.h>

//main함수에서 cough를 호출하는데 cough가 main보다 아래 선언되어있으면 인지하지 못함

int main(void)
{
    for(int i = 0; i < 3 ; i++)
    cough();  //호출지점보다 
}
void cough(void)  //선언이 아래되어있음
{
	printf("cough\n");
}

함수의 프로토 타입 한줄을 위에 추가해서 main함수가 바로 보이고, 세부적인 함수인 cough는 아래 위치할수 있음

#include <stdio.h>

void cough(int n);   //함수의 프로토 타입을 추가하면

int main(void)
{
    cough(3);  //cough함수가 나올때까지 찾아서 매개변수 3으로 전달함
}

void cough(int n)  //매개변수를 받아서 그 수만큼 기침함
{
	for(int i = 0; i < n ; i++)
    {
        printf("cough\n");
    }
}
#include <cs50.h>
#include <stdio.h>

int get_positive_int(void);

int main(void)
{
	int i = get_positive_int();
    printf("%i\n", i);
}

int get_positive_int(void)    //매개변수는 없고(void), return 할 값은 int임
{
	int n;
    do                       //do-while루프 : do 해서 while조건을 만족하면 다시 do해라
    {                        
    	n = get_int("Positive Integer : ");  //cs50라이브러리 함수로 사용자에게 숫자를 받아 리턴하는 함수
    }
    while(n < 1);            // n이 0, -1같은 값이면 계속 do 해서 질문함
    return n;				 // n이 1보다 작지 않으면 n을 리턴함
    
}

 

#으로 블록쌓기

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    int n;

    do
    {
        n = get_int("Size: ");
    }
    while (n < 1);   //n이 1보다 작지 않으면 아래 코드 실행, n이 1보다 작으면 do 실행

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            printf("#"); //n번= j번 만큼 #을 출력해라
        }
        printf("\n"); //n번 엔터하고
    }
}

//결과적으로 #이 n개씩 * n줄 출력됨

 

6) 하드웨어의 한계 

ㄴ메모리 용량이 프로그램 구동에 미치는 영향

메모리 = RAM
ㄴ모든 프로그램이 실행중 저장되는 공간
ㄴ유한한 공간이기 때문에 정확하지 않은 결과를 나타내기도 함 

콤퓨타의 한계1) 부동소수점 부정확성

//사용자에게 몇개의 실수 값을 받아오는 프로그램
#include <cs50.h>
#include <stdio.h>

int main(void)
{
    float x = get_float("x : ");
    float y = get_float("y : ");

    printf("x / y = %.30f\n", x / y);  // x / y를 연산하고, 소숫점이하 30자리까지 나타내기
}

ㄴ위 코드를 실행하여 x에는 1, y에는 10을 할당하면 x / y = 0.100000001490116119384765625000 이라는 값이 출력된다.
ㄴ1/10는  0.100000이지만 컴퓨터가 이를 저장할때는 이진수로 변환하기 때문에 무한소수가 되어 메모리의 한계로 근사 값 저장함
ㄴfloat는 32bit저장가능하고, double은 64비트 저장가능함 => double이 좀더 정확히 결과를 낼수있기는 하지만 
ㄴ결국은 모두 유한한 공간으로 한계를 가짐 

 

콤퓨타의 한계2) int overflow

//정수를 2배씩 계속 늘려서 출력하기 
#include <unistd.h> //sleep함수불러오려고 
#include <stdio.h>

int main(void)
{
   for(int i = 1; ; i *=2)  //조건문이 비워져 있어서 무한루프가 됨
   {
       printf("%i\n", i);
       sleep(1);    //1초씩 쉬세요
   }
}

ㄴ숫자가 계속 커지다가 어느순간 에러메시지와 함께 0 이 계속 출력됨
ㄴint는 32비트까지 저장하기 때문에 이진수로 변환시 32개의 자리이상의 숫자는 저장할수가 없음
ㄴY2K문제(1997년을 97이라고만 저장하도록 했기 때문에 2000년대가 오면 00으로 정수 오버플로우가 발생해서 2000을 1900로 인식한다는 문제)도, 보잉 787문제도 여기서 비롯됨 => 더 많은 메모리를 활용해서 해결