目录

注: 笔记代码均在 Linux 环境下测试运行.


Linux 下 C 程序的编写和运行

  1. Terminal 中,打开/创建 test.c 文件:
     nano test.c
  2. 编写 C 程序, 退出并保存
     #include<stdio.h>
     // 双斜杠, 单行注释
     /* 多行注释
         C 语言中, int main() 和 void main() 是一样的;
         C++ 中, 只接受 int main() 这种定义方式.
     */
     int main() {
         printf("This is a test code.\n");
         return 0;
     }
  3. 编译 C 程序:
    (此处使用 gcc 进行编译, 程序中如使用了 C++ 语法, 需使用 g++ 进行编译)
     gcc test.c -o test
  4. 运行目标程序:
    ("./"不能少)
     ./test

关键字 & 标识符 Keywords & Identifier

As C is a case sensitive language, all keywords must be written in lowercase.

  • 字符集 Character Set
    • 字母 Alphabets
      • 大写 Uppercase : A B C ... X Y Z
      • 小写 Lowercase : a b c ... x y z
    • 数字 Digits : 0 1 2 3 4 5 6 7 8 9
    • 特殊字符 Special Characters (e.g. 空白字符 White space Characters : Blank space, newline, horizontal tab, carriage return and form feed.)
  • 关键字
    • 关键字 Keywords
      Keywords are predefined.
    • 保留字 Reserved Words
      Reserved words used in programming that have special meanings to the compiler.
  • 标识符 Identifier
    • Identifier refers to name given to entities such as variables, functions, structures etc.
    • Identifiers must be unique.
    • Rules for naming identifiers :
      • A valid identifier can have letters (both uppercase and lowercase letters), digits and underscores.
      • The first letter of an identifier should be either a letter or an underscore.
      • You cannot use keywords like int, while etc. as identifiers.
      • There is no rule on how long an identifier can be. However, you may run into problems in some compilers if the identifier is longer than 31 characters.

C 程序的编译链接过程

  1. 预处理:
    宏定义展开\头文件展开\条件编译等, 同时将代码中的注释删掉;
  2. 编译:
    检查语法; 语法无误后, 将预处理后的文件编译生成汇编文件.s;
  3. 汇编:
    将汇编文件生成目标文件.o(二进制文件);
  4. 链接:
    C 语言编写的程序是需要依赖各种库文件(系统组件)的, 所以要把编译之后的目标文件库链接到最终的可执行程序.exe中去.(可能链接多个目标文件)

一般情况下我们所说的“编译器”实际上也包括了链接器

  • 编译器 Compiler
    编译就是将我们编写的源代码“翻译”成计算机可以识别的二进制格式,它们以目标文件的形式存在;
    • 预处理器 Preprocessor
    • 编译器 Compiler
    • 汇编器 Assembler
  • 链接器 Linker
    链接就是一个“打包”的过程,它将所有的目标文件以及系统组件组合成一个可执行文件。

1. 预处理 Preprocess

  • 预处理一般是指在源程序被转换为二进制代码之前, 对源文件进行预编译.
  • 预处理功能:
    • 宏定义
    • 条件编译
    • 文件包含
    • 删除注释

1.1 宏 macro

A macro is *a fragment of code that is given a name.
宏替换, 只做字符序列的替换工作, 不作任何语法检查.

  • 简单宏 macro
    • #define 标识符 字符序列
  • 宏函数 function-like macro
    • #define 标识符() 字符序列
  • 预定义的宏 Predefined Macro:
    • __DATE__ : A string containing the current date
    • __FILE__ : A string containing the file name
    • __LINE__ : An integer representing the current line number
    • __STDC__ : If follows ANSI standard C, then the value is a nonzero integer
    • __TIME__ : A string containing the current date
  • 宏函数 & 函数 的区别:
    • 宏函数, 也是宏, 只做简单的 字符替换, 不会浪费新的堆栈空间, 使用多少次, 就替换多少次, 就编译多少次.
    • 函数, 只会编译一次, 但每次调用都会临时占用新的堆栈空间.
#include <stdio.h>
#define A 5
#define MAX(a,b) ((a)>(b) ? (a) : (b))    // 加 () 防止 运算优先级导致运算出错

int main() {
    // 使用预定义的宏 打印系统当前时间
    printf("Current date & time:\t%s %s\n\n", __DATE__, __TIME__);
    printf("Enter the a Integer: ");
    int a, b;
    scanf("%d %d", &a, &b);
    printf("MAX(%d,A) = %d\nMAX(%d,A) = %d\n", a, MAX(a,A), b, MAX(b,A));

    return 0;
}
#include<stdio.h>
// 多行声明一个宏时, 一行结尾处加上字符'\', 然后紧跟回车, '\'后面不要有其他任何字符.
#define A int main() {\
    B\
    C\
    return D;\
}
#define B printf("This is a macor that could be undefined\n");
#define C printf("This is a macor test\n");
#define D 0

// #undef B    // 取消已定义的宏 B. 取消定义后, 预编译时不能再使用 B 做宏替换 (如继续使用则会报错)

A

1.2 条件编译 conditional compilation

In C programming, you can instruct preprocessor whether to include a block of code or not.

#include<stdio.h>

#if !defined A        // 等价与 `#ifndef A`, 表示如果没有定义宏 A, 则执行以下代码
// #ifndef A
#define A "This is A.\n"
#endif            // 标志最近的一个条件编译(#if !defined A)的结束

#define B 0
#if defined B        // 等价于 `#ifdef B`, 表示如果定义了宏 B, 则执行以下代码
// #ifdef B
#define B "This is B.\n" // 重新定义宏 B, 覆盖原来的定义
#else
#define C This is C.
#endif            // 标志最近的一个条件编译(#if defined B)的结束

// 每个条件编译都需要用 `#endif` 作为结束
int main() {
    printf(A);
    printf(B);
    // printf("C\n");    // 字符串中没有宏替换, 只有一般字符和控制字符等
    return 0;
}
  • It's similar to a if statement with one major difference.
    • The if statement is tested during the execution time to check whether a block of code should be executed or not whereas.
    • The conditionals are used to include (or skip) a block of code in your program before execution.
  • Uses of Conditional
    • use different code depending on the machine, operating system
    • compile same source file in two different programs
    • to exclude certain code from the program but to keep it as reference for future purpose

1.3 文件包含

  • .c 文件, 存放具体功能的实现, 也可直接用于文件包含;
  • .h 文件, 存放声明, 一般用于声明同名 .c 文件中的函数, 并用于文件包含.
  • #include<***> 引入 <标准头文件> 如: #include<stdio.h> 编译系统在系统头文件所在目录搜索包含头文件 stdio.h
  • #include"***" 引入 "自定义头文件" 如: #include"myfile.h" 编译系统首先在 当前的源文件所在的目录 中查找 myfile.h , 如没有找到, 再转到系统头文件所在的目录(即系统指定的 包含文件目录)搜索.
#ifndef STDIO_H        // 如果没有定义 STDIO_H, 则执行以下代码
#define STDIO_H        // 定义宏 STDIO_H
#include<stdio.h>     // 包含头文件 stdio.h
#endif                // 结束这个条件编译

int main() {
    printf("可以通过 条件编译, 来解决文件的 重复包含 问题.\n");
    return 0;
}

Here, stdio.h is a header file. The #include preprocessor directive replaces the above line with the contents of stdio.h header file.
That's the reason why you need to use #include <stdio.h> before you can use functions like scanf() and printf().

2. 编译 Compile

  • 将C语言代码转换成CPU能够识别的二进制指令.
  • 大致包括 词法分析、语法分析、语义分析、性能优化、生成可执行文件 五个步骤.
  • 通常一个目标文件中至少有两个段:代码段 & 数据段
  • 编译是针对一个源文件的,有多少个源文件就需要编译多少次,就会生成多少个目标文件。
  1. 编译: .c/.cpp/.h ---> .s
  2. 汇编: .s ---> .o
    • 对于 Visual C++,目标文件的后缀是.obj
    • 对于 GCC,目标文件的后缀是.o

3. 链接 Link

  • 编译只是将我们自己写的代码变成了二进制形式(并不是最终的可执行文件.exe),它还需要和系统组件(比如标准库、动态链接库等)结合起来,这些组件都是程序运行所必须的.
  • 链接(Link)其实就是一个“打包”的过程,它将所有二进制形式的 目标文件 & 系统组件 组合成一个可执行文件.
  • 随着我们学习的深入,我们编写的代码越来越多,最终需要将它们分散到多个源文件中,编译器每次只能编译一个源文件,生成一个目标文件,这个时候,链接器除了将目标文件和系统组件组合起来,还需要将编译器生成的多个目标文件组合起来.

4 运行 How does C program work?

  • All valid C programs must contain the main() function. The code execution begins from the start of the main() function.
  • The eturn 0; statement inside the main() function is the "Exit status" of the program. It's optional.





Reference Materials:
https://www.cnblogs.com/mickole/articles/3659112.html
https://www.cnblogs.com/carpenterlee/p/5994681.html
https://www.programiz.com/c-programming





Why Learn C Programming?

  • C helps you to understand the internal architecture of a computer, how computer stores and retrieves information.
  • After learning C, it will be much easier to learn other programming languages like Java, Python, etc.
  • Opportunity to work on open source projects. Some of the largest open-source projects such as Linux kernel, Python interpreter, SQLite database, etc. are written in C programming.