当前位置: 首页 > news >正文

C/C++核心知识点详解教程

C/C++核心知识点详解教程

本教程从零开始,系统讲解C/C++语言中的关键概念,适合初学者循序渐进地学习。


目录

  1. 宏定义与预处理
  2. volatile关键字详解
  3. static关键字详解
  4. const关键字详解
  5. 指针与const的组合
  6. new/delete与malloc/free对比
  7. sizeof运算符
  8. 结构体与联合体
  9. 内存布局:栈与堆
  10. extern "C"的作用

1. 宏定义与预处理

1.1 什么是宏定义?

宏定义是C/C++预处理器的功能,在编译前进行文本替换。

基本语法:

#define 宏名 替换文本

1.2 简单宏示例

#define PI 3.14159
#define MAX_SIZE 100int main() {double radius = 5.0;double area = PI * radius * radius;  // 编译时替换为 3.14159return 0;
}

1.3 带参数的宏

#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))int result = SQUARE(5);      // 展开为 ((5) * (5))
int max_val = MAX(10, 20);   // 展开为 ((10) > (20) ? (10) : (20))

注意事项:

  • 参数要加括号,避免运算优先级问题
  • 整个表达式也要加括号

1.4 #和##操作符

# (字符串化操作符)

#define TO_STRING(x) #xTO_STRING(hello)  // 展开为 "hello"

## (连接操作符)

#define CONCAT(a, b) a##bint xy = 10;
int value = CONCAT(x, y);  // 展开为 xy,即访问变量xy

1.5 宏的陷阱

#define SQUARE(x) x * xint a = SQUARE(2 + 3);  // 展开为 2 + 3 * 2 + 3 = 11 (错误!)
// 正确写法: #define SQUARE(x) ((x) * (x))

2. volatile关键字详解

2.1 什么是volatile?

volatile告诉编译器:“这个变量可能会意外改变,不要对它进行优化”。

简单理解: 每次使用这个变量时,都要从内存中重新读取,而不是使用缓存值。

2.2 为什么需要volatile?

编译器为了提高效率,可能会把变量缓存到寄存器中:

int flag = 0;// 编译器可能优化为无限循环
while (flag == 0) {// 编译器认为flag不会改变,只读取一次
}

如果其他线程或硬件改变了flag,主循环可能永远检测不到!

2.3 使用场景

场景1: 硬件寄存器
volatile unsigned int *reg = (unsigned int *)0x40000000;*reg = 0x01;  // 写入硬件寄存器
*reg = 0x02;  // 再次写入
*reg = 0x04;  
*reg = 0x08;  // 不加volatile,编译器可能只保留最后一次写入
// 加了volatile,四次写入都会执行
场景2: 中断服务程序
volatile int interrupt_flag = 0;void interrupt_handler() {interrupt_flag = 1;  // 中断中修改
}int main() {while (interrupt_flag == 0) {// 等待中断}// 处理中断后的逻辑
}
场景3: 多线程共享变量
volatile bool is_running = true;// 线程1
void thread1() {while (is_running) {// 执行任务}
}// 线程2
void thread2() {is_running = false;  // 停止线程1
}

2.4 关键要点

  • volatile 不保证原子性,只保证可见性
  • 多线程编程应配合互斥锁等同步机制
  • volatile主要用于与硬件交互和信号处理

3. static关键字详解

static在不同位置有不同含义,我们逐一讲解。

3.1 函数内的static变量

特点: 只初始化一次,保持值不变

void counter() {static int count = 0;  // 只在第一次调用时初始化count++;printf("调用次数: %d\n", count);
}int main() {counter();  // 输出: 1counter();  // 输出: 2counter();  // 输出: 3return 0;
}

普通变量对比:

void counter_normal() {int count = 0;  // 每次调用都重新初始化count++;printf("调用次数: %d\n", count);  // 始终输出1
}

3.2 文件作用域的static变量

作用: 限制变量只能在当前文件中使用

// file1.c
static int internal_var = 100;  // 只能在file1.c中访问void func() {internal_var++;  // 可以访问
}
// file2.c
extern int internal_var;  // 错误!无法访问file1.c中的static变量

3.3 static函数

作用: 限制函数只能在当前文件中调用

// utils.c
static void helper_function() {// 内部辅助函数
}void public_function() {helper_function();  // 可以调用
}
// main.c
extern void helper_function();  // 错误!无法访问static函数

3.4 为什么static变量只初始化一次?

static变量存储在静态存储区(非栈区),程序运行期间一直存在:

变量类型存储位置生命周期
普通局部变量函数调用期间
static局部变量静态区整个程序运行期间
全局变量静态区整个程序运行期间

4. const关键字详解

4.1 基本用法

const表示"只读",修饰的内容不能被修改。

const int MAX = 100;
MAX = 200;  // 错误!const变量不能修改

4.2 const vs #define

特性const#define
类型检查
作用域遵循作用域规则全局替换
调试可调试难以调试
内存分配内存不分配(编译时替换)

示例:

#define PI 3.14
const double PI_CONST = 3.14;double area1 = PI * 5 * 5;        // 每次使用都替换
double area2 = PI_CONST * 5 * 5;  // 只有一个变量

4.3 const修饰函数参数

防止函数内部意外修改参数:

void print_string(const char *str) {// str[0] = 'A';  // 错误!不能修改printf("%s\n", str);  // 可以读取
}

4.4 const修饰函数返回值

const char* get_name() {return "Alice";
}int main() {const char *name = get_name();// name[0] = 'B';  // 错误!返回的字符串不能修改return 0;
}

4.5 const数组

const int arr[5] = {1, 2, 3, 4, 5};
// arr[0] = 10;  // 错误!

5. 指针与const的组合

这是最容易混淆的部分,我们用口诀记忆。

5.1 四种组合方式

int value = 10;// 1. 指向常量的指针 (pointer to const)
const int *p1 = &value;
// *p1 = 20;  // 错误!不能通过p1修改value
p1 = &other;  // 正确!可以改变指针指向// 2. 常量指针 (const pointer)
int * const p2 = &value;
*p2 = 20;     // 正确!可以修改value
// p2 = &other;  // 错误!不能改变指针指向// 3. 指向常量的常量指针
const int * const p3 = &value;
// *p3 = 20;     // 错误!不能修改value
// p3 = &other;  // 错误!不能改变指针指向// 4. 普通指针
int *p4 = &value;
*p4 = 20;     // 正确!
p4 = &other;  // 正确!

5.2 记忆技巧

“const在星号左边,指向的内容不能变;const在星号右边,指针本身不能变”

const int *p;     // const在*左边 → 内容不能变
int * const p;    // const在*右边 → 指针不能变
const int * const p;  // 都不能变

5.3 实际应用场景

// 函数不会修改字符串内容
void print_message(const char *msg) {printf("%s\n", msg);
}// 函数不会改变指针指向(但可能修改内容)
void modify_value(int * const ptr) {*ptr = 100;  // 可以修改
}

6. new/delete与malloc/free对比

6.1 基本区别

特性new/deletemalloc/free
语言C++运算符C语言函数
返回类型具体类型指针void*
大小计算自动手动指定
构造/析构自动调用不调用
失败处理抛出异常返回NULL

6.2 使用示例

malloc/free (C语言):

int *p = (int *)malloc(sizeof(int) * 10);  // 分配10个int
if (p == NULL) {// 分配失败处理
}
free(p);  // 释放内存

new/delete (C++):

int *p = new int[10];  // 自动计算大小
delete[] p;            // 释放数组int *single = new int(5);  // 分配单个int并初始化为5
delete single;

6.3 对象的构造和析构

class MyClass {
public:MyClass() { cout << "构造函数" << endl; }~MyClass() { cout << "析构函数" << endl; }
};// 使用new - 会调用构造函数和析构函数
MyClass *obj1 = new MyClass();
delete obj1;// 使用malloc - 不会调用构造和析构
MyClass *obj2 = (MyClass *)malloc(sizeof(MyClass));
free(obj2);  // 危险!没有调用析构函数

6.4 关键要点

  • C++中优先使用new/delete
  • new配对delete,new[]配对delete[]
  • 不要混用newfree,或mallocdelete

7. sizeof运算符

7.1 sizeof是什么?

sizeof编译时运算符(不是函数!),返回类型或变量的字节大小。

7.2 基本用法

printf("%zu\n", sizeof(int));        // 4 (32/64位系统)
printf("%zu\n", sizeof(char));       // 1
printf("%zu\n", sizeof(double));     // 8int arr[10];
printf("%zu\n", sizeof(arr));        // 40 (10 * 4)

7.3 sizeof vs strlen

char str[] = "hello";sizeof(str);   // 6 (包括'\0')
strlen(str);   // 5 (不包括'\0')

关键区别:

特性sizeofstrlen
计算时机编译时运行时
作用对象任何类型字符串
是否包含’\0’

7.4 指针的sizeof

int arr[10];
int *p = arr;sizeof(arr);  // 40 (数组大小)
sizeof(p);    // 8 (64位系统指针大小) 或 4 (32位)

易错点:

void func(int arr[]) {// arr实际是指针!int size = sizeof(arr);  // 错误!得到的是指针大小,不是数组大小
}

7.5 结构体的sizeof

struct Example {char a;    // 1字节int b;     // 4字节char c;    // 1字节
};sizeof(struct Example);  // 可能是12,不是6!

原因:字节对齐,下一节详细讲解。


8. 结构体与联合体

8.1 结构体 (struct)

特点: 所有成员各自占用独立内存

struct Student {char name[20];  // 20字节int age;        // 4字节float score;    // 4字节
};  // 总大小 = 28字节(可能因对齐而变化)

8.2 联合体 (union)

特点: 所有成员共享同一块内存

union Data {int i;      // 4字节float f;    // 4字节char c;     // 1字节
};  // 总大小 = 4字节(最大成员的大小)

示例:

union Data d;
d.i = 10;
printf("%d\n", d.i);  // 10d.f = 3.14;
printf("%f\n", d.f);  // 3.14
printf("%d\n", d.i);  // 未定义!被覆盖了

8.3 内存布局对比

struct S {int a;char b;double c;
};union U {int a;char b;double c;
};sizeof(struct S);  // 16 (字节对齐)
sizeof(union U);   // 8  (最大成员double的大小)

8.4 字节对齐规则

编译器为了提高访问效率,会对结构体成员进行对齐:

规则:

  1. 每个成员按其类型大小对齐
  2. 结构体总大小是最大成员的整数倍
struct Example1 {char a;     // 偏移0, 占1字节// 填充3字节int b;      // 偏移4, 占4字节char c;     // 偏移8, 占1字节// 填充3字节
};  // 总大小12字节struct Example2 {char a;     // 偏移0char c;     // 偏移1// 填充2字节int b;      // 偏移4
};  // 总大小8字节 (优化后)

8.5 联合体的应用场景

场景1: 节省内存

struct Packet {int type;union {int int_data;float float_data;char str_data[20];} data;  // 根据type决定使用哪个成员
};

场景2: 类型转换

union FloatBits {float f;unsigned int bits;
};union FloatBits fb;
fb.f = 3.14;
printf("0x%X\n", fb.bits);  // 查看浮点数的二进制表示

9. 内存布局:栈与堆

9.1 内存分区

C程序的内存分为几个区域:

高地址
+------------------+
|      栈 (Stack)   |  ← 局部变量、函数参数
+------------------+  ↓ 向下增长
|        ↓         |
|                  |
|        ↑         |
+------------------+  ↑ 向上增长
|      堆 (Heap)    |  ← 动态分配内存
+------------------+
| 全局/静态区       |  ← 全局变量、static变量
+------------------+
|   常量区          |  ← 字符串常量
+------------------+
|   代码区          |  ← 程序代码
+------------------+
低地址

9.2 栈 (Stack)

特点:

  • 自动管理,函数结束自动释放
  • 容量有限(通常几MB)
  • 速度快
  • 存储局部变量
void func() {int a = 10;        // 栈上分配char str[100];     // 栈上分配
}  // 函数结束,自动释放

9.3 堆 (Heap)

特点:

  • 手动管理(malloc/free 或 new/delete)
  • 容量大(受系统内存限制)
  • 速度较慢
  • 适合大数据或生命周期不确定的数据
void func() {int *p = (int *)malloc(1000 * sizeof(int));  // 堆上分配// 使用p...free(p);  // 必须手动释放!
}

9.4 常见错误

1. 栈溢出

void func() {int huge_array[1000000];  // 太大!可能栈溢出
}

2. 内存泄漏

void func() {int *p = (int *)malloc(100);// 忘记free(p)!  → 内存泄漏
}

3. 悬空指针

int* func() {int a = 10;return &a;  // 危险!返回栈上变量的地址
}  // a已释放,返回的指针无效

9.5 栈的多线程应用

每个线程都有独立的栈:

// 线程1
void thread1() {int local1 = 1;  // 线程1的栈
}// 线程2
void thread2() {int local2 = 2;  // 线程2的栈
}

10. extern "C"的作用

10.1 问题背景

C++支持函数重载,C语言不支持:

// C++可以这样
void print(int x);
void print(double x);
void print(const char *x);

编译器通过名字修饰(Name Mangling)区分:

  • print(int)_Z5printi
  • print(double)_Z5printd

但C语言编译器不做名字修饰:

  • printprint

10.2 extern "C"的作用

告诉C++编译器:“这段代码按C语言方式编译”

// C++文件中
extern "C" {void c_function();  // 按C方式编译
}// 等价于
extern "C" void c_function();

10.3 实际应用场景

场景1: C++调用C库

// math_lib.h (C语言库)
#ifdef __cplusplus
extern "C" {
#endifvoid calculate(int a, int b);#ifdef __cplusplus
}
#endif

场景2: 提供C接口给其他语言

// my_library.cpp
extern "C" {int add(int a, int b) {return a + b;  // C接口}
}

10.4 为什么需要条件编译?

#ifdef __cplusplus
extern "C" {
#endif// 函数声明#ifdef __cplusplus
}
#endif
  • C编译器不认识extern "C",需要条件编译
  • __cplusplus是C++编译器预定义的宏

总结与学习路径

关键知识点回顾

  1. 预处理: 宏定义在编译前进行文本替换
  2. volatile: 防止编译器优化,用于硬件和多线程
  3. static: 控制变量生命周期和作用域
  4. const: 定义只读数据,提高代码安全性
  5. 指针+const: 记住口诀"const在星号哪边,哪边就不能变"
  6. 内存管理: new/delete(C++) vs malloc/free©
  7. sizeof: 编译时计算大小
  8. 结构体vs联合体: 独立内存 vs 共享内存
  9. 栈vs堆: 自动管理 vs 手动管理
  10. extern “C”: 实现C/C++互操作

学习建议

  1. 多敲代码: 每个知识点都要亲手实践
  2. 看编译结果: 使用gcc -E查看预处理结果
  3. 调试验证: 用GDB等工具观察内存布局
  4. 循序渐进: 先掌握基础,再学习高级特性
  5. 阅读经典书籍:
    • 《C Primer Plus》
    • 《C++ Primer》
    • 《深入理解计算机系统》

练习建议

// 练习1: 写一个安全的字符串拷贝函数
void safe_strcpy(char *dest, const char *src, size_t size);// 练习2: 实现一个简单的内存池
typedef struct MemPool {char *memory;size_t size;size_t used;
} MemPool;// 练习3: 理解字节对齐
struct AlignTest {char a;int b;short c;
};
// 计算sizeof并画出内存布局
http://www.dtcms.com/a/431779.html

相关文章:

  • 保定网站建设方案咨询网站开发工程师 课程大纲
  • react打包优化和配置优化都有哪些?
  • AI行业应用:金融、医疗、教育、制造业的落地实践与技术创新
  • 岱山县网站建设网站建设 点指成名
  • 使用CommandLineRunner应该注意什么
  • 网站过度优化的表现word可以做网页吗
  • 网站建设客户需求分析国内做会展比较好的公司
  • 延安网站建设费用html编程教程
  • 十一 第一修联想M7400
  • 用Python打造专属本地贾维斯:声纹识别+离线交互,隐私安全不依赖云端
  • 智能体长记忆解决方案Mem0和Memobase
  • 健康网站模板正规电商平台
  • 网站布局内容tomcat wordpress
  • LeetCode 刷题【94. 二叉树的中序遍历、95. 不同的二叉搜索树 II】
  • 【代码随想录day 31】 力扣 738.单调递增的数字
  • 上海网站设计 企业有赞微商城入口
  • 【Leetcode高效算法】用双指针策略打破有效三角形的个数
  • 浏览器为什么打不开网站wordpress搭建多人博客
  • 牛客算法刷题noob57 凯撒加密
  • 计算机类毕业设计开题报告注意事项
  • Qt QML创建多线程(示例存读数据库)
  • 2026届计算机毕业设计选题推荐
  • 邹城市网站建设长春网站建设方案外包
  • 合肥建公司网站万户信息 做网站怎么样
  • 第十篇:告别new和delete:RAII机制与智能指针导论
  • 做搜狗pc网站优化快速深圳企业模板建站
  • 深度学习第十章 循环神经网络
  • 设计一个外贸网站需要多少钱wordpress 博客地址更改
  • ASP网站建设实训报告总结大德通众包做网站怎么样
  • 查询网站死链接温州百度快速排名优化