---
# System prepended metadata

title: How to basic a first C program - note (In Progress...) - 深入淺出純C程式開發 筆記

---

# How to basic a first C program - note (In Progress...) - 深入淺出純C程式開發 筆記

> All Copyright (c) belongs to ljcucc 2021

You're a C noobs (just like me) who want to take advanced to programming in C ? this may the guideline you looking for (maybeeeeeeeeee?)

this guideline (or note) will focusing on "how to write C codes to build a project"

> 在這個指南(同時也是我的筆記)中會有很多有關C的一些細節，如果你是因為老師在學校有教C，然後覺得C很好玩你很想要用C寫一些東西的人，這個guideline很歡迎你。為了幫助你學習，很多部分都用英文的，絕對不是因為我懶著切換輸入法。

[TOC]

# Entry of the journey

> TODO

> 這個筆記會很著重在如何脫離IDE，並且使用非IDE的環境開發。甚至推薦你直接使用terminal，完全脫離GUI。這對你來說是件好事，畢竟在這個過程中你會逐漸了解整個專案背後的原理和細節。

## Pros and Cons

為什麼要用C寫programs和tools？

1. 效率：和使用Golang和Rust一樣，使用C可以提高程式的效率，因為如果你的C是跑在原生的環境上，你不需要為那個python的VM多跑一層，浪費效率。在伺服器上每一行指令都是一個執行成本，多執行就是多成本。

2. CLI tools: 如果你是Terminal/TUI program的愛用者，那你一定知道CLI tools的方便，如果你是個網頁前端的開發者，你一定會體會到有很多程式都是以CLI tools的形式存在的。對大多數學校教的C programming來說，都是在撰寫CLI program或TUI (Text-based user interface)，相比GUI，TUI只需要在乎input和output就好了，大多數不用考慮GUI Framworks、跟X server的溝通，畫面的渲染、事件程式等等的問題，所以多數老師的教學或初學者在開始學習C時常常會從（或幾乎）用TUI作為開頭。

3. 精簡：C是一個很精簡的程式語言，C當初的設計目的就是爲了越接近硬體，越精簡越好。 (待修正)

> * TUI: Text-based User Interface
> * CLI: Command Line Interface

那用C寫有什麽缺點？

1. Pointer 讓記憶體的操作出現了危險性，最著名的例子就是scanf的security issue。(待修正)
2. 抽象程度不高、不支援語言内 OOP 物件導向。但你可以用struct加functions來實作OOP架構。

### 用純C寫例子

總和以上兩點，如果你用C寫出來的程式可以是非常輕量的（在檔案大小和對算力的需求下），所以對low-end device 非常的友善，例如：

* [suckless software](https://suckless.org): suckless一直想要達成的理念，所以suckless的無論是DWM (Dynamic Window Manager)還是ST(Terminal)都是用純C寫的，甚至要變更config需要到source code裡改完,非常的suckless風。
* 100r.co: 100r是一個兩人組成的小團隊，主要致力於開發low-end device的新型態應用程式，他們的網站初期版本就是用C生成的（現在則是用TAL，一種強形態組合語言）

## Recommended Sites

* [Computer Science from the Bottom Up](https://www.bottomupcs.com): This website is focusing on most computer science topic, but not really talk about details that how to wirte codes or develop of a project.
* https://users.ece.cmu.edu/~eno/coding/CCodingStandard.html Coding Standards of C ( and C++ within another webpage)
* 你所不知道的C語言（系列講座）：https://hackmd.io/@sysprog/c-programming

### Recommended Posts and Articles

* how to write GUI application in C programming language, powered by RaspberryPi.org : https://magpi.raspberrypi.com/books/c-gui-programming
* Input / Output (輸入輸出): [https://hackmd.io/@combo-tw/r19DJIRBr]
* gotop 的 21st Century C, 2nd Edition 中文版試讀（前言）：http://epaper.gotop.com.tw/PDFSample/A595.pdf

### 延申閲讀

* Golang 深入淺出：https://hackmd.io/@ljcucc/golang

# Main program

this is a main program without any arguments:

```c=
#include <stdio.h>
#include <stdlib.h>

int main(void){
    // your code here...
    return 0;
}
```

you may see some codes will written like this:

```c=
int main(void){
    // something in your main function...
    exit(0);
}
```

instead of using ```return```, you can use `exit()` to quit and terminate your program at anywhere.

> Question: why [stdio.h](https://opensource.apple.com/source/Libc/Libc-583/include/stdio.h.auto.html)? and why [stdlib.h](https://opensource.apple.com/source/Libc/Libc-583/include/stdlib.h.auto.html)?

## How you execute your program?

In Visual Studio or Dev C++, you can just click the run button, and the magic will happen, the IDE automatically finished all the build and compile process and bring your program to life.

Detials about compilers which will talk about it just a bit later... but now, in order to make your program up to life, you may want to use those command below, if you're not really understand how to run your C program outside IDE.

```bash=
# if you're using mac (darwin)
clang main.c -o main.o

# in linux (that gcc already installed) or mingw32
gcc main.c -o main.o
```
```bash=
## then run your program
./main.o

## or on M$ Windows
./main.exe
```

if you're using windows, it's not really convenience  to develop without IDE or develop natively. we'll mention baout it later...

## Exit code

if you're program return 0, which means the program finished the process with no issue.

```c=
int main(void){
    return 0; // Execute Successful 
}
```


if you return 1 which means general error:

```c=
int main(void){
    return 1; // general error
}
```

here's a example that you can try out different error code:

```c=
int main(void){
    int num;
    scanf("%d", &num);
    return num;
}
```

> more about exit code: https://www.baeldung.com/linux/status-codes

## Arguments

if you have to handle those arguments, you can write it in:

```c=
int main(int argc, char** argv){
    return 0;
}

// or

int main(int argc, char* argv[]){
    return 0;
}
```

simple way to handle arguments:

```c=
int main(int argc, char** argv){
    for(int i = 0; i < argc; i++)
        printf("%d, %s", argc[i], argv[i]);
    return 0;
}
```


# Start your development journey

## Virtualization Development environment

If you're switching machine between OS frequencly, you can consider using virtualised environment, but however virtualization is recommended, which to make sure everyone's environment is the same and not effected by local host system.

### Multipass

Multipass is a quick way to get started into virtual dev environments, get started by:

* windows: go downlaod from https://multipass.run
* linux: `sudo snap install multipass`
* macOS: `brew install --cask multipass`

and then launch your machine by command:

```bash=
# Create a VM
$ multipass launch -n [your dear VM name]

# Connect to VM's shell
$ multipass shell [your dear VM name]
```

## Basic knowledge: Compiler & Environment

compiler may differernt in differernt OS, e.g.

* Linux
    * GNC Complex Compiler: GCC
    * TCC: Tiny c compiler
* macOS
    * CC: C Compiler (paid & license) （已棄用）
    * clang: Front-end of LLVM
* Windows
    * MSVC (Microsoft Visual C++) - :-1: Not recommended
    * gcc (mingw32 or 64 or cygwin)

these are compiler only, your C program may copmile within some in-built libraries (or Standard Library):

* Linux
    * glibc: GNC C Library (Open Source)
* macOS
    * BSD Libc: C Library for BSD (Open Source)
* Windows
    * MSVC, CRT (Microsoft Visual C++ or C Runtime) (can't find source :-1:) 
    * Compatible with glibs with mingw or cygwin

> Question: What exactly is mingw32?
> https://fekir.info/post/cygwin-and-wsl/

sometimes you need to make sure that the codebase is written for the environment you're using, in this case you can use `#ifdef` to using the correct codebase base on the condition you written.

Example for define a special codebase for MSVC (MS Visual C++):

```c=
#ifdef MSVC
       .... Microsoft specific code
#else
       .... Code for other compiler.
#endif
```
> [::reference::](https://stackoverflow.com/questions/12558538/how-can-i-check-a-certain-type-is-already-defined-in-c-compiler)

if you're coding for Apple ecosystem in Obj-C, you may seen this:

```objectivec=
#ifdef __APPLE__
    #include "TargetConditionals.h"
    #ifdef TARGET_OS_IPHONE
         // iOS
    #elif TARGET_IPHONE_SIMULATOR
        // iOS Simulator
    #elif TARGET_OS_MAC
        // Other kinds of Mac OS
    #else
        // Unsupported platform
    #endif
#endif
```

so if you're using `cc` on mac to compile your code and you want to make sure your codebase is just written for mac, you may write your code like this?

```c=
#ifdef __APPLE__
  printf("hi apple");
#else
  printf("you're not apple");
#endif 
```

> [::reference::](https://stackoverflow.com/questions/6802903/c-ifdef-mac-os-x-question)

## How you execute your program?

In Visual Studio or Dev C++ (IDEs), you will create a "Project file" or "Project" if you want to start coding, then you may have to create main program or IDE will automatically create a main program, then you started create different sub-program of program. if you need to run the program, just click the run button, then IDE will do all build tasks for you...

But in reality, without IDEs, there's no "Project File", no "Workspsce" or "Solution", there just srouce code files and some script to run. you can call right tools to help you to get your program to life, you may have different way but it's up to you. in next section, I'll introduce you some way to make you program up to running.

### Bash Scripting (Raw way to do)

without any experience, you may started compile your program with `gcc` or `clang`, and you may just directly type these command in your terminal and try to compile it and run it:

```bash
$ gcc main.c -o main.o # compile
# Some warning or compile message...

$ ./main.o # run it
```

to make you no need to type these command every time, you can write a bash script to do those command for you:

```bash=
echo "start compile..."
gcc main.c -o main.o
./main.o
```

### Makefile

You may familiar with Makefile if you're using (Bloodshed) Dev C++, in Dev C++, while you build a project, the IDE will automatically create a Makefile called `Makefile.win` then run it by using in-built `gcc` or `g++` in MinGW (if you're using the version within MinGW and gcc), or you're frequently compile program from source, you might see here's a command called `make install` or `make clean`...

you can treat Makefile as a automatic script with command name separate and arguments, e.g.

```
run: 
    clang main.c -o main.o
    ./main.o
    
compile:
    clang main.c -o main.o
```

or with fake target:
```
run: compile
    ./main.o
    
compile:
    clang main.c -o main.o
```

or separate c compiler(CC) and output file name to a variable:

```
CC=clang
OUTPUT=main.o

run: compile
    ./$(OUTPUT)
    
compile:
    $(CC) main.c -o $(OUTPUT)
```

then you can build a simple Makefile for compile, run and debug function:

```
CC=clang
DEBUGER=lldb
MAIN=main.c
OUTPUT=./bin/main.o

run: compile
    $(OUTPUT)

compile:
    $(CC) $(MAIN) -o $(OUTPUT)

debug: compile
    $(CC) $(MAIN) -g -o $(OUTPUT)
    $(DEBUGER) $(OUTPUT)
    
```

### CMake 

> TODO: writing how CMake important it is

## How to debug your program?

> TODO: 

best way to debug your program in tui is using `lldb` or `gdb`. they are powerful tools, you can even using these debugger to disassembly and debug assembly.

by enable debug mode (which will store your source code in binary), you can add `-g` flag while you compile:

```bash
$ clang ./main.c -g -o main.o
$ lldb ./main.o
```

### Logs and Checkpoints

> TODO: using printf, custom infomation

### Breakpoints

> TODO: how to set breakpoint in `clang`?

while you open `lldb` or `gdb`, you can add breakpoints by using command `b`:

```
(gdb) l
1       #include <stdio.h>
2       #include <stdlib.h>
3       #include <stdint.h>
4
5       int main(void){
6         uint8_t a = 10;
7         printf("%d", a);
8
9         exit(0);
10      }
(gdb) b 6
Breakpoint 1 at 0x40155d: file test.c, line 6.
(gdb) r
```

in the terminal screen log, you can see that `(gdb) l` will list your source code, then using `(gdb) b 6` to set a breakpoint at line 6th, then using `(gdb) r` or `(gdb) run` to start you program.

> type `quit` to quit debugger

## Analysis your program - Disassembly & debug with GDB

> [::reference::](http://sourceware.org/gdb/current/onlinedocs/gdb/Machine-Code.html)

let's write some simple program: 

```c=
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(void){
  uint8_t a = 8;
  printf("%d", a);
  exit(0);
}
```

then using `gdb` to debug it...

```
$ gcc -g main.c -o main.o
$ gdb main.o

GNU gdb (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
...
```


OK, let's setup a breakpoint at `exit(0);` and run...
```
(gdb) b 10
Breakpoint 1 at 0x401573: file main.c, line 10.
(gdb) r
Starting program: /path/to/your/main.o
[New Thread 4240.0x9f0]
[New Thread 4240.0x18a4]
8
Thread 1 hit Breakpoint 1, main () at main.c:10
10        exit(0);
```

then disassembly...
```
(gdb) disas /m
Dump of assembler code for function main:
5       int main(void){
   0x0000000000401550 <+0>:     push   %rbp
   0x0000000000401551 <+1>:     mov    %rsp,%rbp
   0x0000000000401554 <+4>:     sub    $0x30,%rsp
   0x0000000000401558 <+8>:     callq  0x401630 <__main>

6         uint8_t a = 8;
   0x000000000040155d <+13>:    movb   $0x8,-0x1(%rbp)

7
8         printf("%d", a);
   0x0000000000401561 <+17>:    movzbl -0x1(%rbp),%eax
   0x0000000000401565 <+21>:    mov    %eax,%edx
   0x0000000000401567 <+23>:    lea    0x2a92(%rip),%rcx        # 0x404000
   0x000000000040156e <+30>:    callq  0x402a60 <printf>

9
10        exit(0);
=> 0x0000000000401573 <+35>:    mov    $0x0,%ecx
   0x0000000000401578 <+40>:    callq  0x402a90 <exit>
   0x000000000040157d <+45>:    nop

End of assembler dump.
```

as you can see, disassembler telling us that each instruction belongs to which code we wrote.

# Deep look into your C code

## Basic Program

* you can reference article from: https://hackmd.io/@sysprog/c-function

## Pointers

我們的code最終都會被編譯成machine code，如果machine code被disassembler disassemble的話，那code就會變成人看得懂的組合語言（assembly）, assembly顯示著程式真正在電腦上執行的過程和方法。

有些時候，我們可以透過一些code上寫法的技巧來推敲出C運作的特性。例如以下example：

```c=
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(void){
  int a = 10;
  int b = 20;

  printf("%d %d", (&a)[1], b);

  exit(0);
}

/*
 * output: 20
 */
```

雖然這乍看之下我們可以用理論解釋爲什麽`(&a)[1]`會跨存取到`b`的記憶體位址，但是我們可以透過上一個section的方法在disassemble裏面看到一些玄機：

```
(gdb) disas /m
Dump of assembler code for function main:
5       int main(void){
   0x0000000000401550 <+0>:     push   %rbp
   0x0000000000401551 <+1>:     mov    %rsp,%rbp
   0x0000000000401554 <+4>:     sub    $0x30,%rsp
   0x0000000000401558 <+8>:     callq  0x401650 <__main>

6         int a = 10;
   0x000000000040155d <+13>:    movl   $0xa,-0x8(%rbp)

7         int b = 20;
   0x0000000000401564 <+20>:    movl   $0x14,-0x4(%rbp)

8
9         printf("%d %d", (&a)[1], b);
   0x000000000040156b <+27>:    lea    -0x8(%rbp),%rax
   0x000000000040156f <+31>:    add    $0x4,%rax
   0x0000000000401573 <+35>:    mov    (%rax),%eax
   0x0000000000401575 <+37>:    mov    -0x4(%rbp),%edx
   0x0000000000401578 <+40>:    mov    %edx,%r8d
   0x000000000040157b <+43>:    mov    %eax,%edx
   0x000000000040157d <+45>:    lea    0x2a7c(%rip),%rcx        # 0x404000
   0x0000000000401584 <+52>:    callq  0x402a80 <printf>

10
11        exit(0);
=> 0x0000000000401589 <+57>:    mov    $0x0,%ecx
   0x000000000040158e <+62>:    callq  0x402ab0 <exit>
   0x0000000000401593 <+67>:    nop

End of assembler dump.
```

例如`int a `的位置和`int b`的位置只只相差4個byte（這和int的size 4 byte相符）

---

# Interfaces and interaction

## [TUI (Text based user interface)](https://en.wikipedia.org/wiki/Text-based_user_interface)

> TODO

### History of TUI - Terminal

* type of terminal
* terminal emulator
* terminal mode

Terminal used to be a kind of hardware to display output texts and get input from keyboard.

![image alt](https://upload.wikimedia.org/wikipedia/commons/thumb/9/9f/DEC_VT100_terminal_transparent.png/1920px-DEC_VT100_terminal_transparent.png)

but in these days, computer are already have the ability to process and display text or graphics, and accept inputs from different type of hardware, i.e. your computer screen (or you can using serial output in these days but you need a serial monitor software, driver chip and a driver software in your computer in order to read serial signal from USB interface)

### Type of terminal and color set

> TODO

here's some terminal (and also most of the terminal emulator supported)

* vt52
* vt100
* vt102
* xterm
* xterm-new
* xterm-256color
* xterm-16color
* ansi
* dtterm
* nsterm
* rxvt
* linux

> what is that "type of terminal" ???

> Q: wait, what's the different bewteen SSH and terminal, aren't they same thing which just remote control a computer system in copmuting center?

### [Terminal emulator](https://en.wikipedia.org/wiki/Terminal_emulator)

> TODO

### [Terminal mode](https://en.wikipedia.org/wiki/Terminal_mode)

> TODO

#### Clear you terminal screen

> TODO

#### Get terminal width and height

```c=
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>

int main (int argc, char **argv)
{
    struct winsize w;
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);

    printf ("lines %d\n", w.ws_row);
    printf ("columns %d\n", w.ws_col);
    return 0;  // make sure your main returns int
}
```

> [unistd.h](https://en.wikipedia.org/wiki/Unistd.h) 是一個包含五花八門功能的library

> [::reference::](https://stackoverflow.com/questions/1022957/getting-terminal-width-in-c)

### Input and output - stdin & stdout

Do you ever wonder how `printf()` or `scanf()` works? here's some experiment and example that you can try it out.

> Experiment: test it on your mac or linux (windows may not working)
```c=
#include <stdio.h>
#include <stdlib.h>

int main(){
  FILE* f = fopen("/dev/stdout", "w");
  fprintf(f, "hihi\n");
  fclose(f);
  exit(0);
}
```

> Experiment:

```c=
#include <stdio.h>
#include <stdlib.h>

int main(){
  fprintf(stdout, "hihi\n");
  exit(0);
}

```

you may find out that you can actually operate input and output through the path of `stdin (/dev/stdin)` and `stdout (/dev/stdout)` or the IO stream object of `stdin` and `stdout`.

> Question: Why? how dose this happend?

> [::reference::](https://www.putorius.net/linux-io-file-descriptors-and-redirection.html)
> [::reference::](https://www.bottomupcs.com/starting_a_process.xhtml)

### File Descriptors

> TODO

> File Descriptors： https://www.bottomupcs.com/file_descriptors.xhtml#d0e490

* `/dev/input` not exists on macOS

> https://stackoverflow.com/questions/47892547/mac-low-level-keyboard-device-corresponding-to-linux-dev-input

### Shell

> shell is a entry point that you run your program... 

> Input / Output (輸入輸出): [https://hackmd.io/@combo-tw/r19DJIRBr]

## GUIs

> `[unfinished]`

### With frameworks

> you can reference this book to learn how to write GUI application in C programming language, powered by RaspberryPi.org : https://magpi.raspberrypi.com/books/c-gui-programming

### Without frameworks

> without GUI framework, you need to handle all the communications bewteen window server and graphics rendering

# 不同OS的環境設定和使用

## On Micro$oft Windows (特例)

If you **MUST** using Micro$oft Windows, then you need to understand that windows is not a unix or unix-like OS, so everything will be different. although you can using VM or mingw to implement the environment, but I'm **VERY** recommended to using linux or UNIXs directly.

if you have no more way or possiability to switch OS from M$ Windows, here's some info may help you out.

more detials you can look at this note: ...

### MinGW and Cygwin (Modified MinGW)

因為Windows沒有POXIS係系統的“everything is a file” 的特性，所以一切都會不一樣，上述的所有方法可能在Windows上都不適用，這時候我們需要一個「模擬的類unix環境」，MinGW就是在幹這種事，而Cygwin、MSYS2則是他的翻版。

你可以發現其實Dev C++和Windows的Codeblocks的CC都是使用MinGW的GNU CC（在Codeblocks中則是可以選擇MSVC，就看你的選擇了）

在開始安裝Cygwin的時候你就可以選擇你想要使用的packages，你只要選擇你想要的套件就可以使用，但是唯一的缺點是如果Cygwin不支援的套件除非你可以自行編譯成功，要不然都必須仰賴Cygwin的社群建源（或者你可以來做）。

### MSYS2 (Modified MinGW)

* 建議閲讀：https://opensourcedoc.com/windows-programming/mingw-msys/

### With IDEs

不推薦使用IDE，自由度不高且耗效能，也無法完全控制你的專案的細節，IDE都幫你包好了。

* MinGW + GCC：Dev C++、Codeblocks
* MSVC：Visual Studio （Visual C++）

### Without IDEs

Editors:
* GUI
    * VSCode: 勉強可以，你可以使用VSCode內建的自動化工具來實現專案的建置。
* TUI
    * **vim (by using MinGW) :+1::+1::+1::+1::+1: 很推薦**
    * ~~Emacs (不要殘害自己)~~

### Troubleshooting

> TODO

## Other Platforms

### On iPad or iPhone

iPad or iPhone can't access OS or directly execute code on device, yet. but you can using JIT or JIT-like solution to run your project on iOS device. e.g. iSH

### On Android or Chromebook

> TODO

# Small project to build

* A website builder - generating a website structure by using C
* A vim like editor?

# Others

* error: unknown type name 'bool'

### String processing tips

* sscanf
* sprintf