최초작성일: 2023-01-27
부스트코스 <모두를 위한 컴퓨터 과학 (CS50 2019)> 강의 수강
2. C언어
1. C 기초
- C로 "hello, world"를 출력하는 프로그램을 만들 수 있다.
- stdio.h, clang, 컴파일러
C언어
#include <stdio.h> //필요한 것. 없으면 컴퓨터가 printf를 이해하지 못함
//'stdio.h'라는 이름의 파일을 찾아 'printf' 함수에 접근할 수 있게 함
int main(void) //스크래치의 '초록 깃발을 클릭했을 때' 블록. '시작'의 의미
{
printf("hello, world\n");
//스크래치 '말해라' 블록
//개행 없으면 터미널에서 줄바꿈 안된 채로 나타남
//텍스트는 큰따옴표 안에. 문장 끝에는 세미콜론(;)
/*이 사이에 작성되는 것도 주석*/
}
- C는 아주 오래되고 전통적인 순수 텍스트 기반의 언어
- 파일 이름은
파일이름.c
로 저장
컴파일러
- 소스코드를 입력으로 받아 머신코드라는 출력을 내보내는 프로그램이 필요
소스코드
: 프로그래밍 언어로 작성된 코드머신코드(기계어)
: 컴퓨터가 실제로 이해하는 0과 1의 조합- 번역을 수행하는 소프트웨어가
컴파일러
터미널 명령어
clang
: 코드를 컴파일하는 프로그램. 컴파일러.
$ clang hello.c - clang으로 hello.c를 컴파일하라
- a.out(assembler output) 생성
$ ./a.out - 현재 디렉토리에 있는 `a.out` 프로그램 실행
$ clang -o hello hello.c - hello.c를 컴파일한 hello라는 파일 생성
$ ls - 현재 폴더 내 파일 목록 보여줌
a.out* hello.c - *는 실행가능, 즉 머신코드라는 의미
$ rm a.out - a.out 지우기
rm: remove regular file 'a.out'? - 지울 거냐고 물어봄. 'y'나 'yes' 입력하면 지워짐. 다른 것 입력하면 그냥 넘어감.
2. 문자열
- C로 문자열 형식을 가진 변수를 선언하고 출력하는 프로그램을 만들 수 있다.
- 형식지정자, string, make
이름을 입력받아 인사하는 프로그램
#include <cs50.h> //string이라는 형식과 get_string이라는 함수에 대한 코드 포함
#include <stdio.h>
int main(void)
{
//변수 answer. string이라고 데이터의 종류를 명시해줘야 함.
string answer = get_string("What's your name?\n"); //이때 string은 형식지정자
printf("hello, %s\n", answer); //'%s'는 형식지정자. string 의미.
//'%s'자리에 string이 들어가는데 그것은 뒤에 넣어 준 answer라는 변수의 내용
}
- 프로그래밍 언어에서
=
는할당연산자
: 오른쪽에 있는 것을 왼쪽에 지정한다는 뜻 get_string
함수가 사용자의 이름을 반환하면 그 이름을answer
라는 변수에 저장
$ clang -o string string.c -lcs50
string.c 파일을 컴파일하여 string이라는 머신코드로 저장
-l은 'link'의 의미. 컴파일시 cs50 파일을 연결하라
- 코드에만 cs50 파일을 보라고 하는 것이 아니라 컴파일할 때에도 cs50의 내용을 추가하도록 해야.
- cs50 또한 C로 작성되어 있을 것이고 기계어로 번역된 것이 어딘가에 있을텐데 그것과 연결되어야 두 코드가 한 프로그램으로 실행될 수 있음.
$ make string
- 간단하게 컴파일 수행
- 컴퓨터가 알아서 파일을 찾아 연결시켜 컴파일한다.
3. 조건문과 루프
- 조건문과 루프를 C로 작성할 수 있다.
- int, if, while, for
변수 생성, 값 저장
counter
라는 변수에 숫자를 저장하고 싶음- C는 저장하고자 하는 변수의 종류를 알려줘야.
int
: 변수가 정수(integer)라는 것을 알려줌
int counter = 0; //counter라는 변수에 0이라는 값을 저장(초기화, 할당)
counter = counter + 1; //counter에 1을 더한 값을 다시 counter에 저장(할당)
counter += counter;
counter++; // 모두 같은 의미
조건문
if (x < y) //보통 조건과 같은 것의 끝에는 세미콜론으로 끝내지 않음.
{
printf("x is less than y\n"); //함수가 들어간 줄들은 세미콜론으로 끝냄.
}
else if (x > y)
{
printf("x is greater than y\n");
}
else // else if (x == y) '=='는 '일치 연산자'
{
printf("x is equal to y\n");
}
반복문(loop)
while (true) //무한루프
{
printf("hello, world\n");
}
int i = o;
while (i < 50) // i < 50이 참이면 계속 수행하라
{
printf("hello, world\n");
i = i + 1;
}
for (int i = 0; i < 50; i++) // (변수 초기화; 변수 조건; 변수 변화)
{
printf("hello, world\n");
}
조건문: switch문
- 조건식의 결과값에 따라 매칭되는
case
의 코드를 실행
switch (x)
{
case 1: //만약 x가 1이면
printf("A\n"); //"A\n" 출력한 후
break; //switch문 빠져나옴
case 2: //x가 2이면
printf("B\n"); //"B\n" 출력 후
break; //빠져나옴
default: //그 외의 경우
printf("C\n"); //"C\n" 출력 후 switch문 빠져나옴
}
조건문: 3항 연산자
- 식 하나를 받아 식이 참이면 :기호 왼편의 값으로, 거짓이면 오른편의 값으로 계산
int y = (x > 3) ? 2 : 1; //x > 3가 참이면 y는 2, 그렇지 않으면 1
4. 자료형, 형식 지정자, 연산자
- 다양한 데이터 타입과 형식 지정자를 나타내는 방법을 학습한다.
- 다양한 연산자를 이용하여 조건문을 표현하는 방법을 학습한다.
- char, long, float, double, %, &&, ||
데이터 타입
bool
: 불리언 표현
참 / 거짓char
: single character. 단 하나의 문자double
: 소수점 뒤에 더 많은 숫자를 가질 수 있는 실수. 부동소수점을 포함한 더 큰 실수float
: 실수. 부동소수점을 갖는 실수int
: 정수. 특정 크기를 가짐. 정해진 수까지만 셀 수 있음. 보통 40억 정도long
: int
보다 더 많은 비트 사용. 더 높은 수까지 셀 수 있음string
: 문자열
큰따옴표 안에 들어간 한 개 이상의 문자
CS50 라이브러리 내의 get 함수
- 사용자에게 특정 값을 물어보고 입력한 값을 사용할 수 있게 함
- 원하는 데이터 타입으로 입력하지 않으면 계속 물어보게 되어있음
get_char
get_double
get_float
get_int
get_long
get_string
형식 지정자
%c
: char
%f
: float
, double
%i
: int
%li
: long
%s
: string
정수와 실수를 받아서 출력해보기
# include <cs50.h>
# include <stdio.h>
int main(void)
{
int age = get_int("what's your age?\n");
int days = age * 365; //이걸 없애고 age * 365 형태로 넣는 것도 가능
printf("Your are at least %i days old.\n", days);
// age 변수까지 없애고 get_int 함수를 printf 파라미터에 바로 넣는 것도 가능
}
#include <cs50.h>
#include <stdio.h>
int main(void)
{
float price = get_float("What's the price?\n");
printf("Your total is %f.\n", price * 1.0625);
// Your total is 106.250000. 출력
//자릿수 조절
printf("Your total is %.2f.\n", price * 1.0625);
// Your total is 106.25. 출력
}
짝수인지 홀수인지 알려주는 코드짜기
#include <cs50.h>
#include <stdio.h>
int main(void)
{
//사용자로부터 정수 입력받기
int n = get_int("n: ");
//입력받는 정수를 2로 나눈 나머지가 0일 경우
if (n % 2 == 0)
{
//"짝수" 출력
printf("even\n");
}
//그렇지 않으면
else if (n % 2 == 1) // else
{
//"홀수" 출력
printf("odd\n");
}
}
기타 연산자 및 주석
%
: 나머지||
: 'or(또는)'를 의미&&
: 'and(그리고)'를 의미//
: 이 기호 뒤에 이어서 쓰는 내용은 코드로 기능하지 않음. 코드의 설명을 씀.
5. 사용자 정의 함수, 중첩 루프
- 사용자 정의 함수와 중첩 루프를 작성할 수 있다.
- 사용자 정의 함수, 중첩 루프
사용자 정의 함수
#include <stdio.h>
int main(void)
{
for (int i = 0; i < 3; i++)
{
printf("cough\n");
}
}
- 반복문으로 만들어 길이를 줄였지만 기능이 한눈에 들어오지 않음
#include <stdio.h>
void cough(void)
{
printf("cough\n");
}
int main(void)
{
for (int i = 0; i < 3; i++)
{
cough();
}
}
- 함수를 정의해서 사용
void 함수명(void)
- 기능은 잘 알아볼 수 있지만 함수를 여러 가지 만들면 가장 중요한
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");
}
- 전에
cough
함수를 본 것처럼 C를 속이는 방법 - 함수 내용을 다 보지는 않았지만 이름은 봤으니까
main
함수에 나올 때까지 계속 코드를 읽는 것
#include <stdio.h>
void cough(int n); //함수 프로토타입
int main(void)
{
cough(3);
}
void cough(int n) //cough 함수 매개변수화
// 함수 이름 왼쪽은 출력의 종류, 오른쪽은 입력의 종류. 없으면 void
{
for (int i = 0; i < n; i++)
{
printf("cough\n");
}
}
- 이러면
main
부분만 보고도 어떤 기능을 수행하는지 알 수 있음 - 원하는 횟수만큼 cough 출력
- 파일 아래로 내려가면
세부 정의
를 확인할 수 있지만 그걸 모르더라도cough
함수를 활용할 수 있음
원하는 형태로 입력받기
#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) //입력은 없고 int를 반환하는 함수
{
int n; //어떤 값을 저장할지 몰라 이것만 적음. //n은 garbage value를 가짐
do //do-while loop
{
n = get_int("Positive Integer: ");
}
while (n < 1); //양의 정수 입력할 때까지 반복
return n;
}
do-while 루프
: 적어도 한 번은 do 부분을 수행한 뒤 반복을 계속할지 결정- while 루프는 먼저 조건을 확인
중첩 루프
#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++) //i
{
for (int j = 0; j < n; j++)
{
printf("#");
}
printf("\n");
}
}
6. 하드웨어의 한계
- 메모리 용량이 프로그램의 구동에 미치는 영향을 설명할 수 있다.
- 메모리, 오버플로우
메모리 용량은 유한하기 때문에 때때로 부정확한 결과를 낸다.
부동 소수점 부정확성
#include <cs50.h>
#include <stdio.h>
int main(void)
{
// 사용자에게 x 값 받기
float x = get_float("x: ");
// 사용자에게 y 값 받기
float y = get_float("y: ");
// 나눗셈 후 출력
printf("x / y = %.50f\n", x / y);
}
/*
x: 1
y: 10
x / y = 0.10000000149011611938476562500000000000000000000000
*/
- 정확한 결과는 0.1이지만
float
에서 저장 가능한 비트 수가 유한하기 때문에 다소 부정확한 결과를 냄 - 계산할 수 있는 값들 중 1/10에 가장 가까운 값 저장
- 유한한 정보를 사용해서는 무한한 숫자들을 정확하게 저장할 수 없다.
정수 오버플로우
#include <stdio.h>
#include <unistd.h>
int main(void)
{
for (int i = 1; ; i *= 2)
{
printf("%i\n", i);
sleep(1);
}
}
/*
...
1073741824
overflow.c:6:25: runtime error: signed integer overflow: 1073741824 * 2 cannot be represented in type 'int'
-2147483648
0
0
...
*/
int
타입이 저장할 수 있는 수를 넘은 후에는 에러.- 10억을 넘기자 앞으로 넘어갈 1의 자리가 없어짐.
float의 정확성이나 정수의 크기에 한계가 있다.
실생활에서의 오버플로우 문제
- Y2K
- 연도를 마지막 두 자리수로 저장했던 관습 때문에 2000년이 되면 '99'에서 '00'으로 정수 오버플로우가 발생하고 새해가 1900으로 인식된다는 문제
- 보잉787
- 구동 후 248일이 지나면 강제로 안전 모드로 진입, 모든 전력을 잃는 문제
- 소프트웨어의 변수가 248일이 지나면 오버플로우 발생
- 248일을 1/100초로 계산하면 대략 2의 32제곱
- 주기적으로 재가동해 변수를 다시 0으로 리셋
결론
다루고자 하는 데이터 값의 범위를 유의하며 프로그램을 작성해야 한다.
'NOTES > CS50' 카테고리의 다른 글
[CS50] Chapter 6. 자료구조 (0) | 2023.03.04 |
---|---|
[CS50] Chapter 5. 메모리 (0) | 2023.02.25 |
[CS50] Chapter 4. 알고리즘 (0) | 2023.02.13 |
[CS50] Chapter 3. 배열 (2) | 2023.02.04 |
[CS50] Chapter 1. 컴퓨팅 사고 (0) | 2023.01.30 |