[CS50] C언어

고성인·2024년 6월 10일
0

CS

목록 보기
4/8

C언어

#include<stdio.h>

int main(void)
{
  printf("hello, world\n");
}

C언어는 전통적인 순수 텍스트 기반의 언어이다.
#include<stdio.h>는 stdio.h라는 파일에서 printf함수에 접근할 수 있도록 해주는 역할을 하며, C로 작성한 코드는 "파일이름.c"로 저장해야한다.

컴파일러

이전에 컴퓨터는 0,1의 2진수를 사용하여 모든 정보를 표현한다 하였는데 위의 코드를 보면 2진수와는 매우 다른 모습을 볼 수 있다.
그렇다면 컴퓨터는 어떻게 c로 작성된 파일을 실행할까?
우선 우리가 작성한 코드는 "소스 코드" 라고 불리며 그 자체로는 컴퓨터가 이해하지 못한다. 그렇기 때문에 이를 2진수로 작성된 "머신 코드" 로 바꿔주는 작업이 필요한 데 이런 작업을 해주는 프로그램이 바로 컴파일러다.

clang 파일이름.c
clang -o 이름 파일이름.c

위 방법으로 머신 코드 파일을 만들 수 있으며, 아래 명령어로 만들 시 "이름"의 파일명으로 만들어진다.

문자열 입력

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

int main(void)
{
  string answer = get_string("What's your name?\n");
  printf("hello, %s\n", answer);
}

사용자에게 문자열을 입력받으려면 어떻게 해야할까?
우선 문자열(string)이란 프로그래머들이 단어나 구절, 문장을 부르는 말을 의미한다.
C에서는 get_string함수를 통해 입력을 받으며, 입력값을 사용하기 위해서는 변수에 저장을 해줘야한다.
변수의 이름은 마음대로 정해도 되며, C에서는 변수의 타입을 정확히 명시해줘야하기 때문에 변수명의 왼쪽에 타입을 적어줘야한다.

또한 수학에서의 =의 의미는 왼쪽식과 오른쪽식이 같다는 의미지만 프로그래밍 언어에서의 =의 의미는 오른쪽에서 왼쪽으로 가는 화살표의 느낌이다.
할당연산자라고 불리며 오른쪽 값을 왼쪽 변수에 저장해주는 역할을 한다.
이렇게 변수에 저장이 끝나면 메모리에 값이 저장되게된다.

코드를 보면 printf에서 %s라는것이 있는데, 이것은 문자열의 형식지정자로 이를 통해 변수의 문자열을 출력할 수 있다.

또한 맨 위 #include <cs50.h>라는 부분이 추가되었는데, 기본적으로 c에서는 string이라는 타입과 get_string이라는 함수가 포함되어있지 않기 때문에 이것들이 포함된 cs50이라는 파일을 포함해야만 컴파일하고 실행할 수 있다.

이 경우 컴파일할때의 명령어가 달라지는데, 아래와 같이 입력하여 컴파일할 수 있다.

clang -o string string.c -lcs50

-lcs50은 link의 의미를 지닌 -l이라는 인자에 추가로 포함한 cs50파일을 합친것이다.

하지만 이 과정은 다소 복잡하기 때문에 아래와 같은 make명령어를 통해 간단히 컴파일을 수행할 수 있다.

make string

조건문과 반복문

조건문

#include<stdio.h>

int main(void)
{
  int x = 10;
  int y = 20;
  if(x<y){
    printf("x is less than y\n");
  }
  else if(x>y){
    printf("x is greater than y\n");
  }
  else if(x==y){
    printf("x is equal to y\n");
  }
}

조건문은 if를 사용하여 나타낼 수 있는데, if(조건)이 true일 시 해당 블럭의 코드를 실행하고 false일 시 다음 조건을 본다.
해당 코드에서 x==y부분이 있는데, equal의 표현을 왜 ==로 했을까?
이유는 = 한개의 경우는 이미 할당연산자로 정의를 해놨기 때문에 equal를 표현할때는 ==로 표현하기로 약속했기 때문이다.

반복문

#include<stdio.h>

int main(void)
{
  int i = 0;
  while(i<50){
    printf("hello, world\n");
    i++;
  }
}

while로 반복문을 만들 시 while(조건)에서 조건이 참일경우 계속해서 반복을 진행해준다.
따라서 위의 코드에서 i가 50보다 작을경우만 반복을 진행하고, 매 반복시 i를 1씩 증가시키는 코드를 통해 50번 반복 후 종료되게 만들었다.
여기서 i++가 무엇일까?
i를 1씩 증가시키는 코드인데, 기본적인 모습은 i = i + 1이다.
수학적으로는 말이안되는 표현이지만 프로그래밍에서의 =은 할당연산자인 것을 기억해두자.
따라서 해당 코드는 i라는 변수에 i+1의 값을 저장하는 코드로, 진행 시 기존 숫자에 1이 더해진 값이 저장된다.
이 과정을 단순하게 줄인 코드가 바로 i++이다.

#include<stdio.h>

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

for문을 통해 반복을 진행한 코드는 위와 같다.
해당 코드 역시 50번 반복하는 반복문인데, for(변수, 조건, 식)을 통해 변수를 선언하고, 해당 조건이 true일 경우 반복하며 식을 통해 반복마다 수행할 코드를 적어준다.
위의 코드 역시 i는 0에서 시작하여 매 반복마다 i에 1을 더해주고 i가 50보다 작은 경우에만 반복을 진행해주는 코드이다.

데이터 타입

아래 항목들은 변수의 데이터 타입으로 사용할 수 있는 것들이다.

  • bool: 불리언 표현
  • char: 문자 하나
  • string: 문자열
  • int: 특정 크기 또는 특정 비트까지의 정수
  • long: 더 큰 크기의 정수
  • float: 부동소수점을 갖는 실수
  • double: 부동소수점을 갖는 더 큰 실수

형식 지정자

%s이외에도 다양한 형식 지정자가 있다.

  • %c: char
  • %f: float, double %2f같은 형식으로 소수점 아래 몇개까지 표현할지 설정할수도 있다.
  • %i: int
  • %li: long
  • %s: string

기타 연산자 및 주석

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

사용자 정의 함수

#include <stdio.h>

void cough(void)
{
    printf("cough\n")
}

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

사용자 정의 함수는 위와 같이 만든다.
cough라는 함수를 정의하는데, 앞쪽에 void는 반환값이 없다는 의미이고, (void)는 입력값이 없다는 의미이다.

#include <stdio.h>

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

void cough(void)
{
    printf("cough\n");
}

첫번째 코드를 위와 같이 순서를 바꾸면 어떻게될까?
C는 생각보다 똑똑한 언어가 아니기때문에 오류가 난다. main함수에서 cough()를 호출할 시 선언된 함수가 없기 때문이다.
그렇다면 함수를 선언할때는 무조건 main함수 위에서 선언해야할까?

#include <stdio.h>

void cough(void);

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

void cough(void)
{
    printf("cough\n");
}

코드를 위와 같이 바꿔주면 된다.
바뀐점은 실제 함수는 main함수 아래에 선언되어있지만 main함수 위에 voi cough(void);를 입력해주면서 cough함수 전체를 본 적은 없지만 이름은 본 적이 있으니 cough함수가 나올때까지 코드를 계속 읽게 만드는 방법이다.

하드웨어의 한계

컴퓨터는 RAM이라는 주기억장치에 구동중인 프로그램이 저장되어지는데, RAM의 크기는 유한하기때문에 때때로 부정확한 결과를 내기도 한다.

부동 소수점의 부정확성

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

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

    float y = get_float("y: ");

    printf("x / y = %.50f\n", x / y);
}

위와같이 x,y값을 입력받아 x/y연산을 수행하는 코드가 있다고 하자.
x=1, y=10을 넣으면 결과가 어떻게나올까?
우리가 지금까지 배워왔던 수학적 지식으로는 당연히 0.1이 나와야한다.
하지만 실제 코드를 구동 시 0.10000000149011611938476562500000000000000000000000이라는 결과가 나온다.
그 이유는 float에서 저장 가능한 비트 수가 유한하기 때문이다.

정수 오버플로우

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    for (int i = 1; ; i *= 2)
    {
        printf("%i\n", i);
        sleep(1);
    }
}

비슷한 오류로 1부터 시작하여 2를 계속하여 곱하는 프로그램도 변수 i를 int로 저장하기 때문에 int타입이 저장할 수 있는 수를 넘은 이후에는 다음과 같은 에러가 발생한다.

참고자료

네이버 부스트코스 모두를 위한 컴퓨터 과학 (CS50 2019)
https://www.boostcourse.org/cs112/joinLectures/41486

0개의 댓글