On a couch

4. 프로그래밍 응용 (1) 본문

프론트엔드 공부/CS50

4. 프로그래밍 응용 (1)

couch 2022. 4. 19. 16:26

01 컴파일

컴파일 과정(Compile)

  • 컴파일은 소스 코드를 오브젝트 코드로 변환시키는 과정입니다. 여기서 소스 코드는 여러분이 C언어와 같은 프로그래밍 언어로 작성한 코드이고, 오브젝트 코드는 기계 코드라고도 알려져 있는데, 0과 1로 이루어져 있으며 컴퓨터에게 프로그램이 어떻게 실행되어야 하는지 알려주는 코드입니다. make 명령어 자체는 컴파일러가 아니고, clang이라는 컴파일러를 호출해서 C 소스 코드를 오브젝트 코드로 컴파일 하도록 합니다.

 

전처리(Precompile)

  • # 으로 시작되는 C 소스 코드는 전처리기에게 실질적인 컴파일이 이루어지기 전에 무언가를 실행하라고 알려줍니다.
  • 예를 들어, #include는 전처리기에게 다른 파일의 내용을 포함시키라고 알려줍니다. stdio.h 파일의 내용이 #include 부분에 포함됩니다.

 

컴파일(Compile)

  • 다음 단계는 컴파일입니다. 컴파일러라고 불리는 프로그램은 C 코드를 어셈블리어라는 저수준 프로그래밍 언어로 컴파일합니다.
  • 컴파일이라는 용어는 소스 코드에서 오브젝트 코드로 변환하는 전체 과정을 통틀어 일컫기도 하지만, 구체적으로 전처리한 소스 코드를 어셈블리 코드로 변환시키는 단계를 말하기도 합니다.

 

어셈블(Assemble)

소스 코드가 어셈블리 코드로 변환되면, 다음 단계인 어셈블 단계로 어셈블리 코드를 오브젝트 코드로 변환시키는 것입니다.

 

링크(Link)

만약 프로그램이 (math.h나 cs50.h와 같은 라이브러리를 포함해) 여러 개의 파일로 이루어져 있어 하나의 오브젝트 파일로 합쳐져야 한다면 링크라는 컴파일의 마지막 단계가 필요합니다. 링커는 여러 개의 다른 오브젝트 코드 파일을 실행 가능한 하나의 오브젝트 코드 파일로 합쳐줍니다.

 

02 버그와 디버깅

버그와 디버깅

버그(bug)는 코드에 들어있는 오류입니다. 버그로 인해 프로그램의 실행에 실패하거나 프로그래머가 원하는 대로 동작하지 않게 됩니다. 디버깅(debugging) 코드에 있는 버그를 식별하고 고치는 과정입니다. 프로그래머는 디버거라고 불리는 프로그램을 사용하여 디버깅을 하게 됩니다.

 

디버깅의 기본

프로그램은 일반적으로 인간보다 훨씬 빠르게 연산을 수행합니다. 그래서 프로그램을 실행시켜보는 것만으로는 무엇이 잘못됐는지 찾아내기 어렵습니다. 디버거는 프로그램을 특정 행에서 멈출 수 있게 해주기 때문에 버그를 찾는데 도움이 됩니다. 프로그램이 멈추는 특정 지점 중지점이라고 합니다. 또한 프로그래머가 프로그램을 한번에 한 행씩 실행할 수 있게 해줍니다. 이로써 프로그래머는 프로그램이 내리는 모든 결정들을 단계별로 따라갈 수 있게 됩니다.

 

GDB사용해보기

+ 숫자형 오류로 심각한 문제의 예 https://www.edwith.org/cs50/lecture/22837/?isDesc=false

 

03 형변환

형변환

C에는 int형, float형, char형을 포함한 여러 개의 자료형이 있습니다. 변수를 다른 자료형으로 변환시켜줘야 할 때, C는 형변환(Typecasting)을 통해 이것을 해결합니다. 형변환할 때 정밀도(표현 범위)가 더 높은 자료형으로 바꿀 경우 값에 오차가 발생할 수 있다는 점을 유의해야 합니다.

 

char형과 int형 사이의 형변환

ASCII 표준은 각 문자에 그 문자를 식별할 수 있는 고유 숫자를 부여했습니다. 대문자 A는 65, 대문자 B는 66과 같이 숫자가 할당되어 있습니다. 그래서 형변환을 쓰면 char형과 int형을 서로 변환할 수 있습니다.

#include <stdio.h>

int main() 
{
	int x = 65;
	printf("%d\n", x); //65출력
	printf("%c\n", (char)x); //A 출력	
}
  • 7행에는 int형이 아닌 char형을 위한 형식문자 %c가 있습니다. 6행과 마찬가지로 x의 값을 출력하지만 변수 이름 앞에 (char) 이 작성되어 있어 char형으로 변환됩니다. 기존 변수 앞에 새로운 자료형을 넣어 바꾸는 것을 명시적 형변환이라고 합니다. 우리가 직접 자료형을 변환하라는 지시를 내리는 것이죠. 사람들이 코드를 보고 이해하기 쉽습니다.
  • 사실 7행의 (char)을 빼도 코드는 여전히 ASCII 65값이 나타내는 문자 A를 출력할 것입니다. ASCII 표준에 해당하는 정수를 %c에 전달하면 컴파일러는 그 값을 자동으로 문자로 해석하기 때문입니다. 이것을 암묵적 형변환이라고 합니다.

 

int형과 float형 사이의 형변환

#include <stdio.h>

int main() 
{
	int a = 28;
	float b = a / 5; //-> 5.0
	float c = (float)a / 5; //-> 5.6
	
	float d = 28.523;
	int e = d; //-> 28
}
  • 형변환은 부동 소수점 수와 정수의 변환에 유용합니다. 6행에서 28에 5를 나눈 값인 5.6을 b에 저장해야 하는데, 실제로는 b의 값이 5.0이 됩니다. 이것은 컴파일러가 정수 두 개를 나누었기 때문에 그 결과도 정수로 나온 것입니다. 이 문제를 해결하기 위해 7행과 같이 a를 float형으로 명시적 형변환하면 그 결과값으로 c는 5.6이 됩니다.
  • 암묵적 형변환도 유용하게 쓰입니다. 정수는 소수점 뒤의 숫자들에 대한 정보를 저장할 수 없기 때문에 float형인 값을 int형으로 변환하면 소수점 뒤를 버린 값을 쉽게 저장할 수 있습니다. 10행을 보면 int형인 변수에 부동 소수점 값을 넣으려고 했을 때, d는 묵시적으로 int형으로 형변환되며, 이 과정에서 소수점 뒤의 값을 버리게 됩니다.

04 함수와 리턴

함수

특정 목적을 위해 만들어진 재사용 가능한 코드입니다. 함수는 입력값과 출력값을 가지며 프로그램 내의 어디에서든 재사용될 수 있습니다. 프로그램을 여러 개의 함수로 만들면 코드를 조직화하고 간단하게 만들 수 있습니다. 이것이 추상화의 한 예입니다. 함수를 한번 작성하면 그 함수가 구체적으로 어떻게 만들어졌는지 몰라도 함수를 사용할 수 있습니다.

 

함수의 문법

여러분이 C에서 작성하는 모든 프로그램은 main 이라는 함수를 가지고 있습니다. 물론 C의 프로그램은 더 많은 함수를 가질 수 있습니다. 아래 코드는 sayHi라는 함수를 정의한 것입니다.

 

#include 

void sayHi(void)
{
	printf("Hi!\n");
}

int main(void)
{
	sayHi();
	sayHi();
}

(3행)함수의 첫 행은 세 부분으로 나누어져 있습니다. 첫째는 반환 자료형인데, 함수를 호출한 쪽에 출력값으로 반환해줄 값의 자료형입니다. 반환할 값이 없을 경우 void를 씁니다. 둘째는 함수의 이름입니다. 공백이 있으면 안되고, C에서 이미 사용하는 키워드는 쓸 수 없습니다. 셋째는, 괄호 안에 인자라고도 알려진 매개변수(parameter)를 넣어줍니다. 이것은 함수의 입력값이며 입력값이 없으면 void를 씁니다. 이 첫 행 뒤에는 중괄호로 묶인 함수 코드가 작성됩니다.

sayHi 함수는 “Hi\n”를 화면에 출력하는 함수입니다. main 함수 안에서 sayHi 함수가 두 번 호출되고 있습니다. sayHi 함수는 매개변수를 받지 않기 때문에, 함수를 호출할 시에 괄호 안에 인자(argument)를 작성하지 않습니다. 실행 결과로 “Hi\n”가 화면에 두 번 출력됩니다.

 

매개변수와 반환값

함수의 반환값을 return이라는 단어로 명시합니다. return이 쓰여진 행이 보통 함수의 마지막 행입니다. 매번 호출될 때마다 함수가 실행되고, 매개변수에 대한 연산값이 함수를 호출한 곳으로 반환됩니다.

 

범위(Scope)

함수 안에서 정의된 변수나 함수의 매개변수로 전달된 것들은 그 함수 안(지역)에서만 유효합니다. 이러한 변수들을 지역변수라고 부릅니다. 지역변수는 그 함수 안에서만 사용 수 있고, 다른 곳에서는 존재하지 않습니다.

 

** main에서 사용되어야 하는 함수가 있는데 main 보다 아랫줄에 정의하고 싶다면, main 윗줄에 생성만 시켜놓는 방법도 유효.

** C에서는 인수를 적을 때도 자료형 명시해서 정의해야 함

05 배열과 문자열

배열

변수와 마찬가지로, 배열을 선언할 때는 첫째로 배열에 저장되는 자료의 유형을 명시합니다. 그리고 배열의 이름을 지정합니다. 이름 뒤에 오는 대괄호 안에는 배열의 크기가 들어갑니다. 배열의 크기는 배열에 얼마나 많은 값을 넣어둘 수 있는지를 정의합니다. 예를 들어 <코드 1>의 1번 줄은 다섯 개의 정수 배열을 선언합니다.

 

배열을 일련의 연속된 칸으로 생각할 수 있는데, 각 칸에는 값이 있고, 숫자 인덱스가 있습니다. 숫자 인덱스는 배열 안에 들어있는 특정 값에 접근하기 위해 쓰이며, 몇 번째 값인지를 가리킵니다. C에서 배열의 인덱스는 0으로 시작하므로 배열의 첫 번째 항목은 인덱스 0이고, 두 번째 항목은 인덱스 1입니다. 배열의 특정 값에 접근할 때는 배열의 이름과 괄호 안에 들어가는 특정 인덱스를 사용합니다. 

배열 인덱스의 값은 일반 변수와 같이 취급됩니다. 예를 들어, 3-6번 줄 처럼 값을 바꾸고, 연산이나 할당 연산자를 적용할 수 있습니다. 배열의 각 값은 인덱스 숫자로 참조하기 때문에 배열을 반복문으로 돌리기 쉽습니다. 7-10번 줄은 for문인데 배열 전체를 순환하면서 각 나이의 값을 1만큼 올려줍니다.

 

문자열

C에서 문자열은 char 값들의 배열을 나타냅니다. 그러므로 string s = “CS50”; 라고 썼을 때 이 정보는 char들의 배열로 이루어진 것이고, 각 문자당 하나의 인덱스에 저장됩니다. C에서 문자열의 마지막 인덱스는 널로 끝나며, \0으로 나타냅니다. 널 종단(null-terminator)은 문자열에게 문자열이 끝났고 더 이상의 문자가 남아있지 않다고 말하는 문자입니다.

문자열은 하나의 배열이기 때문에, 다른 배열에서 인덱스를 이용해 각 원소에 접근하는 것과 마찬가지로 문자열의 인덱스를 통해 특정 문자 값에 접근할 수 있습니다.

 

 


* 'scss는 전처리기'라는 건 알았는데 '전처리기' 자체가 뭔지는 이제야 알았다 ^^

* 사람들이 프로그래밍 언어에 대해 얘기할 때 C가 '근본'인 것처럼 얘기하는 경우가 많아서 항상 궁금했다. 직접 보고 나니 왜인지 알겠다. JS나 Python과 진짜 비슷한데 온갖 문제들에 대한 더 근본적인 이해와 응용력이 필요하다. 나는 역순으로 보는 게 더 이해하기 쉬웠다고 느꼈고, '하나 배우면 다른 건 쉽게 배운다'는 게 뭔지도 점점 알겠다.

 

'프론트엔드 공부 > CS50' 카테고리의 다른 글

6. 인터넷과 네트워크  (0) 2022.04.21
5. 프로그래밍 응용 (2)  (0) 2022.04.20
3. 프로그래밍 기초 (C)  (0) 2022.04.19
2. 알고리즘  (0) 2022.04.18
1. 컴퓨터와 컴퓨팅  (0) 2022.04.16