c语言超详细知识点总结 1500行手写源码 持续更新中ing 从25年5月到6月5日

想象一下,我们身处的数字世界,如同一座座宏伟的建筑。操作系统、编译器、数据库、嵌入式设备乃至绚丽的游戏引擎,它们都是这座大厦的重要组成部分。而C语言,正是构建这一切的坚固基石。自丹尼斯·里奇于贝尔实验室孕育出这颗编程界的明星以来,C语言凭借其高效性灵活性以及对计算机底层那份极致的掌控力,历经数十载风雨洗礼,依旧是无数开发者心中的不二之选。

学习C语言,不仅仅是掌握一门编程技能,更是一次深入探索计算机灵魂的旅程。它能让你理解内存如何管理、程序如何与硬件交互,为你后续学习其他高级语言、深入计算机体系结构打下坚实的基础。本教程将引领你,从C语言的基础语法出发,逐步揭开其神秘面纱,助你在这条充满挑战与机遇的道路上稳步前行。

一、初识C语言:搭建你的第一个“Hello, World!”

万丈高楼平地起,我们从最经典的程序开始。

1.1 第一个C程序:向世界问好

#include <stdio.h> // 引入标准输入输出头文件

// main函数是程序的入口点,操作系统会从这里开始执行
int main() {
    // printf是一个标准库函数,用于向控制台输出文本
    // "\n" 是一个转义字符,表示换行
    printf("Hello, World!\n"); 

    // main函数返回0表示程序正常结束
    return 0; 
}

代码解析

  • #include <stdio.h>:这是一个预处理指令。它告诉编译器在实际编译之前,将stdio.h(标准输入输出头文件)的内容包含进来。这个头文件里声明了我们后面用到的printf等函数。

  • int main():这是C程序的主函数,每个C程序都必须有且只有一个main函数。程序从main函数开始执行,到main函数结束。int表示main函数执行完毕后会返回一个整数值给操作系统。

  • printf("Hello, World!\n");:调用printf函数,将引号内的字符串输出到屏幕上。\n是一个特殊的字符,代表换行。

  • return 0;:表示main函数执行成功并正常退出。通常,返回0代表成功,非0代表出现某种错误。

  • ///* ... */:这些是注释//用于单行注释,/* ... */用于多行注释。注释是给程序员看的,编译器会忽略它们。

1.2 开发环境的选择与搭建

要编译和运行C代码,你需要一个C编译器和开发环境。

  • 编译器

    • GCC (GNU Compiler Collection):Linux和macOS上最常用的开源编译器,也可通过MinGW/Cygwin在Windows上使用。

    • Clang:LLVM项目的一部分,以其快速编译和优秀的错误提示著称。

    • Visual C++ (MSVC):微软Windows平台下的编译器,集成在Visual Studio中。

  • 集成开发环境 (IDE)

    • Visual Studio Code (VS Code):轻量级且强大的跨平台编辑器,通过插件支持C/C++开发。

    • Visual Studio:功能全面的Windows平台IDE。

    • CLion:JetBrains出品的专业C/C++跨平台IDE。

    • Dev-C++:简单易用的Windows平台IDE,适合初学者快速上手。

选择一个你用着顺手的即可。对于初学者,VS Code配合GCC/Clang或者Dev-C++都是不错的选择。

1.3 从源代码到可执行程序:编译与链接之旅

当你写完C代码(.c文件)后,它并不能直接运行,需要经历以下步骤:

  1. 预处理 (Preprocessing):处理#include, #define等预处理指令,展开宏,删除注释等。生成.i文件。

  2. 编译 (Compilation):将预处理后的代码转换成汇编语言。生成.s文件。

  3. 汇编 (Assembly):将汇编代码转换成机器可以执行的二进制指令(目标代码)。生成.o.obj文件。

  4. 链接 (Linking):将你的目标代码和程序中用到的库函数(比如printf)的目标代码组合起来,生成最终的可执行文件(如Windows下的.exe或Linux下的无后缀文件)。

二、C语言的基石:数据类型与变量

程序处理的是数据,而数据有不同的类型。

2.1 基本数据类型:构建数据的砖瓦

C语言提供了多种基本数据类型来存储不同种类的数据:

类型

关键字

大致字节数 (常见32/64位系统)

典型范围 (有符号signed)

用途描述

字符型

char

1

-128 ~ 127

存储单个字符

短整型

short

2

-32,768 ~ 32,767

存储较小范围整数

整型

int

4

-2,147,483,648 ~ 2,147,483,647

常用的整数类型

长整型

long

4 或 8

依赖系统和编译器

存储较大范围整数

更长整型

long long

8

约 -9x10^18 ~ 9x10^18

存储非常大整数

单精度浮点型

float

4

约 ±3.4e±38 (6-7位有效数字)

存储带小数的数

双精度浮点型

double

8

约 ±1.7e±308 (15-16位有效数字)

存储更高精度小数

无类型

void

N/A

N/A

特殊用途,如指针

注意long的大小在不同系统和编译器下可能不同(32位系统通常4字节,64位系统通常8字节)。可以使用sizeof运算符来查看特定类型在当前系统上占用的字节数。

2.2 类型修饰符:数据的更多面貌

  • signed:表示有符号数(可以表示正、负、零),对char和整型类型默认即为signed

  • unsigned:表示无符号数(只能表示非负数)。同样的字节数,无符号类型可以表示更大的正数范围。例如 unsigned int

    unsigned char u_char_val = 200; // 范围 0 ~ 255
    signed char s_char_val = -100;  // 范围 -128 ~ 127
    
    
  • const:定义常量,其值在初始化后不能被修改。

    const double PI = 3.14159;
    // PI = 3.14; // 错误!PI是常量,不可修改
    
    

2.3 变量:存储数据的容器

变量是内存中用于存储数据的一块具名空间。

  • 声明:告诉编译器变量的名字和类型。 数据类型 变量名;

  • 初始化:在声明变量时给它一个初始值。 数据类型 变量名 = 初始值;

#include <stdio.h>

int main() {
    int age;               // 声明一个整型变量 age
    age = 30;              // 给 age 赋值

    float salary = 5000.50f; // 声明并初始化一个浮点型变量 salary (f后缀表示float)
    char grade = 'A';        // 声明并初始化一个字符型变量 grade (单引号括起字符)

    // 变量在使用前通常需要初始化,否则其值是不确定的(垃圾值)
    int uninitialized_var; 
    // printf("%d\n", uninitialized_var); // 行为未定义,可能输出任意值

    printf("年龄: %d\n", age);
    printf("薪水: %.2f\n", salary); // .2f 表示保留两位小数
    printf("等级: %c\n", grade);

    return 0;
}

2.4 常量:不变的值

  • #define 宏常量 (预处理指令):在预处理阶段进行文本替换。

    #define MAX_USERS 100
    int users[MAX_USERS]; // 预处理后变为 int users[100];
    
    
  • const 限定符:定义类型化的常量,编译器会进行类型检查。更推荐使用const

    const int MAX_SCORE = 100;
    // MAX_SCORE = 99; // 编译错误
    
    

三、运算符与表达式:数据的加工厂

运算符用于对数据进行操作,表达式则是由数据和运算符组成的计算式。

3.1 算术运算符

+ (加), - (减), * (乘), / (除), % (取模/取余数)

int a = 10, b = 3;
printf("a + b = %d\n", a + b); // 13
printf("a / b = %d\n", a / b); // 3 (整数除法,结果截断小数)
printf("a %% b = %d\n", a % b); // 1 (10除以3的余数)

float c = 10.0f, d = 3.0f;
printf("c / d = %f\n", c / d); // 3.333333 (浮点数除法)

自增 ++ 和自减 --

  • 前缀++a (先自增,后使用新值)

  • 后缀a++ (先使用原值,后自增)

int x = 5;
int y = ++x; // x先变成6,然后y被赋值为6。此时 x=6, y=6
int z = x++; // z先被赋值为6(x的当前值),然后x变成7。此时 x=7, z=6
printf("x=%d, y=%d, z=%d\n", x, y, z); // 输出 x=7, y=6, z=6

3.2 关系运算符

用于比较两个值,结果为真(1)或假(0)。 == (等于), != (不等于), > (大于), < (小于), >= (大于等于), <= (小于等于)

int num1 = 5, num2 = 10;
printf("num1 == num2 is %d\n", num1 == num2); // 0 (假)
printf("num1 < num2 is %d\n", num1 < num2);   // 1 (真)

3.3 逻辑运算符

用于连接或修改关系表达式,结果也为真(1)或假(0)。

  • && (逻辑与):两边都为真,结果才为真。

  • || (逻辑或):一边为真,结果就为真。

  • ! (逻辑非):真变假,假变真。

int age = 25;
float height = 1.75f;
// 年龄大于18 并且 身高大于1.7
if (age > 18 && height > 1.7) {
    printf("符合条件\n");
}

短路求值

  • 对于 A && B,如果A为假,则B不会被求值。

  • 对于 A || B,如果A为真,则B不会被求值。

3.4 位运算符 (了解即可,进阶内容)

直接对数据的二进制位进行操作。 & (按位与), | (按位或), ^ (按位异或), ~ (按位取反), << (左移), >> (右移)

3.5 赋值运算符

= (简单赋值), +=, -=, *=, /=, %= (复合赋值) a += 5; 等价于 a = a + 5;

3.6 条件运算符 (三目运算符)

C语言中唯一的三目运算符:条件 ? 表达式1 : 表达式2 如果条件为真,则整个表达式的值为表达式1的值;否则为表达式2的值。

int score = 85;
char grade = (score >= 60) ? 'P' : 'F'; // P (Pass), F (Fail)
printf("成绩等级: %c\n", grade); // 输出 P

优点:简洁。缺点:嵌套过多时可读性差,不宜滥用。

3.7 sizeof 运算符

返回一个类型或一个变量所占用的内存字节数。它是一个编译时运算符(大多数情况)。

printf("sizeof(int) = %zu bytes\n", sizeof(int));
printf("sizeof(double) = %zu bytes\n", sizeof(double));
int arr[10];
printf("sizeof(arr) = %zu bytes\n", sizeof(arr)); // 输出 10 * sizeof(int)
```%zu` 是 `sizeof` 结果的推荐格式说明符。

### 3.8 运算符优先级与结合性
当一个表达式中包含多个运算符时,优先级决定了运算顺序(类似数学中的先乘除后加减)。结合性决定了相同优先级的运算符从左到右还是从右到左执行。
**经验法则**:不确定优先级时,多用括号 `()` 来明确运算顺序,提高代码可读性。

例如,`*p++` 和 `(*p)++` 是面试中常考的:
* `*p++`:后缀`++`优先级高于`*`,且结合性从左到右。相当于 `*(p++)`。它先取得`p`指向的值,然后`p`指针自增(指向下一个元素)。
* `(*p)++`:括号优先级最高。它先取得`p`指向的值,然后对这个值进行自增操作。`p`指针本身不移动。

---

## 四、程序的眼睛和嘴巴:标准输入与输出

程序需要与用户交互,接收输入并展示结果。

### 4.1 标准输出 `printf()`
我们已经多次使用过`printf`函数了。它的原型在`<stdio.h>`中。
`printf("格式控制字符串", 参数列表);`
**格式控制符**:
* `%d` 或 `%i`:输出有符号十进制整数。
* `%u`:输出无符号十进制整数。
* `%f`:输出浮点数 (默认6位小数)。
    * `%.2f`:输出浮点数,保留2位小数。
    * `%e` 或 `%E`:以科学计数法输出浮点数。
* `%c`:输出单个字符。
* `%s`:输出字符串 (字符数组,直到遇到`\0`)。
* `%p`:以十六进制形式输出指针地址。
* `%x` 或 `%X`:以十六进制形式输出无符号整数。
* `%%`:输出一个 `%` 字符。

```c
#include <stdio.h>

int main() {
    int item_count = 10;
    float price = 19.99f;
    char item_name[] = "C语言教程"; // 字符串本质是字符数组

    printf("商品名称: %s\n", item_name);
    printf("数量: %d\n", item_count);
    printf("单价: %.2f 元\n", price);
    printf("总价: %.2f 元\n", item_count * price);
    printf("商品地址(内存中): %p\n", (void*)item_name);
    return 0;
}

4.2 标准输入 scanf()

用于从键盘读取用户输入。 scanf("格式控制字符串", &变量1的地址, &变量2的地址, ...); 关键点

  • scanf的参数必须是变量的地址,所以变量名前要加取地址符 &。 (数组名本身代表地址,通常不需要&)。

  • 输入数据时,各项数据之间默认用空格、制表符或回车分隔。

  • scanf在遇到不匹配的输入时可能会停止读取。

  • 安全隐患scanf("%s", str)读取字符串时,若输入过长,会导致缓冲区溢出,非常危险。应使用限制长度的读取方式或fgets

#include <stdio.h>

int main() {
    int age;
    float height;
    char name[50]; // 字符数组用于存储字符串

    printf("请输入您的姓名: ");
    scanf("%s", name); // 读取字符串时,name是数组名,代表地址,不用&
                       // 注意:这里有缓冲区溢出风险!

    printf("请输入您的年龄和身高 (用空格隔开): ");
    scanf("%d %f", &age, &height); // 读取整数和浮点数,需要&

    printf("\n--- 您的信息 ---\n");
    printf("姓名: %s\n", name);
    printf("年龄: %d 岁\n", age);
    printf("身高: %.2f 米\n", height);

    // 更安全的字符串输入方式
    char safe_name[50];
    printf("\n再次输入您的姓名 (安全方式): ");
    // fgets会读取换行符,如果不需要可以处理掉
    if (fgets(safe_name, sizeof(safe_name), stdin) != NULL) {
        // 移除可能存在的换行符
        // size_t len = strlen(safe_name);
        // if (len > 0 && safe_name[len-1] == '\n') {
        //     safe_name[len-1] = '\0';
        // }
        printf("安全获取的姓名: %s\n", safe_name);
    }
    
    return 0;
}

关于scanf的返回值scanf返回成功读取并赋值的参数个数。如果发生错误或到达文件末尾,会返回EOF。检查scanf的返回值是一个好习惯。

4.3 字符输入输出

  • getchar():从标准输入读取一个字符,返回其ASCII码(int类型),或在出错/文件结束时返回EOF

  • putchar(int c):将字符c(实际是其ASCII码)输出到标准输出。

#include <stdio.h>

int main() {
    char ch;
    printf("请输入一个字符: ");
    ch = getchar(); // 读取一个字符

    printf("您输入的字符是: ");
    putchar(ch);    // 输出该字符
    putchar('\n');  // 输出换行

    return 0;
}

注意清空输入缓冲区:连续使用scanfgetchar时,scanf可能会留下未读取的换行符在缓冲区中,影响后续getchar的读取。例如 scanf("%d", &num); char c = getchar(); c可能会直接读到换行符。

五、程序的骨架:流程控制语句

流程控制语句决定了代码的执行顺序。

5.1 条件判断:if-else 语句

根据条件是否为真来执行不同的代码块。

if (条件1) {
    // 条件1为真时执行
} else if (条件2) {
    // 条件1为假,且条件2为真时执行
} else {
    // 以上条件都为假时执行
}

示例:判断一个数的奇偶性

#include <stdio.h>

int main() {
    int number;
    printf("请输入一个整数: ");
    scanf("%d", &number);

    if (number % 2 == 0) {
        printf("%d 是偶数。\n", number);
    } else {
        printf("%d 是奇数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值