모두를 위한 컴퓨터 과학(CS50 2019) [2. C언어] 강의
David J. Malan (데이비드 J. 말란)의 <모두를 위한 컴퓨터 과학(CS50 2019)> 수강 내용
https://www.boostcourse.org/cs112/joinLectures/41307
2. C언어
1) C 기초
학습목표
C로 “hello, world”를 출력하는 프로그램을 만들 수 있습니다.
핵심키워드
- studio.h
- clang
- 컴파일러
C언어
#include <stdio.h>
int main(void)
{
printf("hello, world\n");
}
C : 아주 오래되고 전통적인 순수 텍스트 기반의 언어
#include <studio.h>
- int main(viod) : 스크래치의 when clicked 블록과 같은 역할
→ '시작한다'의 의미를 가진 역할 - printf() : 스크래치의 say 블록과 같은 역할
- 글자나 단어, 문장을 적을 때는 언제나 텍스트에 "" 표기
- 문장의 끝에는 마침표로 C에서는 세미콜론(;)을 표기
- \n : 줄바꿈 기능, 키보드에서 ENTER의 기능과 동일
💡 파일저장 : C로 작성한 코드는 확장자 "c"를 붙여서 "파일이름.c"로 저장
컴파일러
- source code : 우리가 직접 작성한 코드
- ex) C, Python, Java, C++ etc
- machine code : 컴퓨터가 실제로 이해하는 '0','1'의 조합
- compiler : 소스코드로부터 머신코드를 얻기 위해서 번역을 수행하는 알고리즘 혹은 소프트웨어를 지칭
- clang : 코드를 컴파일하는 프로그램의 이름
→ clang을 사용해서 소스코드를 머신코드로 번역가능
- clang : 코드를 컴파일하는 프로그램의 이름
Clang hollo.c
- 터미널창의 명령어 프롬프트에서 $ 기호 옆에 우리가 원하는 명령어 입력
- "clang"이라는 컴파일러로 "hello.c"라는 코드를 컴파일하라는 의미
→ a.out이라는 파일 생성
./a.out : 컴퓨터가 현재 디렉토리에 있는 a.out이라는 프로그램을 실행
2) 문자열
학습목표
C로 문자열 형식을 가진 변수를 선언하고 출력하는 프로그램을 만들 수 있습니다.
핵심키워드
- 형식지정자
- string
- make
string
스크래치 : ask 함수 = CS50 Sandbox : get_string 함수
String은 단어나 구절, 문장을 부르는 말(숫자와는 다른 종류의 데이터)
string answer = get_string("what's your name?\n");
💡 할당 연산자
프로그래밍 언어에서 '=' 오른쪽에 있는 것을 왼쪽에 지정한다는 의미로 생각
stting answer = get string("what's your name?\n");
printf("hello, %s\n", answer);
형식지정자
c 언어는 오래된 언어이기 때문에 변수가 저장하는 데이터의 종류를 아주 정확하게 명시해야되기 때문에 화면상에 출력하고자 하는 구절이나 문장을 적는데 그 값이 아직 모른다면 %와 문자열을 의미하는 s를 적어 형식지정자를 사용
- answer이라는 변수에 들어있는 이름을 출력하려면 %를 사용
- 문자열을 받기 때문에 string에서의 s를 %뒤에 붙여서 인자를 받는다
make
#include <stdio.h>
int main(void)
{
string answer = get_string("What's your name?\n");
printf("hello, %s\n", answer);
}
위 프로그램을 컴파일(소스코드에서 머신코드로 번역) 하려면
$clang string.c
- 기본설정으로 인해 프로그램 이름이 a.out으로 저장
- 앞에 -o string을 입력하여 프로그램 이름을 string으로 지정
$clang -o string string.c
→ Error
-
컴퓨터가 string이라는 문자열 형식과 get_string이라는 함수에 대한 코드를 모르기 때문에 에러가 발생
-
string 문자열 형식과 get_string 함수 코드를 아는 cs50.h를 소스코드에 추가하고 명령어 -lcs50으로 연결
- 소스코드 - #include <cs50.h>
- 컴파일 - -lcs50
#include <cs50.h> #include <stdio.h> int main(void) { string answer = get_string("What's your name?\n"); printf("hello, %s\n", answer); }
$clang -o string string.c -lcs50
하지만 이 모든 내용이 솔직히 말하면 쓸데없이 복잡하고 어렵기때문에 외우는 대신 개념만 이해하도록 하고 프로그램을 만들어 달라는 명령어를 사용
$make string
- make라는 명령어는 알아서 어떤 인자를 사용해야 할 지 파일명은 무엇으로 할 지 다른 사람들은 어떤 라이브러리나 코드를 연결했는지 등을 찾아준다
3) 조건문과 루프
학습목표
조건문과 루프를 c로 작성할 수 있습니다.
핵심키워드
- int
- if
- while
- for
int
int counter = 0;
int : 변수가 정수(integer)라는 것을 알림
counter : 변수의 이름
0 : 값에 0을 저장
💡 구문설탕Syntax Sugar
컴퓨터 프로그래밍에서 구문을 변경하여 사람이 이해 하고 표현하기 쉽게 코딩할 수 있도록 하기 위해 사용
counter = counter + 1; → counter += 1; → counter ++;
if
if (x < y)
{
printf("x is less than y\n");
}
if (x < y)
{
printf("x is less than y\n");
}
else
{
printf("x is not less than y\n");
}
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 (x < y)
{
printf("x is less than y\n");
}
else if (x > y)
{
printf("x is greater than y\n");
}
else
{
printf("x is equal to y\n");
}
: x가 y보다 작지도 크지도 않으면 남은 유일한 가능성이 x와 y가 같다는 것이기 때문에 더 간결하게 표현이 가능하다
→ 얼마나 효율적으로 메모리나 CPU를 적게 사용해서 코딩을 하는 것이 중요!!
루프
(1) 무한 반복
while (true)
{
printf("Hello, world\n");
}
(2) 특정 횟수
- while문
int i = 0; while (i < 50) { printf("Hello, world\n"); i++; }
- for문
for (int i = 0; i < 50; i++) { printf("Hello, world\n"); }
4) 자료형, 형식 지정자, 연산자
학습목표
다양한 데이터 타입과 형식 지정자를 나타내는 방법을 학습합니다.
다양한 연산자를 이용하여 조건문을 표현하는 방법을 학습합니다.
핵심키워드
- char
- long
- float
- double
- %
- &&
- ||
데이터 타입
아래 목록은 변수의 데이터 타입으로 사용할 수 있는 것들
- bool : 불리언 표현
ex) True, False, 1, 0, yes, no- char : 문자 하나
ex) 'a', 'z' '?'- string : 문자열
- int :특정 크기 또는 특정 비트까지의 정수
ex) 5, 28, -3, 0- long : int보다 더 큰 크기의 정수
- float : 부동소수점을 갖는 실수
ex) 3.14, 0.0, -28.56- double : 부동소수점을 포함한 더 큰 실수
CS50 라이브러리 내의 get 함수
데이터 타입을 입력값으로 받을 수 있는 아래와 같은 함수들을 포함
- get_char
- get_double
- get_float
- get_int
- get_long
- get_string
형식 지정자
printf 함수에서는 각 데이터 타입을 위한 형식 지정자를 사용 가능
- %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;
printf("You are at least %i days old.\n", days);
}
더 간단하게 코드 작성
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int age = get_int("What's your age?\n");
printf("You are at least %i days old.\n", age * 365);
}
더 간단하게 코드 작성
#include <cs50.h>
#include <stdio.h>
int main(void)
{
printf("You are at least %i days old.\n", get_int("What's your age?\n") * 365);
}
✅ 극단적으로 줄여버린 코드는 디자인적 측면에서나 가독성에서나 좋지 못하여 이전 코드가 나은 선택
실수 데이터 받아서 출력
#include <cs50.h>
#include <stdio.h>
int main(void)
{
float price = get_float("What's the price?\n");
printf("Your total is %f", price * 1.0625);
}
💡 소수점 2자리까지만 보려면 형식 지정자 %.2f를 사용한다
#include <cs50.h>
#include <stdio.h>
int main(void)
{
float price = get_float("What's the price?\n");
printf("Your total is %.2f", price * 1.0625);
}
짝수인지 홀수인지 알려주는 코드짜기
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int n = get_int("n: ");
if(n % 2 == 0)
{
printf("even\n");
}
else
{
printf("odd\n");
}
}
주석
C에서는 //로 주석이 가능
//주석입니다.
- 기능적으로는 아무 의미 없지만 일종의 포스트잇
- 코드를 보는 모든 사람들을 위해 이 코드가 무슨 일을 하는지 설명하는 글
- 주석으로 잘 설명하는 습관이 중요
5) 자료형, 형식 지정자, 연산자
학습목표
사용자 정의 함수와 중첩 루프를 작성할 수 있습니다.
핵심키워드
- 사용자 정의 함수
- 중첩 루프
사용자 정의 함수
'cough'라고 3번 말하는 C 프로그램을 작성하려면?
-
기본적인 작성
#include <stdio.h> int main(void) { printf("coungh\n"); printf("coungh\n"); printf("coungh\n"); }
-
for문으로 작성
#include <stdio.h> int main(void> { for(int i = 0; i < 3; i++) { print("cough\n"); } }
-
우리만의 함수(사용자 정의 함수)
#include <stdio.h> void cough(void) { ptintf("cough\n"); } int main(void) { for (int i = 0; i < 3; i++) { cough(); } }
- 위 코드는 함수를 여러 개 만들수록 main 함수가 아래로 내려가기 때문에 문제가 발생
→ void cough(void)를 세미클론과 함께 올려서 C를 속이는 방법으로 해결
#include <studio.h> void cough(void); int main(void) { for (int i = 0; i < 3; i++) { cough(); } } void cough(void) { printf("cough\n"); }
- cough 함수를 좀 더 다재다능하게 만들 수 있다.
→ void cough(int n){...} : cough 함수를 직접 정의하기
#include <stdio.h> void cough(int n); int main(void) { cough(3); } void cough(int n) { for (int i = 0; i < n; i++) { printf("cough\n"); } }
- 위 코드는 함수를 여러 개 만들수록 main 함수가 아래로 내려가기 때문에 문제가 발생
예제
#include <cs50.h>
#include <stdio.h>
int get_positive_int(void);
// 1번
int main(void)
{
int i = get_positive_int(); // 2번, 3번
printf("%i\n", i);
}
// 4번, 5번
int get_positive_int(void) // 6번, 7번
{
int n; // 8번
do // 9번
{
n = get_int("Positive Integer: ");
}
while (n < 1);
return n;
}
// 1번 : get_positive_int 함수는 CS라이브러리에 없는 함수
// 2번 : 입력을 받지 못 했기 때문에 괄호 안이 비었음
// 3번 : 아무 양의 정수를 받으면 된다
// 4번 : get_postive_int 함수가 무언 가를 반환하게 하기 위해 int
// 5번 : int get_positive_int(void)에서 함수의 좌측 int는 출력의 종류
// 6번 : int get_positive_int(void)에서 괄호 안의 void는 입력의 종류
// 7번 : 만약 입출력이 없다면 void를 사용
// 8번 : int n;은 어떤 값을 저장할 지 아직 모르기 때문에 임시 표기
→ garbage value을 가지게 된다
// 9번 : do-while 루프
불리언 표현 while(n<1);이 참일 때 수행
→ n이 1보다 작으면 계속해서 질문을 반복
💡 while문과 do-while문의 차이
- while문은 조건이 참이어야만 수행
- do-while문은 do에서 무조건 한 번은 먼저 수행
중첩 루프
예시 1. ????블록
-
1) 보이는 대로 코드
// Prints a row of 4 question marks #include <stdio.h> int main(void) { printf("????\n"); }
-
2) 중첩 루프를 사용한 코드
// Prints a row of n question marks with a loop #include <cs50.h> #include <stdio.h> int main(void) { int n; do { n = get_in("width: "); } while (n < 1); for (int i = 0; i < n; i++) { printf("?"); } printf("\n"); }
예시2. 2차원 공간
#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");
}
}
- int n;으로 정수 값을 갖는 변수 n을 정의
- do{...}while()을 이용해서 while()의 조건이 만족할 때까지 get_int 함수로 사용자가 입력값을 받아 n에 저장
→ do{}while()을 사용하면 조건과 상관없이 최소한 한 번은 {}안의 내용을 실행할 수 있기 때문에 - for 루프를 두 번 중첩해서 돌면서 "#블록"을 출력
- 외부루프 : 변수 i를 기준으로 n번 반복 : 세로 "#블록" 생성
- 내부루프 : 변수 j를 기준으로 n번 반복 : 가로 "#블록" 생성
→ 내부루프에서 "#블록"을 출력하고, 내부루프가 끝날 때마다 외부루프에서 줄바꿈을 실행하면서 최종적으로 가로 n개, 세로 n개인 "#블록"이 출력
6) 하드웨어의 한계
학습목표
메모리의 용량이 프로그램의 구동에 미치는 영향을 설명할 수 있습니다.
핵심키워드
- 메모리
- 오버플로우
컴퓨터는 RAM(랜덤 엑세스 메모리)이라는 물리적 저장장치를 포함
즉, 우리가 작성한 프로그램은 구동 중에 RAM에 저장
But RAM은 유한한 크기의 비트만 저장할 수 있어 때때로 부정확한 결과를 내기도 한다
부동 소수점 부정확성
실수 x, y 를 인자로 받아 x 나누기 y를 하는 프로그램
#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);
}
나눈 결과를 소수점 50자리까지 출력(%.50f)하고, x에 1, y에 10을 입력하면 다음 결과가 나온다
x: 1
y: 10
x / y = 0.10000000149011611938476562500000000000000000000000
✅ 정확한 결과는 0.1이 되어야 하지만, float에서 저장 가능한 비트 수가 유한하기 때문에 다소 부정확한 결과가 나온다
정수 오버플러우
1부터 시작하여 2를 계속 곱하여 출력하는 프로그램
#include <stdio.h>
#include <unistd.h>
int main(void)
{
for (int i = 1; ; i *= 2)
{
printf("%i\n",i);
sleep(1);
}
}
변수 i를 int로 저장하기 때문에 2를 계속 곱하다가 int타입이 저장할 수 있는 수를 넘은 이후에는 아래와 같은 에러와 함께 0이 출력
...
1073741824
overflow.c:6:25: runtime error: signed integer overflow: 1073741824 * 2 cannot be represented in type 'int'
-2147483648
0
0
...
✅ 정수를 계속 키우는 프로그램에서 10억을 넘기자 앞으로 넘어갈 1의 자리가 없어진 것
why? int에서는 32개의 비트가 다였기 때문, 그 이상의 숫자는 저장불가
실제 오버플로우 사례
- 1990, Y2K 문제
연도를 마지막 두 자리수로 저장했던 관습때문에 새해가 오면 '99'에서 '00' 으로 정수 오버플로우가 발생했는데, 새해가 2000년이 아닌 1900년으로 인식된다는 문제
✅ 수백만 달러를 투자해서 프로그래머들에게 더 많은 메모리를 활용해서 해결
- 비행기 보잉 787 문제
구동 후 248일이 지나면 모든 전력을 잃는 문제 → 강제로 안전 모드로 진입
248일 1/100초로 계산하면 대략 232
보잉을 설계할 때 사용한 변수보다 너무 커졌던 것
✅ 주기적으로 재가동하여 변수를 0으로 리셋
즉, 다루고자 하는 데이터 값의 범위를 유의하며 프로그램을 작성하는 것이 중요
Quiz 2
Author And Source
이 문제에 관하여(모두를 위한 컴퓨터 과학(CS50 2019) [2. C언어] 강의), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@khj930629/모두를-위한-컴퓨터-과학CS50-2019-2.-C언어-강의저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)