【AI小智硬件程序(二)】
int age;}People_T;因为 app_main 函数比较特殊,是系统自动会调用这个函数名称,而 C++ 编译器会将函数名重新修饰,这会导致系统找不到这个 app_main 函数名,加了 extern “C” 告诉编译器这个函数安装 C 的标准走不要对他进行函数名修饰\n");while (1)
AI小智硬件程序(二)
链接: B站Up
C程序基本语法
1.C 程序的基本结构
#include <stdio.h> // 头文件(输入输出库)
int app_main() { // 主函数,程序入口
printf("Hello, World!\n"); // 打印语句
return 0; // 返回状态码(0表示成功)
}
2.变量与数据类型
| 类型 | 示例 | 说明 |
|---|---|---|
| int | int a = 10; |
整型(4字节) |
| float | float b = 3.14; |
单精度浮点型(4字节) |
| double | double c = 3.14159; |
双精度浮点型(8字节) |
| char | char d = 'A'; |
字符型(1字节) |
| bool | bool f = true; |
布尔类型,不是真就是假只有两个值 |
变量声明与赋值
int age = 20; // 声明并赋值
float price;
price = 99.8; // 先声明后赋值
常量
const int DAYS = 7; // 不可修改的常量
#define PI 3.14159 // 宏定义(无分号)
3.运算符

4.控制流
条件语句
// if-else
if (score >= 60) {
printf("Pass!\n");
} else {
printf("Fail!\n");
}
// switch-case
switch (grade) {
case 'A': printf("Excellent!"); break;
case 'B': printf("Good!"); break;
default: printf("Invalid grade.");
}
循环语句
// for 循环
for (int i = 0; i < 5; i++) {
printf("%d ", i); // 输出 0 1 2 3 4
}
// while 循环
int count = 0;
while (count < 3) {
printf("Count: %d\n", count);
count++;
}
5.数组
int numbers[5] = {1, 2, 3, 4, 5}; // 声明并初始化
printf("%d", numbers[0]); // 访问第一个元素(下标从0开始)
// 遍历数组
for (int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
6.函数
// 函数定义
int add(int a, int b) {
return a + b;
}
// 函数调用
int result = add(3, 5);
printf("Sum: %d", result);
7.指针(基础概念)
int var = 10;
int *ptr = &var; // ptr指向var的地址
printf("Value: %d\n", *ptr); // 通过指针访问值(输出10)
用途:
动态内存分配(如 malloc)。
函数参数传递(修改实参的值)。
8.结构体(自定义类型)
typedef struct {
int age;
char name[20];
}People_T;
People_T peopleT = {.age = 18, .name = "zhangsan"};
void app_main(void)
{
peopleT.age = 20;
printf("peopleT.age = %d\n", peopleT.age);
}
C++基本语法
1.C++程序结构
#include <iostream> // 输入输出流头文件
int num=10;
int main() { // 主函数
std::cout << "我有" << num << "个苹果" << std::endl; // 输出
return 0;
}
2.变量与数据类型
基本类型(扩展自 C):string 字符串类型
示例:string s = “C++”;
类型推断(C++11 新增功能)
auto x = 10; // 自动推断为 int
auto y = 3.14; // 自动推断为 double
3.运算符(新增)
| 运算符 | 用途 | 示例 |
|---|---|---|
| :: | 作用域解析 | std::cout |
| new | 动态内存管理 | int* p = new int; |
| delete | 动态内存管理 | |
| << | 流操作符 | cout << x; |
| >> | 流操作符 |
4.控制流(与 C 相同)
条件语句: if-else, switch-case
循环语句: for、while, do-while
范围 for 循环(C++11):
int arr[] = {1, 2, 3};
for (int num : arr) { // 遍历数组
cout << num << " ";
}
C++核心特性
1.函数增强
函数重载(Overloading)
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; } // 同名函数,参数不同
规则:函数名相同,参数类型或数量不同。
2.面向对象(OOD)
类与对象
class People { // 类定义
private:
string name; // 私有成员
int age;
public:
void setName(string n) { name = n; } // 公有方法
string getName() { return name; }
void setAge(int a)
{
if(a<0){
return;
}
age = a;
} // 公有方法
string getAge() { return age; }
};
People people = People(); // 创建对象
people.setName("Alice");
people.setAge(-1);//不会生效
cout << people.getName(); // 输出 "Alice"
这段代码是 C++ 面向对象编程(OOP)的基础示例,核心展示了 “类(Class)” 与 “对象(Object)” 的定义和使用,以及 OOP 核心特性 ——封装的实现方式。代码通过定义People类抽象出 “人” 的属性(姓名、年龄)和行为(设置 / 获取属性),并通过访问权限控制保护数据安全,最终创建类的实例(对象)完成属性的操作与校验。
构造函数和析构函数
class Box {
public:
Box() { cout << "对象创建了"; } // 对象创建时自动调用,可以放些初始化函数
~Box() { cout << "对象销毁了"; } // 对象销毁时自动调用,可以释放资源
};
构造函数(Box()):
对象创建时自动执行,专门用来做初始化(比如给成员变量赋值、申请内存),这段代码里只是打印 “对象创建了”。
析构函数(~Box()):
对象销毁时自动执行,专门用来做清理(比如释放内存、关闭文件),这段代码里只是打印 “对象销毁了”。
3.动态内存管理
int* p = new int(10); // 动态分配内存
delete p; // 释放内存
// 动态数组
int* arr = new int[5]{1, 2, 3};
delete[] arr;
对比 C:new/delete 替代 malloc/free,会调用构造 / 析构函数。
4.常用容器
#include <vector>
#include <map>
vector<int> v = {1, 2, 3}; // 动态数组
v.push_back(4); // 添加元素
map<string, int> m; // 键值对
m["Alice"] = 90;
这是 C++ 里常用容器的基础示例,展示了两个最常用的标准容器:
vector是 “可以自动扩容的数组”,替代 C 语言里的普通数组;
map是 “键值对字典”,可以通过 “键” 快速找到对应的 “值”。
5.智能指针(C++11)
#include <memory>
unique_ptr<int> p1(new int(10)); // 自动释放内存
shared_ptr<int> p2 = make_shared<int>(20);
这是 C++11 新增的智能指针(用来自动管理动态内存的工具),核心是解决普通指针容易忘释放内存导致的 “内存泄漏” 问题,示例里是两种常用智能指针:
- 先明确前提
普通指针用new申请内存后,得手动delete释放(否则内存泄漏);而智能指针会在不需要时自动释放内存,不用手动管。 - 代码里的两种智能指针
#include :使用智能指针必须包含的头文件;
(1)unique_ptr(独占型智能指针)
代码:unique_ptr p1(new int(10));
特点:unique_ptr:一人独享,用完就自动还(内存),不让别人碰;
(2)shared_ptr(共享型智能指针)
代码:shared_ptr p2 = make_shared(20);
特点:shared_ptr:多人共用,记着有多少人在用,最后一个人用完才自动还(内存);
6.C VS C++关键区别
| 特性 | C | C++ |
|---|---|---|
| 输入输出 | printf scanf |
cout cin |
| 字符串 | char[] | string类 |
| 内存管理 | malloc free |
new delete |
| 面向对象 | 不支持 | 类、继承、多态 |
| 函数 | 无重载 | 支持重载和默认参数 |
混合编程C&C++
C 和 C++ 的混合编程在实际开发中非常常见,尤其是在嵌入式系统、硬件驱动开发或复用现有 C 库时
1、C++ 中引用 C 标准库函数
#include <cstdio> // C++ 版本的 C 标准库头文件
#include <cmath>
int main() {
std::printf("Hello from C!\n"); // 需加 std::
double x = std::sqrt(2.0); // 明确使用 std 命名空间
return 0;
}
C++ 把 C 的标准库头文件(比如 C 的<stdio.h>)改成了去掉.h、加c前缀的形式(比如),并且把这些 C 函数放进了std命名空间里。
所以在 C++ 里用 C 的函数(比如printf、sqrt),得在前面加std::
(比如std::printf)。
2. 头文件的兼容性
目标:让同一头文件既能被 C 编译器也能被 C++ 编译器正确解析。
方法:在 C 头文件中添加 __cplusplus 判断。
#ifdef __cplusplus // 如果是 C++ 编译器
extern "C" { // 按 C 方式导出符号
#endif
// C 函数声明
void c_function(int x);
#ifdef __cplusplus
}
#endif
前后两段#ifdef __cplusplus是成对、连在一起的,本质是 “给中间的函数声明套了一个「C++ 编译器专属的外壳」”
3. C++ 引入 C 写的函数
#include <iostream> // 普通C++头文件,正常引入(和调用C函数无关)
extern "C" { // 关键:开启“C语言规则模式”
#include "my_lib.h" // 引入C头文件 → 里面的所有函数(比如c_function)都按C规则处理
} // 关闭“C语言规则模式”
int main() {
c_function(42); // 调用C函数 → 因为上面套了extern "C",C++能正确找到这个C函数
return 0;
}
如果不这么写。。。
#include "my_lib.h" // 没套extern "C"
int main() {
c_function(42); // 编译/链接报错!
return 0;
}
原因:C++ 编译器会给c_function改名字,而 C 语言写的c_function还是原名字,两者对不上,就会提示 “找不到 c_function 函数”。
4. C 调用 C++ 函数
C++代码
#include <iostream>
extern "C" { // 强制以 C 方式导出函数
void cpp_function(int x) {
std::cout << "C++ function called with: " << x << std::endl;
}
}
C代码
#include <stdio.h>
extern void cpp_function(int); // 声明 C++ 函数
int main() {
cpp_function(42); // 调用 C++ 函数
return 0;
}
C++ 端:给要被调用的函数套extern “C” { … },强制按 C 规则编译(保留原函数名);
C 端:用extern声明这个 C++ 函数(告诉 C 编译器 “有这个函数”);
编译链接:分别编译 C/C++ 代码,再链接成可执行文件,C 代码就能直接调用。
PS:
✅ C++ 默认行为:编译函数时会 “篡改” 函数名(专业叫「名字修饰 / Name Mangling」),比如cpp_function(int)会被改成_Z11cpp_functioni这种格式;
✅ C 的特性:C 编译器不会改函数名,只会用原名字(比如cpp_function).C++ 改函数名的核心原因:为了支持「函数重载」
C++ 自己为能链接到
只有和 C 交互时才出问题
5.总结

更改点灯程序为C++程序
1.更改文件名

2.更改CMakeList.txt文件

3、添加 extern “C” 修饰 app_main 函数
因为 app_main 函数比较特殊,是系统自动会调用这个函数名称,而 C++ 编译器会将函数名重新修饰,这会导致系统找不到这个 app_main 函数名,加了 extern “C” 告诉编译器这个函数安装 C 的标准走不要对他进行函数名修饰
#include <cstdio>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_system.h"
extern "C" void app_main(void)
{
std::printf("Hello World!\n");
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << GPIO_NUM_10);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
while (1)
{
gpio_set_level(GPIO_NUM_10, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_NUM_10, 1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
void app_main(void)
{
printf("Hello World!\n");
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL<<GPIO_NUM_10);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
while (1)
{
gpio_set_level(GPIO_NUM_10, 0);
vTaskDelay(500 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_NUM_10, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
4. C版本与C++版本代码核心差异说明
-
app_main函数声明差异- C版本:
void app_main(void) - C++版本:
extern "C" void app_main(void) - 作用/原因:C++ 编译器会修改函数名,加
extern "C"强制按 C 规则保留app_main原名,保证系统能找到入口函数
- C版本:
-
printf调用方式差异- C版本:
printf("Hello World!\n"); - C++版本:
std::printf("Hello World!\n"); - 作用/原因:C++ 将 C 标准库函数归入
std命名空间,必须加std::才能调用(或加using namespace std;)
- C版本:
-
头文件(隐含差异)
- C版本:
#include <stdio.h>(C 标准头文件) - C++版本:
#include <cstdio>(截图里能看到) - 作用/原因:C++ 推荐用
cXXX格式的头文件(如<cstdio>)替代 C 的XXX.h(如<stdio.h>)
- C版本:
更多推荐



所有评论(0)