C언어 - 프로그래밍 언어의 문법

이미지
1. 언어의 문법 인간이 사용하는 언어에는 매우 다양한 문법 이 존재하며, 이를 익히는 데에는 많은 시간이 필요하다. 명사, 동사, 시제, 어순 등 매우 많은 문법을 익혀야 한다. 프로그래밍 언어는 컴퓨터에게 무엇을 어떻게 할지 지시하기 위한 명확한 목적을 가진 언어이다. 비교적 적은 수의 문법 요소만으로도 의미의 표현과 실행이 가능하다. 사람이 쓰는 언어와 달리 프로그래밍 언어는 몇 가지 핵심 문법 만 이해하면 바로 사용이 가능하다. 2. C언어 문법 프로그래밍 언어는 다음과 같이 다섯 가지로 구성되어 있다. 변수 - 값을 저장한다. 연산 - 값을 계산한다. 문장 - 내용을 실행한다. 제어 - 실행 순서를 변경한다. 함수 - 여러 문장을 하나로 묶는다. 이 다섯 가지만의 문법만 이해해도 프로그램의 기본 구조를 이해할 수 있다. 2.1. 변수 (Variable) 변수는 값(value)을 저장하는 메모리 공간에 이름 을 부여한 것이다. 프로그램은 변수를 통해 데이터를 기억하고, 참조하고, 변경한다. 2.1.1. 변수 타입(Type) 변수 타입은 변수가 어떤 종류의 값을 저장 하며, 그 값을 어떻게 해석할지를 결정하는 규칙이다. 변수 타입에 의하여 저장 가능한 값의 형태, 메모리 사용 방식, 그리고 허용되는 연산의 종류가 결정된다. int count; count = count + 1 ; // int type stores integer values.   char grade = 'A' ; // char type stores a single character or small integer values.   float ratio = 0.75f ; ratio = ratio * 100.0f ; // float type stores floating-point (real) values. 2.1.2. 상수 상수는 값이 바뀌지 않는 변수 이다. C언어에...

C 언어 - "Hello World!"로 시작하기

1. C 컴파일러 설치

Linux 환경에서 C 언어 프로그램을 개발하기 위해 가장 널리 사용하는 컴파일러는 GCC(GNU Compiler Collection)이다. 이를 개별적으로 설치할 수도 있지만 개발에 필요한 필수 도구 모음인 build-essential 패키지를 통해 설치하는 것이 효율적이다.

$ sudo apt update
$ sudo apt install -y build-essential
$ gcc --version
gcc ...
$ make --version
GNU Make ...  

  • GCC (GNU Compiler Collection): 리눅스 환경의 표준 C 컴파일러이다. 
  • build-essential: GCC를 포함하여 g++(C++ 컴파일러), make(빌드 자동화 도구), libc6-dev(표준 라이브러리 및 헤더 파일) 등 개발에 필수적인 소프트웨어들을 한 번에 설치해 주는 패키지이다.

2. 첫 번째 프로그램

1978년에 발행된 교재의 첫 번째 예제로 “Hello, world!” 문장을 출력하는 예제가 소개된 이후 , 이 문장은 거의 모든 프로그래밍 언어 The C Programming Language [1] 를 설명하는 교재와 서적에서 가장 기본적인 첫 예제로 사용되고 있다.

[출처] The C Programming Language

2.1. hello.c

먼저 "hello.c" 라는 파일을 생성하고, “Hello, world!” 문장을 출력하는 예제를 작성한다.

hello.c

#include <stdio.h>
main() {
   printf("Hello, world!\n");
}

"hello.c" 파일을 컴파일한다.

$ gcc hello.c
hello.c:3:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
3 \
 main() \
 ^~~~

위와 같이 경고(warning)가 발생하는 것을 확인할 수 있다. 해당 경고는 main() 함수에 반환 형식을 지정하지 않았기 때문에 발생하는 경고이다. 

※ 이전 K&R 스타일에서는 반환형을 생략하면 int 로 가정하는 규칙이 있었으나, C99 이후 표준에서는 반드시 명시해야 하며 [2], 생략 시 컴파일러가 경고를 출력한다. C 언어에서 모든 함수는 어떤 값을 리턴하는지 지정하여야 한다.

hello.c 코드를 수정한다.


#include <stdio.h>
int main() {
   printf("Hello, world!\n");
   return 0;
}
  

"hello.c" 파일 수정후 다시 컴파일한다.

$ gcc hello.c
$ ./a.out
Hello, world!  

"a.out" 실행파일이 생성되었고, 실행하면 "Hello, world!" 문장이 출력된다.


2.2. Makefile

생성할 파일이름 "hello"를 지정하여 컴파일한다.

$ gcc -o hello hello.c
$ ./hello
Hello, world!  

이렇게 생성할 파일 이름을 gcc 실행 인자로 지정할 수 있다. 생성 파일 이외에서도 다양한 컴파일 옵션을 지정할 수 있고, 또 파일이 수정될 때만 컴파일이 수행되도록 할 수 있는데, 이것은 Makefile을 통하여 이루어진다. Makefile을 사용하면 소스 파일 간의 의존성을 정의하고, 필요한 경우에만 빌드를 수행하도록 자동화할 수 있다.

"Makefile"이라는 이름으로 다음과 같이 스크립트를 작성한다.

Makefile


TARGET = hello
all: $(TARGET)
$(TARGET): hello.c
    gcc -o $(TARGET) hello.c
clean:
    rm -f $(TARGET)

이제 다음과 같이 make 명령을 이용하여 컴파일 할 수 있다.

$ make
gcc -o hello hello.c
$ make clean
rm -f hello


3. 실행 파일 분석

컴파일된 실행 파일은 코드와 다양한 섹션으로 구성된 구조를 가지고 있다. 이를 분석해 보면 프로그램이 어떻게 동작하는지 이해할 수 있다.

3.1. .rdata (.rodata)

.rdata(ReadOnly Data) 또는 .rodata 섹션은 읽기 전용 데이터가 저장되는 영역으로, 변경되지 않는 상수 데이터가 배치된다.

$ objdump -s -j .rodata hello
Contents of section .rodata:
 2000 01000200 48656c6c 6f2c2077 6f726c64 ....Hello, world
 2010 2100

"Hello, world!" 문자열이 실행 파일 내부의 읽기 전용 영역에 저장되어 있는 것을 확인할 수 있다.

3.2. disassemble

실행 파일을 디스어셈블(disassemble)하면 실제 CPU가 실행하는 어셈블리 코드를 확인할 수 있다.

$ objdump -d -M intel --disassemble=main hello
0000000000001149 <main>:
 1149: f3 0f 1e fa endbr64
 114d: 55 push rbp
 114e: 48 89 e5 mov rbp,rsp
 1151: 48 8d 05 ac 0e 00 00 lea rax,[rip+0xeac]    # 0x2004 <_IO_stdin_used+0x4>
 1158: 48 89 c7 mov rdi,rax
 115b: e8 f0 fe ff ff call 1050 <puts@plt>
 1160: b8 00 00 00 00 mov eax,0x0
 1165: 5d pop rbp
 1166: c3 ret

또한 다음 명령을 통해 특정 심볼의 주소를 확인할 수 있다.

$ nm hello | grep _IO_stdin_used
0000000000002000 R _IO_stdin_used
∴ _IO_stdin_used = 0x2000 + 0x04 = 0x2004 → 48656c6c... = Hell...

_IO_stdin_used 주소를 기반으로 문자열이 저장되어 있는 위치를 계산하면, "Hello, world!" 문자열의 주소임을 알 수 있다.

GCC 최적화: printf 함수에 포맷 지정자가 없는 경우, 컴파일러가 자동으로 puts() 함수로 대체하여 더 효율적인 코드로 변환한다.

이 블로그의 인기 게시물

실행 파일은 파일은 어떻게 동작하는가

게이트 회로로 이해하는 컴퓨터 연산

C언어 - 프로그래밍 언어의 문법