[CS50 2019] 1. C

모두를 위한 컴퓨터 과학

1. C 언어 기초

#include <stdio.h>

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

#include <stdio.h>

  • 이 프로그램이 stdio.h라는 파일 안에 들어있는 미리 작성된 함수들에 접근할 수 있도록 함
  • 여기에서 함수란, 특정 기능을 수행하기 위한 문장들을 모아놓은 것
  • ex. 화면에 문자를 보여주는 역할을 하는 printf라는 함수

int main(void)

  • 프로그램의 시작점을 정의
  • 중괄호는 main 함수의 코드를 묶어주는 역할을 함

printf()

  • 화면에 보여주고자 하는 문자열을 입력값으로 넣어줄 수 있음
  • \n: 줄바꿈 문자
  • 세미콜론 (;): 프로그램 명령의 마지막이 어디인지를 알려줌

컴파일 (Compile)

  • 우리가 만든 소스코드를 컴퓨터가 이해할 수 있게 0과 1로 만드는 작업
  • 소스 코드를 일련의 0과 1들로 이루어진 오브젝트 코드(object code) 로 전환해주는 것

주석이란?

  • 문장 중 //이나 /**/ 안에 작성된 코드는 컴파일러에 의해 완전히 무시됨
  • 프로그래머들이 코드에 대한 설명을 기록하는데 주로 사용

2. 문자열

#include <stdio.h>

int main(void) 
{
  char animal[20];
  
  printf("좋아하는 동물은?");
  scanf("%s", animal);
  printf("내가 좋아하는 동물은 %s이다.", animal);
  
  return 0;
 }
  • string(문자열): 단어나 구절, 문장 등과 같이 문자의 나열로 이루어진 데이터 종류
  • =(할당 연산자): 오른쪽에 있는 것을 왼쪽에 지정한다.
  • %를 이용하여 사용자가 입력한 변수 출력

3. 조건문과 루프

✔ 부울 연산자

  • 참(True)거짓(False)을 판단하는 연산자
  • ex. <(더 작다), >(더 크다), ==(같다), <=(작거나 같다), >=(크거나 같다), !=(같지 않다)
  • 두 부울 값을 비교하는 연산도 가능 ex. &&(and), ||(or),,

조건문

  • 조건 분기: 다른 상황에 따라 다른 코드가 실행되어야 한다는 개념

✔ if 문

if (number > 0) 
{
  printf("The number is positive.\n");
}
else if (number == 0) 
{
  printf("The number is zero.\n");
} 
else 
{
  printf("The number is negative.\n");
}
  • if 문은 조건을 검사하고 해당 조건이 참인 경우에만 특정 블록의 코드를 실행
  • 만약 조건이 거짓이라면, else ifelse블록으로 넘어간다.

✔ switch 문

switch (day) 
{
  case 1: 
    printf("Monday\n"); 
    break;
  case 2: 
    printf("Tuesday\n"); 
    break;
  case 3: 
    printf("Wednesday\n"); 
    break;
  default: 
    printf("Other day\n"); 
    break;
}

조건식의 결과값에 따라 매칭되는 case의 코드를 실행

✔ 3항 연산자

int y = (x > 3) ? 2: 1;

식이 이면 : 기호 왼편의 값으로 계산되고, 거짓이면 오른편의 값으로 계산

루프 (반복 순환)

✔ while 문

while (i <= 5) {
  printf("%d\n", i);
  i++;
}

주어진 조건이 참일 동안 반복적으로 코드 블록을 실행

✔ for 문

for (int i = 1; i <= 5; i++) {
  printf("%d\n", i);
}
  • for(변수 초기화; 변수 조건; 증감식)을 통해 반복의 흐름 정의
  • 반복 횟수나 특정 범위의 값을 순회하는데 매우 편리하다.

4. 자료형, 형식 지정자

✔ 실수를 입력받아 출력하는 코드

#include <stdio.h>

int main() {
    double inputValue;

    // 사용자로부터 실수 입력 받기
    printf("실수 값을 입력하세요: ");
    scanf("%lf", &inputValue);

    // %.2f로 입력받은 실수 값을 소수점 둘째 자리까지 출력
    printf("입력한 실수 값: %.2f\n", inputValue);

    return 0;
}

데이터 타입

  • bool: 불리언 표현 ex) True, False, 1, 0
  • char: 문자 하나 ex) ‘a’, ‘B’, ‘?’
  • string: 문자열
  • int: 특정 크기 또는 비트까지의 정수 (40억까지 셀 수 있음)
  • long: 더 큰 크기의 정수
  • float: 부동 소수점을 갖는 실수 ex) 3.14, -28.56
  • double: 부동 소수점을 포함한 더 큰 실수

float vs double

  • float는 보통 4byte, double은 보통 8byte의 크기를 가짐
  • float보다 double더 높은 정밀도를 제공하기에 더 많은 메모리를 사용하고 상대적으로 연산이 느리다.
  • float는 메모리가 제한적인 상황, 예를 들어 그래픽 처리 장치에서 주로 사용
  • double더 높은 정밀도가 필요한 계산에서 사용
  • 대부분의 경우, double의 사용이 메모리나 성능 면에서 큰 차이를 만들지 않으면서 더 높은 정밀도를 제공하기에 double이 더 널리 사용됨

형식 지정자

프로그래밍 언어에서 변수를 출력할 때나 입력받을 때, 해당 변수의 데이터 타입출력 형식을 지정하는 데 사용되는 기호

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

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

사용자 정의 함수

  • 사용자 정의 함수를 이용하면 동일한 작업을 반복하는 것을 단순화할 수 있음
  • 함수의 이름 왼쪽에는 출력의 종류 / 오른쪽에는 입력의 종류를 지정
  • 출력의 종류 + 함수 이름 + (입력의 종류)
// 매개변수를 받지 않는 함수
int simpleFunction(void) {
    return 42; // 임의의 정수 반환
}

void 타입

  • void란 함수의 매개변수가 없음을 나타냄
  • 즉, 입출력이 없을 때 void 타입으로 지정
  • int simpleFunction()과 같이 void 생략 가능

중첩 루프

  • 하나 이상의 루프가 다른 루프 안에 포함된 형태
  • 외부 루프와 내부 루프로 이루어져 있으며, 외부 루프가 한 번 반복될 때마다 내부 루프가 여러번 반복
  • 다양한 패턴이나 복잡한 작업 수행을 위해 쓰임

✔ 예시 코드

#include <stdio.h>

int main(void)
{
    int n;

    do
    {
        printf("Size: ");
        scanf("%d", &n);
    }
    while (n < 1);

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

    return 0;
}
>> Size: 3
###
###
###
  1. do-while 루프
    • do-while 루프는 루프 내부의 코드를 최소한 한 번 실행한 후에 조건을 검사
    • while()의 조건이 true일 동안 루프가 계속 실행
    • 즉, 사용자가 양의 정수를 입력할 때까지 계속해서 ‘Size: ‘를 출력
  2. 중첩된 for 루프
    • 사용자로부터 양의 정수를 입력받은 후, 이 크기에 따라 중첩된 for 루프를 사용하여 정사각형 출력

6. 하드웨어의 한계

  • 컴퓨터는 RAM이라는 물리적 저장장치를 포함하고 있음
  • 사용자가 작성한 프로그램은 구동 중에 RAM에 저장되는데, 유한한 크기의 비트만 저장할 수 있기 때문에 부정확한 결과를 내기도 한다.

부동 소수점 부정확성

#include <stdio.h>

int main() {
    float sum = 0.0;

    for (int i = 0; i < 10; ++i) {
        sum += 0.1;
    }

    printf("0.1을 10번 더한 결과: %.10f\n", sum);

    return 0;
}
>> 0.1을 10번 더한 결과: 1.0000000149

정확한 결과는 1이 되어야 하지만, float에서 저장 가능한 비트 수가 유한하기 때문에 다소 부정확한 결과를 내게 되는 것

정수 오버플로우

#include <stdio.h>

int main() {
    int maxInt = 2147483647; // int 자료형의 최댓값

    printf("최댓값: %d\n", maxInt);
    printf("최댓값 + 1: %d\n", maxInt + 1);

    return 0;
}
>> 
최댓값: 2147483647
최댓값 + 1: -2147483648
  • int 자료형의 최댓값을 초과한 값을 저장하려고 하면 정수 오버플로우가 발생하여 최소값으로 감소
  • 정수 자료형이 표현할 수 있는 범위를 벗어나서 생기는 문제