C语言基础(一)

C语言基础(一)

文章目录

  • C语言基础(一)
    • [1. 什么是 C 语言?](#1. 什么是 C 语言?)
      • [1.1 语言概述](#1.1 语言概述)
      • [1.2 历史与标准(从 ANSI C 到 C11)](#1.2 历史与标准(从 ANSI C 到 C11))
      • [1.3 编译环境与常见编译器](#1.3 编译环境与常见编译器)
    • [2. 第一个 C 语言程序](#2. 第一个 C 语言程序)
      • [2.1 代码剖析](#2.1 代码剖析)
      • [2.2 运行流程:编译 → 链接 → 运行](#2.2 运行流程:编译 → 链接 → 运行)
    • [3. 数据类型(Data Types)](#3. 数据类型(Data Types))
      • [3.1 基本类型](#3.1 基本类型)
      • [3.2 类型的大小:使用 `sizeof`](#3.2 类型的大小:使用 sizeof)
      • [3.3 类型存在的意义](#3.3 类型存在的意义)
    • [4. 变量与常量](#4. 变量与常量)
    • [5. 字符串、转义字符与注释](#5. 字符串、转义字符与注释)
      • [5.1 字符串与 `\0`](#5.1 字符串与 \0)
      • [5.2 转义字符与 `strlen` 陷阱](#5.2 转义字符与 strlen 陷阱)
      • [5.3 注释](#5.3 注释)
    • [6. 选择语句:`if-else`](#6. 选择语句:if-else)
      • [6.1 基本结构](#6.1 基本结构)
    • [7. 循环语句:`while`](#7. 循环语句:while)
      • [7.1 `while` 循环](#7.1 while 循环)
    • [8. 函数(Functions)](#8. 函数(Functions))
      • [8.1 函数的定义](#8.1 函数的定义)
      • [8.2 函数的调用](#8.2 函数的调用)
      • [8.3 函数的意义](#8.3 函数的意义)
    • [9. 数组(Arrays)](#9. 数组(Arrays))
      • [9.1 数组定义](#9.1 数组定义)
      • [9.2 下标访问(从 0 开始)](#9.2 下标访问(从 0 开始))
      • [9.3 遍历数组](#9.3 遍历数组)
    • [10. 操作符(Operators)](#10. 操作符(Operators))
      • [10.1 算术操作符](#10.1 算术操作符)
      • [10.2 其他操作符(简要提及)](#10.2 其他操作符(简要提及))
    • [11. 常见关键字](#11. 常见关键字)
      • [11.1 关键字概览](#11.1 关键字概览)
      • [11.2 `typedef`:类型重命名](#11.2 typedef:类型重命名)
      • [11.3 `static`(静态)](#11.3 static(静态))
    • [12. `#define` 定义常量和宏](#define` 定义常量和宏)
      • [12.1 标识符常量](#12.1 标识符常量)
      • [12.2 宏(Macros)](#12.2 宏(Macros))
    • [13. 指针(内容深度)](#13. 指针(内容深度))
      • [13.1 内存与地址(The Foundation)](#13.1 内存与地址(The Foundation))
      • [13.2 指针变量(Pointer Variables)](#13.2 指针变量(Pointer Variables))
      • [13.3 解引用操作(Dereferencing)](#13.3 解引用操作(Dereferencing))
      • [13.4 指针的大小(Size of Pointers)](#13.4 指针的大小(Size of Pointers))
    • [14. 结构体(Struct)](#14. 结构体(Struct))
      • [14.1 为什么需要结构体](#14.1 为什么需要结构体)
      • [14.2 定义与初始化](#14.2 定义与初始化)
      • [14.3 成员访问:`.` 与 `->`](#14.3 成员访问:.->)

1. 什么是 C 语言?

1.1 语言概述

  • 底层开发

    C 语言可以非常直接地操作内存、位、字节等底层细节,适合写:

    • 操作系统内核
    • 驱动程序
    • 嵌入式程序(单片机、ARM 开发板等)
  • 高效率

    C 语言编译后生成的可执行文件体积小、运行速度快,开销很低。

    很多高性能场景(数据库、编译器、图形库等)仍然大量使用 C/C++。

  • 跨平台特性

    • C 语言本身是标准化的(ANSI C / C89 / C99 / C11 等)。
    • 同一份源代码,只要避免依赖特定平台的库函数和特性,就可以在不同操作系统(Windows / Linux / macOS)下通过对应编译器重新编译运行。

1.2 历史与标准(从 ANSI C 到 C11)

  • 早期 C:由 Dennis Ritchie 在 1970 年代为 UNIX 系统设计。
  • ANSI C(C89/C90)
    • 由 ANSI(美国国家标准协会)在 1989 年标准化,后来被 ISO 采纳为 C90。
    • 奠定了 C 语言的基础语法和标准库接口。
  • C99
    • 加入了 // 单行注释、long long、可变长数组、inline 等特性。
  • C11
    • 引入多线程支持(<threads.h>)、原子操作、对内存模型的规范等。
  • 实际开发中:
    • 很多项目仍以 C89/C99 为主,C11 支持情况取决于编译器版本。

1.3 编译环境与常见编译器

  • GCC(GNU Compiler Collection)
    • 常见于 Linux / macOS。
    • 命令行使用:gcc main.c -o main
  • Clang
    • 由 LLVM 项目提供,强调模块化和更友好的错误信息。
    • 在 macOS 上经常默认使用。
    • 命令行:clang main.c -o main
  • MSVC(Microsoft Visual C++)
    • Windows 平台上 Visual Studio 自带的编译器。
    • 通常通过 IDE(图形界面)进行编译,也可以用命令行工具 cl.exe

2. 第一个 C 语言程序

2.1 代码剖析

示例代码:

c 复制代码
#include <stdio.h>

int main(void)
{
    printf("Hello, C!\n");
    return 0;
}
  • #include <stdio.h>
    • 预处理指令,表示"把标准输入输出库的声明包含进来"。
    • stdio.h 中包含了 printf 等函数的声明。
  • int main(void)
    • 程序入口函数 ,操作系统运行程序时,会从 main 函数开始执行。
    • 返回类型是 int,表示程序结束时会返回一个整数状态码给操作系统。
  • { ... }
    • 花括号内是 main 函数的函数体,写的是程序实际要执行的语句。
  • printf("Hello, C!\n");
    • 调用标准库函数 printf 打印字符串。
    • \n 是换行符(转义字符)。
  • return 0;
    • 返回值 0 通常表示程序正常结束
    • 如果返回非 0,就常被用来表示出现错误。

2.2 运行流程:编译 → 链接 → 运行

  1. 预处理(Preprocessing)
    • 处理 #include#define 等预处理指令,生成"预处理后的源代码"。
  2. 编译(Compile)
    • 将 C 源代码编译为目标文件(机器码,但是还没有连到一起)。
  3. 链接(Link)
    • 把多个目标文件和库文件链接成一个最终可执行文件
    • 解决函数、全局变量的"符号引用"。
  4. 运行(Run)
    • 操作系统加载可执行文件到内存,找到 main 函数,开始执行。

3. 数据类型(Data Types)

3.1 基本类型

常见的基本数据类型:

  • 整型
    • charshortintlonglong long
    • 有符号与无符号:signed / unsigned
  • 浮点型
    • float(单精度)
    • double(双精度)
    • long double(扩展精度)
  • 字符型
    • char 存放一个字符(本质上是一个整数,通常 1 字节)。
    • 例如:char c = 'A';

3.2 类型的大小:使用 sizeof

  • sizeof 用来获取某种类型或某个变量占用的字节数
c 复制代码
printf("int: %zu bytes\n", sizeof(int));
printf("double: %zu bytes\n", sizeof(double));
printf("char: %zu bytes\n", sizeof(char));
  • 不同平台、不同编译器,某些类型的大小可能会有差异(尤其是 intlong 等)。

3.3 类型存在的意义

  • 内存利用率
    • char 存储只需要 0~127 的编码就足够了,如果用 int 会浪费空间。
  • 数值范围
    • 需要存储很大的数字时,就要用 long longdouble
  • 性能与语义
    • 选择合适的类型可以提升性能,也更清晰表达变量的用途。

4. 变量与常量

关键点:变量要理解"作用域 (在哪里可见)"和"生命周期(能活多久)";常量用来表示不能被修改的值。

4.1 变量(Variables)

定义与初始化

  • 定义:指定类型 + 变量名

    c 复制代码
    int a;           // 定义一个 int 变量 a
    int b = 10;      // 定义并初始化
    double pi = 3.14;
  • 命名规则

    • 只能由字母、数字和下划线组成,不能以数字开头。
    • 不能使用关键字(如 intreturnif 等)。
    • 尽量使用有意义的名字:count, sum, index 等。

变量的分类

  • 局部变量(local variable)
    • 定义在函数内部或代码块内部 {} 中。
    • 典型存储在栈区
    • 作用域:从定义处到所在代码块结束。
    • 当程序执行离开这个代码块时,局部变量就会被销毁。
  • 全局变量(global variable)
    • 定义在所有函数外面。
    • 存储在静态区
    • 作用域:通常为从定义位置开始到整个源文件结束,如果加上 extern 可以在其他文件中使用。
    • 生命周期:程序从开始运行到结束。
  • 同名时的"局部优先原则"
    • 如果局部变量和全局变量同名,在该局部变量的作用域内,局部变量会屏蔽全局变量

核心属性:作用域与生命周期

  • 作用域(Scope)
    • 变量"在哪些代码范围内可以被访问":
      • 块作用域(函数体、ifwhile{}
      • 文件作用域(全局变量)
  • 生命周期(Lifecycle)
    • 变量在程序执行过程中"从什么时候开始存在,到什么时候消亡":
      • 局部变量:进入代码块时创建,离开时销毁。
      • 全局变量 / 静态变量:程序运行期间一直存在。

4.2 常量(Constants)

字面常量

  • 写在代码里的直接数值:
    • 整型字面量:100, -5
    • 浮点字面量:3.14, 0.5
    • 字符字面量:'A', '\n'
    • 字符串字面量:"hello"

const 修饰的常变量

c 复制代码
const int MAX_SIZE = 100;
  • 表达"这个变量不允许被修改"。
  • 本质仍是变量:有地址、有存储空间,有类型。
  • 编译器对于修改 MAX_SIZE 的行为会报错或警告。

#define 定义的标识符常量

c 复制代码
#define PI 3.14159
#define MAX_SIZE 100
  • 属于预处理阶段的文本替换
  • 没有类型概念,只是在编译前,把所有 PI 替换为 3.14159
  • 写法上常用全大写表示宏常量。

枚举常量 enum

c 复制代码
enum Color {
    RED,    // 0
    GREEN,  // 1
    BLUE    // 2
};
  • 一组逻辑相关的常量,默认从 0 开始递增。
  • 也可以指定初始值:
c 复制代码
enum Weekday {
    MON = 1,
    TUE,
    WED
};

5. 字符串、转义字符与注释

5.1 字符串与 \0

  • 在 C 中没有"独立的字符串类型",字符串通常用字符数组 表示,并以 '\0' 结尾:
c 复制代码
char str1[] = "hello";   // 实际上是 {'h','e','l','l','o','\0'}
  • '\0' 是字符串结束标志。没有 '\0' 的字符数组,很多字符串函数会"一直往后读",直到在内存中遇到某个 \0 为止,可能导致:
    • 打印出奇怪的字符
    • 访问越界,甚至程序崩溃

5.2 转义字符与 strlen 陷阱

常见转义字符:

  • \n:换行
  • \t:水平制表符(Tab)
  • \\:反斜杠
  • \':单引号
  • \":双引号
  • \0:字符串结束符

strlen 的行为:

c 复制代码
#include <string.h>

char str[] = "ab\nc";
size_t len = strlen(str); // 计算不包括 '\0'
  • strlen 会从起始地址开始,一直数到第一次遇到 '\0' 为止。
  • 如果数组中没有 '\0'strlen 会继续向后读到未知区域,结果不可预期。

5.3 注释

  • C 风格注释(块注释)/* ... */
    • 可以跨多行
    • 不支持嵌套
  • C++ 风格注释(单行注释)//
    • // 起到本行末尾都是注释。
    • C99 及以后标准支持 //

6. 选择语句:if-else

6.1 基本结构

c 复制代码
if (condition) {
    // 条件为真时执行
} else {
    // 条件为假时执行
}
  • 条件 condition 为"非 0"时视为 ,为 0 视为
  • 可以有多分支:
c 复制代码
if (score >= 90) {
    printf("A\n");
} else if (score >= 60) {
    printf("B\n");
} else {
    printf("C\n");
}

7. 循环语句:while

7.1 while 循环

c 复制代码
while (condition) {
    // 当 condition 为真时,重复执行这段代码
}

示例:计算 1 到 5 的和

c 复制代码
int i = 1;
int sum = 0;

while (i <= 5) {
    sum += i;
    i++;
}
printf("sum = %d\n", sum);
  • 进入循环前会先判断条件。
  • 每次循环结束后,再次判断条件决定是否继续。

8. 函数(Functions)

8.1 函数的定义

一个典型函数的格式:

c 复制代码
返回类型 函数名(参数列表)
{
    // 函数体
}

示例:

c 复制代码
int add(int a, int b)
{
    return a + b;
}

8.2 函数的调用

c 复制代码
int result = add(3, 5);  // 调用 add 函数
printf("%d\n", result);  // 输出 8
  • 传参:把实际的值(实参)传递给函数定义中的形参。
  • 返回值 :通过 return 把结果返回给调用者。

8.3 函数的意义

  • 模块化:把一个大问题拆解成多个小函数,便于理解与维护。
  • 代码复用:减少重复代码,提高可读性。

9. 数组(Arrays)

9.1 数组定义

c 复制代码
int arr[5];          // 定义一个包含 5 个 int 元素的数组
int arr2[3] = {1,2,3};
  • 数组是"一组相同类型元素的集合",在内存中是连续存放的。

9.2 下标访问(从 0 开始)

  • 第一个元素下标为 0,最后一个元素下标为 n-1
c 复制代码
arr[0] = 10;
arr[1] = 20;
  • 访问越界(例如 arr[5],当数组长度为 5 时)会导致未定义行为。

9.3 遍历数组

通常结合循环:

c 复制代码
int arr[5] = {1, 2, 3, 4, 5};
int i = 0;

while (i < 5) {
    printf("%d ", arr[i]);
    i++;
}

10. 操作符(Operators)

10.1 算术操作符

  • +:加
  • -:减
  • *:乘
  • /:除
  • %:取模(余数,只能用于整数)

示例:

c 复制代码
int a = 7, b = 3;
printf("%d\n", a / b);  // 2(整数除法,结果为整数)
printf("%d\n", a % b);  // 1(余数)

10.2 其他操作符(简要提及)

  • 关系操作符:==, !=, <, >, <=, >=
  • 逻辑操作符:&&, ||, !
  • 自增自减:++, --
  • 位操作符:&, |, ^, ~, <<, >>
  • 条件操作符:?:

(这些可以在后续进阶时再深入)


11. 常见关键字

11.1 关键字概览

C 语言保留了一些有特殊含义的单词,如:

  • int, char, double, void
  • if, else, while, for, switch, case, default
  • return, break, continue
  • struct, union, enum, typedef, static, extern

这些单词不能作为变量名或函数名使用。

11.2 typedef:类型重命名

c 复制代码
typedef unsigned int uint;
typedef struct Student {
    char name[20];
    int age;
} Student;
  • 之后可以用 uint 代替 unsigned int,用 Student 直接表示结构体类型。

11.3 static(静态)

  • 修饰局部变量
c 复制代码
void func(void)
{
    static int count = 0;  // 生命周期为整段程序运行期
    count++;
    printf("%d\n", count);
}
  • static 局部变量:
    • 仍然只在函数内部可见(作用域不变)。
    • 但生命周期从程序开始到结束(不会随着函数退出而销毁)。
  • 修饰全局变量 / 函数
    • 作用:改变"链接属性",限制其只在当前源文件内可见(内部链接)。
    • 可以避免全局名字在多文件工程中冲突。

12. #define 定义常量和宏

12.1 标识符常量

c 复制代码
#define MAX 100
#define PI 3.14159
  • 纯粹的文本替换,没有类型检查。
  • 用来统一管理一些不易变化的"魔法数字"。

12.2 宏(Macros)

  • 带参数的宏本质上是一种文本模板:
c 复制代码
#define SQUARE(x) ((x) * (x))
  • 使用时:int r = SQUARE(5); → 预处理阶段变成 ((5) * (5))
  • 要注意用括号包裹参数和整体表达式,避免优先级坑。

13. 指针(内容深度)

从"内存与地址"理解指针:
地址 是门牌号,指针变量 是专门存门牌号的变量,解引用是拿着门牌号去房间里找东西。

13.1 内存与地址(The Foundation)

  • 内存单元
    • 内存被划分为一个个字节(Byte),通常 1 字节 = 8 bit。
  • 地址(Address)
    • 每个字节有一个唯一的编号,就像"门牌号"。
    • 指针本质上就是存这个编号的变量。
  • 取地址操作符 &
c 复制代码
int a = 10;
int *p = &a;   // &a 表示"a 在内存中的地址"

13.2 指针变量(Pointer Variables)

  • 定义形式:类型 *指针名
c 复制代码
int *p;      // p 是一个指向 int 的指针
double *q;   // q 是一个指向 double 的指针
  • 类型匹配的意义
    • int * 表示"这个指针指向的是一个 int 类型的对象"。
    • 编译器需要通过"指向的类型"来决定:
      • 解引用时读取多少字节
      • 指针运算(p + 1 跳过多少字节)

所以,int 的地址就应该存到 int * 里,double 的地址存到 double * 里。

13.3 解引用操作(Dereferencing)

  • 操作符:*
c 复制代码
int a = 10;
int *p = &a;

printf("%d\n", *p);  // 通过指针访问 a 的值
*p = 20;             // 通过指针修改 a 的值
  • 含义:
    • p 是"存放地址的变量"。
    • *p 是"沿着地址去找到那个变量本身"。

13.4 指针的大小(Size of Pointers)

  • 在同一平台上,所有指针的大小通常是相同的:
    • 32 位系统:指针通常是 4 字节。
    • 64 位系统:指针通常是 8 字节。
c 复制代码
printf("%zu\n", sizeof(int *));
printf("%zu\n", sizeof(double *));
printf("%zu\n", sizeof(char *));
  • 原因
    • 指针大小由 CPU 的地址总线宽度决定,即"可以表达多大的地址空间"。
    • 与"指向的数据类型"无关。

14. 结构体(Struct)

14.1 为什么需要结构体

  • 现实中的对象往往有多个属性,比如"学生":
    • 姓名(字符串)
    • 年龄(整数)
    • 分数(浮点数)
  • 如果用多个分散的变量:name, age, score,很难整体传递和管理。
  • 结构体就是把一组相关的不同类型的数据"打包"成一个整体。

14.2 定义与初始化

c 复制代码
struct Student {
    char name[20];
    int age;
    double score;
};

struct Student s1 = {"Alice", 18, 95.5};
  • struct 是关键字,Student 是结构体标签名。
  • 定义变量时可以直接初始化。

14.3 成员访问:.->

  • 结构体变量通过 . 访问成员:
c 复制代码
struct Student s;
s.age = 20;
  • 结构体指针通过 -> 访问成员:
c 复制代码
struct Student *ps = &s;
ps->age = 21;       // 等价于 (*ps).age = 21;

相关推荐
郝学胜-神的一滴37 分钟前
深入理解Linux中的Try锁机制
linux·服务器·开发语言·c++·程序人生
liliangcsdn37 分钟前
bash中awk如何切分输出
开发语言·bash
无限码力38 分钟前
华为OD机试真题双机位C卷 【运维日志排序】C语言实现
c语言·华为od·华为od机考·华为od机试真题·华为od机试双机位c卷·华为od机考双机位c卷·华为od上机考试
csbysj202043 分钟前
JSON.parse() 方法详解
开发语言
奔波霸的伶俐虫1 小时前
redisTemplate.opsForList()里面方法怎么用
java·开发语言·数据库·python·sql
小郭团队1 小时前
未来PLC会消失吗?会被嵌入式系统取代吗?
c语言·人工智能·python·嵌入式硬件·架构
yesyesido1 小时前
智能文件格式转换器:文本/Excel与CSV无缝互转的在线工具
开发语言·python·excel
_200_1 小时前
Lua 流程控制
开发语言·junit·lua
环黄金线HHJX.1 小时前
拼音字母量子编程PQLAiQt架构”这一概念。结合上下文《QuantumTuan ⇆ QT:Qt》
开发语言·人工智能·qt·编辑器·量子计算
王夏奇1 小时前
python在汽车电子行业中的应用1-基础知识概念
开发语言·python·汽车