비전공자 개발자의 컴퓨터공학 찍먹담<C>

HAN BAEK
9 min readJan 16, 2022

나의 본격적인 첫 개발은 React와 Typescript로 시작되었다. 그러던 중 나는 React와 Typescript를 모르는게 아닌 Javascript를 모른다는 걸 깨닫게되었고 ‘두두닷’ 팀원들과 개발스터디의 오브젝트로 Javascript를 선택했다.

Javascript의 엔진을 까다보니 문득 C언어가 궁금해졌다. 순간 황당했다. 내가 프로그래밍 언어를 처음 접했을 때도 C언어만큼은 피하자 쪽이었는데 그런 내가 C언어를 궁금해하다니.. 참 사람 일은 아무도 모른다는 걸 다시금 깨닫게 되었다.

C언어를 깊게 파고드는 건 현실적으로 비용이 많이든다. 난 회사 일에 우리 스타트업팀 일까지 해야하니 깊게 들어갈 수는 없었다. (마침 팀에서 3개월간의 역량강화 시간이 주어져 가능했다.)

이번 기회에 컴퓨터공학 지식을 쌓을 겸 C언어를 찍먹을 해보기로 마음 먹었다.

Hello World!

C는 통합 개발 환경으로 주로 Visual Studio를 사용한다고 한다.

여기서 통합 개발 환경이란 프로그램 개발에 사용되는 코딩, 디버깅, 컴파일 등의 모든 걸 통합적으로 관리해주는 소프트웨어를 말한다.

시작은 모든 프로그래밍 언어의 시작점인 ‘Hello World!’를 찍어보기로 했다.

main.c

참고로 파일명 뒤에 확장자 명으로 .c의 이름으로 파일을 생성해줘야한다.

다른 언어들이 C언어로부터 파생되었다는 말을 들은 적이 있다.

그 때문인지 나름 ‘Hello World’찍어 본 언어가 다양한데 그 중 제일 복잡한 언어가 C언어 였다. 주의 깊게 봐야하는 부분이 3가지 있다.

1. `#include <stdio.h>`는 C의 라이브러리 중 하나인 stdio.h라는 헤더 파일에 선언된 내용을 포함한다는 뜻이다. `#include`를 통해 다양한 라이브러리를 불러올 수 있으며, `stdio.h`는 standard input output의 약자로 표준 입출력을 의미한다.

여기에는 여러 기본적 기능이 있는데 대표적인 예로 `printf`(어떤 내용을 사용자에게 출력)가 있다.

2. `system(“pause”)`은 콘솔 창 닫힘 방지 함수이고 여기서 `system()`은 함수를 이용해 운영체제의 기본적인 기능을 이용할 수 있다. `pause`는 명령 프롬프트에서 키보드를 입력하기 전까지 대기하는 기능을 수행한다.

변수와 상수

모든 프로그래밍 언어에서 나오는 개념이다.

변수(Variable) = 변할 수 있는 데이터
상수(Constant) = 변하지 않는 데이터

변수를 선언할 때 자료형과 변수명을 입력하는 것도 거의 비슷한데 C에서 가장 많이 사용되는 변수는 정수형(integer)변수이다.

초기화 되지 않은 변수에는 쓰레기 값이 들어간다.

예약어와 식별자

식별자(identifier)란 변수나 함수 등의 고유한 이름을 지정할 때 사용된다. C언어 문법으로 정해진 예약어는 식별자로 사용할 수 없다는 것도 다른 언어와 비슷한데 기본적으로 C언어의 대표적인 예약어는 string, for, void, bool, if, while, char, return, double 등이 있다.

기본 입출력

언어마다 입출력의 방식이 다른데 c언어는 꽤나 까다롭다. 내가 C언어 알레르기가 있는 이유도 바로 이 때문이다.

일단 C언어에서는 특정한 변수에 값을 넣기 위해서 `scanf()`함수를 사용한다.

이 함수는 취약한 함수로 분류되어 그런 함수를 거르는 Visual Studio 사용 시 취약해도 그냥 사용하겠다라는 의미인`#define_CRT_SECURE_NO_WARNINGS`를 정의해주어야 한다.

`&`의 의미는 특정한 변수의 주소를 의미한다. 쉽게말해 입력 받을 주소를 나타내기 위해 사용한다. 실제로 컴퓨터는 특정한 메로리 주소에 접근하여 데이터를 수정하므로 &를 이용한다. 그때 사용하는 것이 형식 지정자이다.

여기서 double형만 유독 입력과 출력이 다른데 그 이유는 입력을 받을 때는 특정 주소에 특정 크기만큼 입력을 수행하고 출력할 때는 주소가 아닌 값 자체를 이용해 출력하므로 구체적인 크기를 지정하지 않기 때문이다.

이해하기 쉽게 예제를 보자

실수형으로 입력 받아서 소수점 셋째 자리까지 출력하는 예제이다.

% 자체를 문자로 출력하고 싶으면 ‘%%’를 입력하여 출력할 수 있다.

C언어에서 특정한 표현을 출력하기 위해 사용하는 **이스케이프 시퀀스**를 정리해봤다.
1. `\n` : 줄바꾸기
2. `\t` : 수평 탭 넣기
3. `\\` : 백슬래시 넣기
4. `\”` : 큰 따옴표 넣기
5. `\b` : 백 스페이스 넣기

함수

C언어의 함수는 다음과 같이 구성된다.

여기서 매개변수와 return값은 경우에 따라 없어도 상관은 없다. 이 경우에 자료형은 void가 된다.

함수도 사실 크게 다른 건 없다. 그저 폼을 보여주기 위해서 집어 넣었다.

참고로 C언어는 함수로 시작해서 함수로 끝나는 언어이다.

문자열과 배열

기본적으로 C언어는 문자열 자료형을 제공하지 않는다. 따라서 C언어에서 문자를 여러 개 묶어 놓는 형태로 문자열을 표현한다.

추후에 이 문제를 해결하고자 C++에서는 string 자료형을 제공한다.

C언어에서 하나의 문자는 1바이트만을 담는다고 한다.

문자열을 입력 받을 때 및 출력할 때는 `%s`라는 형식 지정자를 사용해야 한다.

형식지정자는 너무 외우기 복잡하나 문자열은 중요하니 잘 외워둬야겠다.

동적 메모리 할당

C언어에서는 배열의 경우 사전에 적절한 크기만큼 할당해주어야 한다. 이 부분은 굉장히 신기했다.

C언어에서는 `malloc()`함수를 이용해 원하는 만큼의 메모리 공간을 확보할 수 있었다.

이 함수는 메모리 할당에 성공하면 주소를 반환하고 그렇지 않으면 NULL을 반환한다.
(참고로 `stido.h` 라이브러리에 정의되어 있다.)

이렇게 동적으로 할당된 변수는 ’힙 영역’에 저장된다.

전통적인 C언어에서는 스택에 선언된 변수는 따로 메모리 해제를 해주지 않아도 되나 동적으로 할당된 변수는 반드시 `free()`함수로 메모리 해제를 해주어야 한다.

왜냐하면 메모리 해제를 하지 않으면 메모리 내의 프로세스 무게가 더해져 메모리 누수가 발생하기 때문이다.

할당한 메모리 해제 뒤에 다시 할당하면 동일한 메모리 주소를 할당 받을 확률이 크다.

함수 포인터

개인적으로 C언어의 가장 큰 특징이라고 생각한다. 자바스크립트를 공부하다가 C언어의 흥미가 생긴 이유도 바로 이 ‘포인터’개념 때문이었다.

포인터에 대해 알아보자면 C언어는 함수의 이름을 이용해 특정한 함수를 호출하는데 함수 이름은 메모리 주소를 반환한다.

이 때 함수 포인터는 특정한 함수의 반환 자료형을 지정하는 방식으로 선언이 가능하다. 또한 함수 포인터를 이용하면 형태가 같은 서로 다른 기능의 함수를 선택적으로 사용할 수 있다.

위의 예제를 보면 알 수 있듯이 포인터를 사용해 함수를 선택적으로 사용 가능하다.

실질적으로 함수 포인터는 자주 사용되지 않는다고 한다.

구조체

C언어에는 구조체라는 개념이 있다. 여러 개의 변수를 묶어 하나의 객체를 표현할 때 사용하는 방식으로 객체를 정의하는 목적으로 많이 사용된다.

구조체는 이렇게 자료형과 변수명으로 정보를 저장한다. 예를 들어 구조체명에 음식을 넣고 그 음식의 대한 정보를 담을 수 있다.

파일 입출력

C언어에는 특이하게 파일 입출력 변수가 존재했다. 말 그대로 파일을 열고 닫는 기능이다. 예제를 보는게 가장 빠를 거 같아 예제를 가져왔다.

먼저 파일을 열고닫는 함수이다.

  • 파일 입출력 변수는 `FILE`형식의 포인터 변수로 선언한다.
  • 파일을 열 때는 `fopen()`함수를 닫을 때는 `fclose()`함수를 이용한다.
  • 파일 열기 함수인 `fopen()` 함수에는 파일 경로와 접근 방식을 설정할 수 있으며, 기본 경로는 현재 프로그램의 경로이다.

다음은 파일 입출력 함수이다.

기본적인 입출력을 위해서 `printf()`와 `scanf()`함수를 사용하곤 했는데 파일 입출력에서는 `fprintf()`와 `fscanf()`가 사용된다.

파일 입출력은 열고, 읽고/스고, 닫기의 과정을 철저히 따라야한다.

파일을 열 때는 파일 포인터가 사용되며, 이는 동적으로 할당된 것이다.
파일 입출력이 끝난 이후에는 반드시 파일 객체를 메모리에서 할당해제해주어야 한다.(메모리 누수 방지)

파일 입출력을 이용해 데이터를 파일로부터 가져오거나 데이터를 파일로 내보낼 수 있다.

이렇게 C언어 찍먹이 끝이났다. 내용이 부실할 수도 있으나 언어 하나를 다 공부하려면 너무 양이 방대하고 나에겐 C의 내용을 전부 짚고 넘어갈 시간이 없기에 처음부터 ‘찍먹’이라는 컨셉으로 글을 작성했다.

이 외에도 다른 부분도 있으나 C의 특성만을 담은 글이다보니 연산자, 조건문, 반복문은 다른 언어와 크게 다르지않아 생략하였다.

모든 프로그래밍 언어는 비슷한 부분이 많다고 생각한다. 거기서 조금씩 생겨나고 사라졌을뿐 근본은 비슷하다고 생각한다.

프로그래밍 언어 하나를 정복하면 다른 언어를 배울 때 들어가는 시간적 비용이 적듯이 자바스크립트 엔진을 까고 있는 도중에 C언어를 보니 나름 이해가 금방 되어서 놀라웠다.

평소에 C언어에 울렁증이 있는 분들이 있다면 꼭 한 번쯤은 ‘찍먹’을 해보시길 바란다. 생각보다 이해가 잘되어 놀랄 수도 있다. 아마 감사함도 느낄 것이다.

매 번 느끼지만 이렇게 복잡했던 프로그래밍 언어를 보다 사용하기 쉽게 만들어 준 선배님들에게 경의를 표하며 다시 한 번 감사의 인사를 드리고싶다.

많은 사람들이 겪는 문제를 해결하기 위해 자신의 인생을 바치는 사람들이 있다. 나도 그런 사람이 되고싶다.

--

--