스크립트 기반이다. (텍스트based 언어)
- 이 예제의 C파일명은
hello.c
로 한다.- 참고로, C언어로 파일을 만들 때, 관습적으로
.c
로 파일명을 마친다고 한다.
- 참고로, C언어로 파일을 만들 때, 관습적으로
#include <stdio.h>
int main(void)
{
printf("hello, world");
}
- C에서는 함수를 사용하기 위해서 그 함수가 어디에 구현되어 있는지 또는 어디에 저장되어 있는지 알려줘야 한다.
- 위 에제에서는
stdio.h
를 통해 합수에 접근하라고 지정했다. - C에서 가장 보편적으로 활용하는
라이브러리
가stdio.h
다. 라이브러리
란, 여러 함수들을 모아둔 것이라고 볼 수 있다.
- 위 에제에서는
- 코드 첫줄과
int
로 시작하는 코드 사이에 한 줄을 띄운 것은 단순히 가독성을 좋게 하기 위해서다. 안 띄워도 상관 없다. int main(void){}
는 코드 작성을 시작한다는 의미이다.printf()
는 뭔가를 화면 상에 출력하고 싶을 때 쓰는 함수다.- 괄호 사이에 값을 입력한다. 이때 입력값은 인자(argument) 또는 매개변수(parameter)라고 한다.
- 명령은
;
로 끝낸다. 마치 마침표처럼. - 텍스트는
" "
로 감싼다.String
=== 문자열. 쌍따옴표 안에 들어간 0개 이상의 문자 배열.
-
컴퓨터는 이진법만 알아듣기 때문에, C언어로 작성한
소스코드 source code
를머신코드 machine code
로 바꿔주는 일종의 프로그램이 필요하다.- 소스코드는 C, 파이썬 등의 언어.
- 머신코드는 컴퓨터가 알아듣는 말.
-
그런 '일종의 프로그램'을
컴파일러 compiler
라고 한다. -
터미널에
$ clang hello.c
을 입력한다.clang
은 코드를 컴파일하는 프로그램 이름이다.- 오류났을 때, 어디에서 그 오류가 발생했고, 어떻게 해결할 수 있을지 단서를 주기도 한다.
- 물론 실제로 해결할 때는 구글링해야 함 ㅋㅋㅋㅋㅋ
-
해당 명령 실행 결과로,
a.out
이라는 파일이 생성된다. -
터미널에
$ ./a.out
을 입력한다.- 이 명령은 컴퓨터가 현재 디렉토리에 있는 a.out이라는 프로그램을 실행하도록 한다.
- 이때,
./
는이 폴더 안에(있는)
를 의미한다.
-
이 결과,
hello, world$
가 출력된다. -
$
표시는 단지 프롬프트로, 이곳에 명령을 입력하라는 표시다.- 만약, 문자열 뒤에 출력되는
$
표시를 다음 줄로 옮기고 싶다면, 다음과 같이 진행한다.- 줄바꿈을 할 땐,
\n
을 입력한다.
#include <stdio.h> int main(void) { printf("hello, world\n"); }
- 이렇게 수정한 후 다시 컴파일(
$ clang hello.c
)을 해주고, 실행($ ./a.out
)하면 다음과 같은 결과가 출력된다.- 컴파일을 하지 않으면, 결과물은 달라지지 않는다(
hello,world$
가 그대로 출력된다.)
- 컴파일을 하지 않으면, 결과물은 달라지지 않는다(
hello, world $
- 줄바꿈을 할 땐,
- 만약, 문자열 뒤에 출력되는
-
컴파일 명칭을 맘대로(customize)하기 위해서는, 터미널에 다음과 같이 입력한다.
$ clang -o hello hello.c
- 소스코드
hello.c
에 대한 머신코드 파일hello
가 생성된다.
- 소스코드
-
터미널에서
$ ls
(리스트)를 입력하면, 현재 디렉토리에 포함되어 있는 파일 목록이 출력된다.- 위 예제를 따라했다면, 출력되는 결과는 다음과 같다.
a.out* hello.c
- 이때,
a.out
뒤에 붙은*
는 실행가능하다, 즉, 머신코드로 이루어진 파일이라는 의미이다. *
가 붙지 않은 hello.c는 소스코드라는 것을 알 수 있다.
-
또한, 터미널에서
$ rm
(삭제)을 활용하여 파일을 삭제할 수도 있다.$ rm a.out
- 이 후, 파일 삭제를 묻는 문장이 나타나면
y
또는yes
를 입력함으로써 명령어만을 사용하여 해당 파일을 삭제할 수 있다.
- 이 후, 파일 삭제를 묻는 문장이 나타나면
string
단어, 구문, 문장을 의미한다.
#include <stdio.h>
int main(void)
{
string answer = get_string("What's your name?\n");
printf("hello, %s\n", answer);
}
-
c에서 문자열을 쓰려면 추가 작업이 필요한 모양. 위 코드를 그대로 입력하면, 오류가 나타난다.
- string에 대해서 컴퓨터가 모르는 상태이기 때문.
- cs50수업을 위해 만들어진 라이브러리를 활용하면 문제를 해결할 수 있다.
- 이 수업에서 제공하는
CS50.h
라는 파일 안에 문자열 관련 도움을 주는 도구가 있다. 그렇기 때문에, 이 수업에서 지정한 샌드박스(사이트형태의 IDE)에서 다음과 같이 입력해야 한다.
// string을 쓰기 위해서 cs50.h을 참조하라는 명령 #include <cs50.h> #include <stdio.h> int main(void) { string answer = get_string("What's your name?\n"); printf("hello, %s\n", answer); }
- 또한,
get_string
함수,string
등 cs50에 들어있는 것을 구현하는 코드를 사용하려면, 터미널에서 다음과 같이 입력해줘야 한다.
$ clang -o string string.c -lcs50
l
는 link,cs50
에 연결하라는 의미.
-
그런데, 이것보다 훨씬 더 편리하게 해당 기능들을 사용할 수 있다.
- string과 get_string에 대한 위 내용들은 개념만 이해하고 다음과 같이 실전에서 활용하기.
- 터미널에 입력하는 값이다.
$ make string
make (프로그램이름)
- cs50에서만 제공하는 것이 아니라, 일반명령이다.
make
명령어를 쓰면, 컴퓨터가 알아서 어떤 argument(인자)를 사용해야 할지, 파일명은 무엇으로 할지, 다른 사람들은 어떤 라이브러리나 코드를 연결했는지 등을 찾아준다.
-
변수answer의 왼쪽에 저장하고자하는 값의 종류가 문자열(string)임을 알려줘야 한다.
=
는 오른쪽에서 왼쪽으로 가는 화살표와 유사하다. 오른쪽 내용을 왼쪽에 대입한다는 의미.
-
아직 대입할 값을 모른다면
%s
를 입력한다. 이때,s
는 string을 의미한다.- 이것을
형식지정자
라고 한다. - 만약, 입력하려고 하는 값이 문자열이라면, 모든 형식지정자에
%s
를 입력해야 한다.
- 이것을
-
여러 개의 인자를 받을 때는
,
(쉼표)로 구분해준다.
int counter = 0;
// 변수를 증가시키는 방법
counter = counter + 1;
counter += 1;
counter ++;
int
정수데이터. integer의 약자.
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 not less than y\n");
}
else if (x == y)
{
printf("x is equal to y\n");
}
==
는일치연산자
.=
는 할당연산자- 위 예제에서는 boolean이 3가지 등장한다. <,>,==
boolean
은 예/아니오, 참/거짓, 1/0의 값을 가진다.
while
{
printf("hello, world\n");
}
- 50번 반복하기
int i = 0; while (i < 50) { printf("hello, world\n"); i++; }
i++;
==i = i + 1;
for (int i = 0; i < 50; i++)
{
printf("hello, world\n");
}
- 위의 while문 예제와 같은 기능을 한다.
- 세 가지 조건을 입력해야 한다.
- 첫 조건 == 초기화.
- 두번째 == 계속해서 묻고 싶은 boolean 표현.
- 세번째 == 변수를 어떻게 수정할 것인가. update.
-
데이터타입
== 자료형bool: 불리언 표현, (예) True, False, 1, 0, yes, no char: 문자 하나 (예) 'a', 'Z', '?' string: 문자열 int: 특정 크기 또는 특정 비트까지의 정수. 약 40억까지 셀 수 있다. (예) 5, 28, -3, 0 long: 더 큰 크기의 정수 float: 부동소수점을 갖는 실수 (예) 3.14, 0.0, -28.56 double: 부동소수점을 포함한 더 큰 실수
-
형식지정자
- printf함수에서, 각 데이터타입을 위한 형식 지정자를 사용할 수 있다.
%c : char %f : float, double %i : int %li : long %s : string
-
기타
연산자
및주석
+: 더하기 -: 빼기 *: 곱하기 /: 나누기 %: 나머지 예) n % 3 == 0의 의미는, n을 3으로 나눈 나머지는 0. &&: 그리고 ||: 또는 //: 주석
- 나이를 일수로 계산하기
// 사용자가 입력한 나이를 일수(days)로 계산하기
#include <cs50.h>
#include <stdio.h>
// get_int는 cs50라이브러리에 있는 함수. 정수 값을 받아오는 기능.
int main(void)
{
int age = get_int("what's your age?\n");
int days = age*365;
printf("Your are at least %i days old.\n", days);
}
- 위 예제에서 days 변수를 지정하는 행을 줄이면
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int age = get_int("what's your age?\n");
printf("Your are at least %i days old.\n", age*365);
}
- 짝수인지 홀수인지 알려주는 프로그램
// 짝수인지 홀수인지 알려주는 프로그램
// get_int함수를 cs50.h에서 참고한다.
#include <cs50.h>
#include <stdio.h>
// 사용자로부터 n값을 받아, 2로 나눈 값이 0인지 아닌지에 따라 판별한다.
// 나머지가 0이면 even, 아니면 odd를 출력한다.
// 정수를 2로 나눈 나머지는 0 아니면 1이니까, 추가 조건을 달 필요가 없다.
int main(void)
{
int n = get_int("n:");
if(n % 2 == 0)
{
printf("even\n");
}
else
{
printf("odd\n");
}
}
// 부가세 계산하기
# include <cs50.h>
# include <stdio.h>
int main(void)
{
float price = get_float("What is the price?\n");
printf("Your total is %f\n", price*1.0625);
}
// 터미널에 입력하는 값
$ ./float
// 아래 물음이 출력된다
What is the price?
// 다음을 입력한다(예시)
100
// 프로그램 실행 결과, 아래 값이 출력된다
Your total is 106.250000
- 만약, 원하는 소수점 자리까지만 값을 출력하고 싶으면
# include <cs50.h> # include <stdio.h> int main(void) { float price = get_float("What is the price?\n"); printf("Your total is %.2f\n", price*1.0625); }
%f
에서 f앞에.원하는 자리수
를 입력한다.- 위 예제와 같이 결과를 출력해보면,
106.25
가 도출된다.
// 주석이다 주석주석
- 이 코드가 무슨 기능을 하는지 설명하기 위해서 단다.
- 자기가 짠 코드도 몇 달 혹은 며칠 후에는 새로워보일 수 있다.
- 주석으로 기능(이 코드, 이 부분이 어떤 일을 하는지)을 잘 설명하는 습관이 중요하다.
//for문을 활용하여 cough를 세 번 반복한다
#include <studio.h>
int main(void)
{
for(int i = 0; i < 3; i++)
{
printf("cough\n");
}
}
// 직접 함수를 정의해서 cough를 세 번 반복한다
#include <stdio.h>
// void 원하는함수명(void)
// 이를 통해 직접 함수를 정의할 수 있다
void cough(void)
{
printf("cought\n");
}
int main(void)
{
for(int i = 0; i < 3; i++)
{
cough();
}
}
- 함수를 여러 개 만들 수록 main함수는 아래로 내려가서 보기 어려워진다.
- 중요한 것을 바로 찾을 수 있는 것이 보기 좋다.
- 하지만, C는 위에서부터 아래로 프로그래밍을 실행하기 때문에, main을 위로/사용자정의함수(cough)를 아래로 내리면 오류가 발생한다.
- 이를 해결하기 위해, 이전에 봤던 것처럼 컴퓨터를 속이는 방법이 있다.
// cough 정의보다 main함수를 더 위에 올려주어 좀 더 '보기 좋게'만든 예제 #include <stdio.h> // 이 뒤에 cough라는 함수가 정의되어있다는 것을 알려주는 신호라고 볼 수 있다 // cough함수가 정의되지 않았더라도, 사용에 앞서서 이름을 먼저 제시했기 때문에 main함수에 cough가 나오더라도 계속 코드를 읽게 된다 void cough(void); int main(void) { for (int i = 0; i < 3; i++) { cough(); } } void cough(void) { printf("cough\n"); }
// 원하는 횟수만큼 cough를 출력하는 프로그램
#include <stdio.h>
void cough(int n);
// 3이라는 값을 cough함수에 전달한다
int main(void)
{
cough(3);
}
// cough라는 함수의 기능과 명칭을 직접 정의한다
// 위 main함수를 통해 3이라는 값을 전달받는다
void cough(int n)
{
for (int i = 0; i < n; i++)
{
printf("cough\n");
}
}
cough()
안의int n
은 함수가 입력값을 받아서 int형식을 갖는 n이란 변수에 저장한다는 의미다.
// get_int 함수를 cs50.h에서 참조한다
#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 n;
do
{
n = get_int("Positive Integer: ");
}
while (n < 1);
return n;
}
- 위 예제의 맨 아랫단에서
int get_positive_int(void){}
라고 함수를 정의할 때- 함수명
get_positive_int
의 왼쪽에 있는int
부분은출력 종류
를 의미한다.- 위 예제에서는 양의 정수를 출력하기 위해서
int
라고 지정했다.
- 위 예제에서는 양의 정수를 출력하기 위해서
- 함수명 뒤쪽
()
안에 있는void
는입력의 종류
를 의미한다.입출력이 없을 때
는void
를 써준다.
- 함수 안에 있는
int n;
은 아직 어떤 값을 저장할지 모르는 n이라는 변수를 컴퓨터에게 달라고 하는 신호다. 나중에 n에 제대로된 값을 할당하면 된다. 일단 n이라고 써놓는 것이다.
- 함수명
do-while
부분은중첩루프
이다.- while을 단독 사용하면, 조건이 참일 때만 기능이 작동하지만, 위 예제에서는
do-while
이 쓰였기 때문에do
에서 무조건 한 번은 먼저 수행한다.
- while을 단독 사용하면, 조건이 참일 때만 기능이 작동하지만, 위 예제에서는
위 예제(링크)에서
do-while
문
// 화면에 가로 n개, 세로 n개의 #을 출력하는 프로그램
#include <cs50.h>
#include <stdio.h>
// for루프를 중첩 사용한다
// 첫 루프는 변수 i 기준으로 n번, 내부 루프는 변수 j 기준으로 n번 반복한다
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");
}
}
컴퓨터에 작성한 프로그램은 RAM(랜덤 엑세스 메모리)이라는 물리 저장장치에 저장되는데, RAM은 크기가 유한하기 때문에 부정확한 결과를 도출하기도 한다.
// 실수 x,y를 argument로 받아 x/y를 하는 프로그램
#include <cs50.h>
#include <stdio.h>
// 나눈 결과를 소수점 50째 자리까지 출력하기로 한다
int main(void)
{
// 사용자에게 x 값 받기
float x = get_float("x: ");
// 사용자에게 y 값 받기
float y = get_float("y: ");
// 나눗셈 후 출력
printf("x / y = %.50f\n", x / y);
}
- 위 프로그램 결과, 정확하게는
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
로 저장하기 때문에, int타입 데이터가 저장할 수 있는 값을 넘어가면 에러가 발생한다.- 이때 등장하는 에러 명칭이
overflow
- 이때 등장하는 에러 명칭이
-
문자열
- 에러가 났다고 해서 내가 최악의 프로그래머인 것은 아니다. 자책하지 말기! 컴퓨터가 길을 잃고 헤메느라 그렇다.
- 에러가 났다면, 첫 에러 메시지에 집중하기! 아래로 달리는 나머지 메세지들은 거기(첫 에러)에서 나온 허상이다.
-
자료형, 형식지정자, 연산자
- 극단적으로 줄여버린 코드는 가독성이 좋지 않을 수 있다. 사람마다 기준이 다를 수는 있지만, 읽기 편하고 이해하기 쉬운 코드가 더 선호된다!
-
사용자정의함수, 중첩루프
- 누군가는 cough함수를 어떻게 정의했는지 궁금해 할 수 있지만 적어도 여러분은 알 필요가 없습니다. 그냥 누군가가 구현해 준 기능을 그대로 활용해서 여러분에게 더 흥미로운 프로그램을 만들면 됩니다.
- 다루고자 하는 데이터 값의 범위를 유의하며 프로그램 작성하는 것이 좋다.
- 1999년에서 2000년으로 넘어갈 때, 두 자리로 연도를 저장하던 관습 탓에 2000년인 00이 1900년으로 인식되는 문제 발생. 해결하는 데 많은 비용 들었다고 함.
- 부스트코스 질문: Y2K와 보잉787과 같은 문제를 방지하기 위해서는 프로그램을 어떻게 설계해야 할까요?
- 내 답변: hw 방면으로는 더 많은 정보를 저장할 수 있도록 개발하는 것.
sw 측면에서는 오버플로우 발생 전에 정기적으로 변수를 0으로 리셋해주는 프로그램을 설계하는 것.
(정기적으로 알아서 변수가 0이 되면, 데이터를 더 효율적으로 쓸 수 있을 것 같다)
- 내 답변: hw 방면으로는 더 많은 정보를 저장할 수 있도록 개발하는 것.