C语言结构体函数使用函数指针
#include <stdio.h>
struct car{
char* name;
int age;
void (*print)(struct car* c);
};
void printA(struct car* c) {
printf("CarAAAA name: %s, Age: %d\n", c->name, c->age);
}
void printB(struct car* c) {
printf("CarBBBB name: %s, Age: %d\n", c->name, c->age);
}
int main()
{
struct car car1;
car1.name = "BMW";
car1.age = 5;
car1.print = printA;
car1.print(&car1);
struct car car2 = {"Audi", 3, printB};
car2.print(&car2);
struct car* car3 = (struct car*)malloc(sizeof(struct car));
car3->name = "Tesla";
car3->age = 2;
car3->print = printB;
car3->print(car3);
printf("你好!\n");
return 0;
}
这段C语言代码演示了结构体(struct car
)的使用、函数指针作为结构体成员,以及不同方式初始化结构体变量的过程。以下是逐部分的详细解释:
1. 结构体 struct car
的定义
struct car{
char* name; // 指向汽车名称的字符串指针
int age; // 汽车使用年限(或车龄)
void (*print)(struct car* c); // 函数指针:指向一个打印汽车信息的函数
};
这是一个自定义的结构体类型,包含3个成员:
name
:char*
类型,存储汽车名称的字符串地址(如 “BMW”)。age
:int
类型,存储汽车的使用年限。print
:函数指针,指向一个**返回值为void
,参数为struct car*
(指向自身的指针)**的函数。它的作用是:当调用car.print()
时,执行具体的打印逻辑。
2. 打印函数 printA
和 printB
void printA(struct car* c) {
printf("CarAAAA name: %s, Age: %d\n", c->name, c->age);
}
void printB(struct car* c) {
printf("CarBBBB name: %s, Age: %d\n", c->name, c->age);
}
这两个函数是 struct car
中 print
函数指针的具体实现,功能是打印汽车的名称(c->name
)和车龄(c->age
),但输出的前缀不同(CarAAAA
或 CarBBBB
)。
参数 struct car* c
是指向具体 car
结构体实例的指针,通过 c->name
或 c->age
可以访问该实例的成员变量。
3. main 函数中的结构体实例化与使用
① 实例 car1
:手动赋值
struct car car1;
car1.name = "BMW"; // 直接赋值名称
car1.age = 5; // 赋值车龄
car1.print = printA; // 函数指针指向 printA
car1.print(&car1); // 调用 printA 打印 car1 的信息
- 先定义
car1
为struct car
类型的变量。 - 逐个赋值成员变量:
name
指向字符串"BMW"
,age
设为5,print
函数指针指向printA
。 - 调用
car1.print(&car1)
:通过函数指针调用printA
,传入car1
的地址(&car1
),printA
内部通过该指针访问car1
的成员并打印。
输出结果:CarAAAA name: BMW, Age: 5
② 实例 car2
:初始化列表赋值
struct car car2 = {"Audi", 3, printB}; // 按顺序初始化成员
car2.print(&car2); // 调用 printB 打印 car2 的信息
- 使用结构体初始化列表直接赋值,顺序必须与结构体成员定义的顺序一致:
第一个值对应name
("Audi"
),第二个对应age
(3),第三个对应print
(函数指针printB
)。 - 调用
car2.print(&car2)
:通过printB
打印car2
的信息。
输出结果:CarBBBB name: Audi, Age: 3
③ 实例 car3
:动态内存分配
struct car* car3 = (struct car*)malloc(sizeof(struct car)); // 动态分配内存
car3->name = "Tesla"; // 赋值名称(注意:`->` 是指针访问成员的语法)
car3->age = 2; // 赋值车龄
car3->print = printB; // 函数指针指向 printB
car3->print(car3); // 调用 printB 打印 car3 的信息
- 使用
malloc
动态分配struct car
大小的内存,并将地址赋值给指针car3
。 - 通过
->
操作符(指针访问成员的语法)为car3
的成员赋值。 - 调用
car3->print(car3)
:传入car3
自身的指针(无需取地址,因为car3
已经是指针),通过printB
打印信息。
输出结果:CarBBBB name: Tesla, Age: 2
④ 最后输出中文
printf("你好!\n"); // 输出中文 "你好!"
直接调用标准库函数 printf
输出字符串。注意:能否正确显示中文取决于编译器和终端的编码设置(如 UTF-8),现代环境通常可以正常显示。
潜在问题(需注意)
- 内存泄漏:
car3
使用malloc
分配了内存,但代码中未调用free(car3)
释放内存。长期运行可能导致内存泄漏。 - 字符串指针的风险:
name
是char*
类型,直接指向字符串字面量(如"BMW"
),这些字符串存储在只读内存中,不可修改。如果尝试修改(如car1.name[0] = 'b'
)会导致未定义行为。
完整输出结果
CarAAAA name: BMW, Age: 5
CarBBBB name: Audi, Age: 3
CarBBBB name: Tesla, Age: 2
你好!
C++结构体与类函数使用函数指针
#include <stdio.h>
#include <iostream>
struct car{
char* name;
int age;
void (*print)(struct car* c);
};
class motor{
public://如果不写public,则默认是priate
std::string name;
int age;
//函数指针
void (*print)(motor* m);
//成员函数声明
void printClaim();
//成员函数定义
void printDefin() {
printf("Motor::Define name: %s, Age: %d\n", name.c_str(), age);
}
};
void printA(struct car* c) {
printf("CarAAAA name: %s, Age: %d\n", c->name, c->age);
}
void printB(struct car* c) {
printf("CarBBBB name: %s, Age: %d\n", c->name, c->age);
}
void printC(motor* c) {
printf("CarBBBB name: %s, Age: %d\n", c->name, c->age);
}
void motor::printClaim() {
printf("MotorClaim name: %s, Age: %d\n", name.c_str(), age);
}
int main()
{
struct car car1;
car1.name = "BMW";
car1.age = 5;
car1.print = printA;
car1.print(&car1);
struct car car2 = {"Audi", 3, printB};
car2.print(&car2);
struct car* car3 = new car;
car3->age = 2;
car3->print = printB;
car3->print(car3);
//上述表明c++也支持c语法,注意malloc变成了new
motor m1;
m1.name = "Tesla";
m1.age = 1;
m1.print = printC;
m1.print(&m1);
m1.printClaim();
m1.printDefin();
//使用new创建对象
motor* m2 = new motor;
m2->name = "BYD";
m2->age = 2;
m2->print = printC;
m2->print(m2);
m2->printClaim();
m2->printDefin();
printf("你好!\n");
return 0;
}
这段代码展示了C++中结构体与类的混合使用,以及函数指针和成员函数的区别。以下是关键分析:
1. C++兼容C语言特性
// C风格结构体(默认public)
struct car{
char* name;
int age;
void (*print)(struct car* c);
};
- C++完全兼容C语言的结构体语法。
- 与C不同的是,C++的结构体可以包含成员函数(但此代码未使用)。
2. C++类与结构体的区别
class motor{
public:
std::string name; // C++字符串类
int age;
void (*print)(motor* m); // 函数指针(非成员函数)
void printClaim(); // 成员函数声明
void printDefin() { /* ... */ } // 成员函数定义
};
- 类(class) 默认成员是
private
,而结构体(struct) 默认是public
。 - 使用
std::string
(需包含<string>
头文件)代替C风格的char*
,更安全且自动管理内存。
3. 函数指针与成员函数的区别
函数指针(全局/静态函数)
void printC(motor* c) {
printf("CarBBBB name: %s, Age: %d\n", c->name, c->age); // 错误:%s 应改为 %ls
}
- 函数指针
void (*print)(motor* m)
只能指向全局函数或静态成员函数,无法直接指向类的非静态成员函数(因为非静态成员函数隐含this
指针)。 - 问题:
printf
中使用%s
打印std::string
是错误的,应使用c->name.c_str()
转换为const char*
。
成员函数
void motor::printClaim() {
printf("MotorClaim name: %s, Age: %d\n", name.c_str(), age);
}
- 成员函数属于类的实例,可直接访问类的成员变量(无需通过指针)。
- 通过对象或指针调用:
m1.printClaim()
或m2->printClaim()
。
4. 对象创建与内存管理
C风格内存分配
struct car* car3 = new car; // C++中可用new替代C的malloc
new
会调用构造函数(即使类/结构体没有显式定义构造函数)。- 问题:未初始化
car3->name
,直接调用print
会导致未定义行为。
C++对象创建
motor* m2 = new motor; // 动态分配
motor m1; // 栈上分配
new
返回指针,需手动delete
释放内存。- 栈上分配的对象在作用域结束时自动销毁。
5. 代码中的错误与警告
① 未包含必要的头文件
#include <iostream> // 包含了iostream但未使用
// 缺少必要的:
#include <cstdio> // 若使用printf
#include <string> // 若使用std::string
② printf
与 std::string
不兼容
// 错误:
printf("CarBBBB name: %s, Age: %d\n", c->name, c->age);
// 正确:
printf("CarBBBB name: %s, Age: %d\n", c->name.c_str(), c->age);
③ 内存泄漏
struct car* car3 = new car; // 未delete
motor* m2 = new motor; // 未delete
- 使用
new
分配的内存必须用delete
释放,否则会导致内存泄漏。
6. 修正后的完整代码
#include <cstdio> // printf
#include <string> // std::string
struct car{
char* name;
int age;
void (*print)(struct car* c);
};
class motor{
public:
std::string name;
int age;
void (*print)(motor* m);
void printClaim();
void printDefin() {
printf("Motor::Define name: %s, Age: %d\n", name.c_str(), age);
}
};
void printA(struct car* c) {
printf("CarAAAA name: %s, Age: %d\n", c->name, c->age);
}
void printB(struct car* c) {
printf("CarBBBB name: %s, Age: %d\n", c->name, c->age);
}
void printC(motor* c) {
printf("Motor name: %s, Age: %d\n", c->name.c_str(), c->age); // 修正:使用c_str()
}
void motor::printClaim() {
printf("MotorClaim name: %s, Age: %d\n", name.c_str(), age);
}
int main()
{
struct car car1;
car1.name = "BMW";
car1.age = 5;
car1.print = printA;
car1.print(&car1);
struct car car2 = {"Audi", 3, printB};
car2.print(&car2);
struct car* car3 = new car;
car3->name = "Tesla"; // 补充:初始化name
car3->age = 2;
car3->print = printB;
car3->print(car3);
delete car3; // 释放内存
motor m1;
m1.name = "Tesla";
m1.age = 1;
m1.print = printC;
m1.print(&m1);
m1.printClaim();
m1.printDefin();
motor* m2 = new motor;
m2->name = "BYD";
m2->age = 2;
m2->print = printC;
m2->print(m2);
m2->printClaim();
m2->printDefin();
delete m2; // 释放内存
printf("你好!\n");
return 0;
}
7. 关键总结
- 结构体 vs 类:C++中结构体与类的主要区别是默认访问权限(public/private)。
- 函数指针:只能指向全局函数或静态成员函数,无法直接指向非静态成员函数。
- 内存管理:使用
new
分配的内存必须用delete
释放,避免内存泄漏。 - 字符串处理:
std::string
比char*
更安全,使用c_str()
转换为C风格字符串。
类套类
#include <stdio.h>
#include <iostream>
class wheel{
public:
std::string name;
int age;
//成员函数定义
void printDefin() {
printf("Wheel::Define name: %s, Age: %d\n", name.c_str(), age);
}
};
class motor{
public://如果不写public,则默认是priate
std::string name;
int age;
wheel wheel1;
wheel *wheel2;
//函数指针
void (*print)(motor* m);
//成员函数声明
void printClaim();
//成员函数定义
void printDefin() {
printf("Motor::Define name: %s, Age: %d\n", name.c_str(), age);
}
};
void printC(motor* c) {
printf("CarBBBB name: %s, Age: %d\n", c->name, c->age);
}
void motor::printClaim() {
printf("MotorClaim name: %s, Age: %d\n", name.c_str(), age);
}
int main()
{
//使用new创建对象
motor* m2 = new motor;
m2->name = "BYD";
m2->age = 2;
m2->print = printC;
m2->print(m2);
m2->printClaim();
m2->printDefin();
m2->wheel1.name = "MIKIN";
m2->wheel1.age = 3;
m2->wheel1.printDefin();
m2->wheel2 = new wheel;
m2->wheel2->name = "HONDA";
m2->wheel2->age = 4;
m2->wheel2->printDefin();
return 0;
}
这段代码展示了C++中类的组合关系(composition)和内存管理,但存在几个关键问题需要注意:
1. 类的组合关系
class motor{
public:
wheel wheel1; // 组合:直接包含wheel对象
wheel *wheel2; // 组合:通过指针包含wheel对象
};
wheel1
:motor
对象创建时,wheel1
会自动构造;motor
销毁时,wheel1
会自动销毁。wheel2
:需要手动new
和delete
,否则会导致内存泄漏。
2. 内存泄漏问题
m2->wheel2 = new wheel; // 分配内存
// ... 使用 wheel2 ...
// 缺少对应的 delete m2->wheel2;
delete m2; // 仅释放了 m2,未释放 wheel2
- 问题:
wheel2
是动态分配的内存,但程序结束前未调用delete m2->wheel2
,导致内存泄漏。
3. 字符串打印问题
void printC(motor* c) {
printf("CarBBBB name: %s, Age: %d\n", c->name, c->age); // 错误
}
- 错误:
%s
要求参数为const char*
,但c->name
是std::string
。 - 修正:使用
c->name.c_str()
将std::string
转换为const char*
。
4. 修正后的完整代码
#include <cstdio> // printf
#include <string> // std::string
class wheel{
public:
std::string name;
int age;
void printDefin() {
printf("Wheel::Define name: %s, Age: %d\n", name.c_str(), age);
}
};
class motor{
public:
std::string name;
int age;
wheel wheel1;
wheel *wheel2;
void (*print)(motor* m);
void printClaim();
void printDefin() {
printf("Motor::Define name: %s, Age: %d\n", name.c_str(), age);
}
};
void printC(motor* c) {
printf("Motor name: %s, Age: %d\n", c->name.c_str(), age); // 修正:使用 c_str()
}
void motor::printClaim() {
printf("MotorClaim name: %s, Age: %d\n", name.c_str(), age);
}
int main()
{
motor* m2 = new motor;
m2->name = "BYD";
m2->age = 2;
m2->print = printC;
m2->print(m2);
m2->printClaim();
m2->printDefin();
m2->wheel1.name = "MIKIN";
m2->wheel1.age = 3;
m2->wheel1.printDefin();
m2->wheel2 = new wheel;
m2->wheel2->name = "HONDA";
m2->wheel2->age = 4;
m2->wheel2->printDefin();
delete m2->wheel2; // 释放 wheel2 的内存
delete m2; // 释放 m2 的内存
return 0;
}
5. 改进建议
① 使用智能指针管理动态内存
#include <memory> // 包含智能指针头文件
class motor{
public:
std::unique_ptr<wheel> wheel2; // 使用 unique_ptr 自动管理内存
};
// 在 main 函数中:
m2->wheel2 = std::make_unique<wheel>(); // 无需手动 delete
- 优势:
std::unique_ptr
会在对象销毁时自动释放内存,避免内存泄漏。
② 构造函数初始化成员
class motor{
public:
motor() : wheel2(new wheel()) {} // 构造函数初始化 wheel2
// 或使用 C++11 的成员初始化器
// std::unique_ptr<wheel> wheel2 = std::make_unique<wheel>();
};
- 优势:确保指针成员被正确初始化,减少使用时的错误。
6. 关键知识点
- 组合关系:类可以包含其他类的对象或指针,实现更复杂的数据结构。
- 内存管理:手动
new
的对象必须手动delete
,建议优先使用智能指针(如std::unique_ptr
)。 - 字符串处理:
printf
打印std::string
时需用c_str()
转换为const char*
。