C기초
c 어는 아주 오래되고 전통적인 순수 텍스트 기반의 언어이다.
int main(void)는 우리가 코드를 실행할 수 있도록 시작한다는 의미를 가지고 있다.
C에는 printf라는 함수가 있다.
글자나 단어, 문자을 적을 때는 언제나 텍스트에 " " 쌍 따옴표로 감싸야 한다.
그리고 우리가 일상에서 문장의 끝에 붙이듯 C에서는 세미콜론( ; ) 을 붙여야 한다.
(참고로 \n은 줄바꿈의 기능을 한다.)
우리가 문서를 저장하는 것처럼 , 문서.확장자명 으로 저장되는 것처럼.
C로 작성된 코드에도 파일명.c로 저장해야 한다.
터미널 창의 명령어 프롬프트에서 $(달러사인) 기호 옆에 우리가 원하는 명령어를 입력하면 된다.
clang hello.c 라는 명령어는 "clang" 이라는 컴파일러로 "hello.c"라는 코드를 컴파일하라는 의미이다.
그 결과로 a.out이라는 파일이 만들어 진다.
./a.out 이라는 명령어를 실행하면 현 디렉토리에 있는 a.out을 실행하게 해준다.
문자열
사용자에게서 입력값, 즉 문자열을 받아내려면 어떻게 할 수 있을까?
get_string 함수를 사용할 수 있다.
(string이란 단어나, 구절, 문장을 부르는 말이다.)
string answer = get_string("what's your name?\n");
사용자의 이름을 받아서 저장할 변수를 answer라고 정해보고,
원하다면 마음대로 정해도 된다.
다만 여기서 유의할 점은 C는 오래된 언어이기 때문에 변수가 저장하는 데이터의 종류를 아주 정확하게 명시해줘야 한다.
그래서 우리가 저장할 값의 종류가 문자열(string)이라는 것을 알려줘야한다.
이때의 string을 형식지정자라고 한다.
그리고 우리가 일반적으로 같다라고 사용하는 ( = ) 은 사실 할당 연산자로
오른쪽에 있는 것을 왼쪽에 할당하는 역할을 한다.
string answer = get_string("What's your name?\n");
printf("hello, %s\n", answer);
우리는 answer라는 변수에 들어있는 이름을 출력하기 위해 %를 사용해 준다.
그리고 어떤 종류의 인자를 받는지 알려줘야 하기 때문에 string의 s를 뒤에 붙여서 인자를 받아준다.
그리고는 터미널 창에 아래 명령어를 입력하여 컴파일을 할 수 있다.
$ clang _o string string.c_lcs50
여기서 _o string은 string.c를 string.out 이라는 머신 코드로 저장하는 명령어 이다.
-lcs50은 "link"라는 의미를 지닌 -l이라는 인자에 우리가 추가로 포함한 cs50파일을 합친 것이다.
이를 통해 컴파일시 cs50파일을 연결하도록 알려줄 수 있다.
다소 복잡한 이런 과정 대신 make 명령어를 통해 간단하게 컴파일 할 수 있다.
$ make string
조건문과 루프
int counter = 0;
위에서 배웠던 것처럼 C는 오래된 언어이기 때문에 저장하고자 하는 변수의 종류를 꼭 알려줘야 한다.
우리는 counter 라는 변수에 숫자를 저장해보려고 한다.
여기서 int 는 변수가 정수(integer)라는 것을 알려주는 것이고,
counter는 변수의 이름, 0은 그 값에 0을 저장(초기화)하는 것이다.
counter = counter + 1;
counter += 1;
counter ++;
그렇다면 위 코드는 어떤 의미일까?
즉, counter에 1을 더한 값을 다시 counter에 저장(할당)한다는 의미가 된다.
그 아래 두 방식처럼 수행할 수도 있다.
마찬가지로 조건문도 C코드로 나타낼 수 있다.
if (x < y)
{
printf("x is less than y\n");
}
if ( ) 의 괄호 안에는 검사하고자 하는 조건이 들어가고,
{ } 안에는 조건을 만족할 때 수행하고자 하는 작업이 들어간다.
여기에서는 조건이 True면 "x is less than y"를 출력을 하라는 것이다.
else를 이용해 처음 조건이 아닌 경우에는 어떤 것을 하라라고 적어줄 수 있다.
if (x < y)
{
printf("x is less than y\n");
}
else
{
printf("x is not less than y\n");
}
이 경우에는 첫 번째 x < y 조건이 False,
즉 x가 y보다 작지 않을 경우에는 "x is not less than y"를 출력하라는 것이다.
else if 를 통해서 아래와 같이 조건을 추가할 수도 있다.
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");
}
== 는 =와는 다르게 일치 연산자라고 한다.
같은지 묻는 연산자라는 뜻이다.
하지만 위의 코드는 논리자체에 이상은 없지만 더 간결하게 만들 수 있다.
if (x < y)
{
printf("x is less than y\n");
}
else if (x > y)
{
printf("x is not less than y\n");
}
else
{
printf("x is equal to y\n");
}
C에서도 while 이나 for 을 통해서 루프를 구현할 수 있다.
while (true)
{
printf("hello, world\n");
}
먼저 while 의 경우 아래 코드와 같이 while ( )의 괄호 안에 조건을 넣고 { } 안에 수행할 작업을 포함시키면 된다.
즉, C에서 루프를 구현하고 싶다면 성립 조건을 정해줘야 한다.
답이 네, 참, 혹은 1로 나올 수 있는 질문을 던져줘야 하는 것이다.
답이 참으로 나오게 하는 방법은 여러가지가 있을 수 있다.
5=5, 1<2 등등 하지만 가장 간단한 방법은 그냥 true를 적는 것이다.
아래 코드에서는 true라는 항상 참이 되는 조건을 통해 while 루프가 영원히 수행되도록 한다.
따라서 위의 코드는 계속해서 "hello, world"를 무한정 출력하게 된다.
특정 횟수만큼 작업을 수행하도록 하려면 어떻게 해야할까?
int i = 0;
while (i<50)
{
printf("hello, world\n");
i = i+1;
}
처음에 i를 0이라고 정해주었고 while는 계속해서 i가 50보다 작은지를 물어볼 것이다.
따라서 이 코드가 정상적으로 작동하려면 i를 증가시켜야 한다.
(i = i + 1, i += 1, i++ 모두 같은 결과를 낸다.)
진행 순서를 정리해보자면 아래와 같다.
i는 0으로 설정 -> i는 50보다 작은가? -> 작다 -> hello world를 출력한다 -> i를 1증가시킨다 -> i가 50보다 작은가?
-> (반복) -> i가 50보다 작은가? -> 작지 않다 -> 종료
for (int i = 0; i<50; i = i + 1)
{
printf("hello, world\n");
}
따로 변수를 선언해도 되지만 아래와 같이 for 를 사용하면 for ( ) 안에 각각 (변수 초기화; 변수 조건; 변수 증가) 에 해당하는 코드를 넣어서 간단하게 표현할 수 있다.
즉, 가장 먼저 정수 값을 가지는 i라는 변수를 0으로 초기화하고, i가 50인지 매번 검사를 하고, 이를 만족하면 { } 안의 내용을 수행한 후에, i를 1씩 증가시킨다는 의미다.
자료형, 형식 지정자, 연산자
데이터 타입
- bool: 불리언 표현, (예) True, False, 1, 0, yes, no
- char: 문자 하나 (예) 'a', 'Z', '?'
- string: 문자열
- int: 특정 크기 또는 특정 비트까지의 정수 (예) 5, 28, -3, 0
* int는 대략 40억까지 셀 수 있기 때문에 40억게 이상의 데이터를 가진 일부 거대 기업과 같은 상황이 아닌 일반 사용자들은 대부분 정수에 int를 사용한다
- long: 더 큰 크기의 정수
- float: 부동소수점을 갖는 실수 (예) 3.14, 0.0, -28.56
- double: 부동소수점을 포함한 더 큰 실수
CS50 라이브러리 내의 get 함수
CS50 라이브러리는 위와 같은 데이터 타입을 입력값으로 받을 수 있는 아래와 같은 함수들을 포함한다.
- 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> 는 무엇인가?
CS50 수업을 위해 만들어진 라이브러리 . 라이브러리는 여러 함수들을 모아둔 것이라고 볼 수 있다.
반면에 C에는 표준 라이브러리도 있다. 우리가 처음부터 사용한 #inclue<stdio.h>가 표준 라이브러리 중 하나다.
가장 많이 쓰고 가장 보편적으로 사용하는 라이브러리입니다.
그 밖에도 <math.h>, <time.h> 등 자신이 코딩하는데 필요한 함수들을 그때 그때 라이브러리를 불러와서 다른 사람들이 만들어둔 함수를 사용할 수 있다.
그렇다면 sandbox.cs50.io가 아닌 Visual Studio 같은 곳에서 CS50 라이브러리를 바로 사용이 가능할까?
답은 "사용할 수 없다"이다.
표준 라이브러리는 기본적으로 설치가 되어 있기 때문에 사용이 가능하지만 앞서 말했듯이 CS50 라이브러리는 수업을 위해 만들어진 라이브러리이기 때문이다.
그래서 sandbox.cs50.io 처럼 미리 설치가 된 곳이 아니면 따로 설치를 하여야만 사용이 가능하다.
사용자 정의 함수
#include <stdio.h>
void 함수명(형식자 변수);
형식자 main(void)
{
함수명(변수);
}
void 함수명(형식자 변수)
{
함수내용;
}
이렇게 코드를 표현하면 마치 이전에 함수명을 봤던 것처럼 C를 속여서 메인 함수에서 사용할 수 있게 된다.
항상 코드는 위에서 아래로, 왼쪽에서 오른쪽으로 읽기 때문이다.
하드웨어의 한계
#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자리까지 출력하기로 하고, x에 1을, y에 10을 입력하면 아래와 같은 결과가 나온다.
x: 1
y: 10
x / y = 0.10000000149011611938476562500000000000000000000000
정확한 결과는 0.1이 되어야 하지만. float에서 저장 가능한 비트 수가 유한하기 때문에 부정확한 결과를 내게 된다.
마찬가지로
정수를 계속 키우는 프로그램에서 10억을 넘기자 앞으로 넘어갈 1의 자리가 없어지게 된다..
int에서는 32개의 비트가 다였기 때문이다.
그 이상의 숫자는 저장할 수 없는 것이다.
이것을 우리는 오버플로우라고 한다.
'🖥️Computer Science > CS50' 카테고리의 다른 글
CS50_자료구조 (0) | 2023.06.13 |
---|---|
CS50_메모리 (0) | 2023.06.13 |
CS50_알고리즘 (0) | 2023.06.13 |
CS50_배열 (0) | 2023.06.13 |
CS50_컴퓨팅 사고 (0) | 2023.06.06 |