C语言函数指针和结构体
函数指针、数组、结构体
一、函数指针
1.1 函数名
- 一个函数在编译时被分配一个入口地址,这个地址就称为函数的指针,函数名代表函数的入口地址
#include <stdio.h>// 定义一个简单的函数
void func(){printf("这是内部打印\n");
}int main() {// 打印函数func的地址,三种写法结果相同// func: 函数名本身就是函数的地址// &func: 取函数的地址// *func: 解引用函数指针,但仍然得到函数的地址printf("%p %p %p\n", func, &func, *func); // 这三种的取到的函数地址都是一样的// 函数指针的使用:函数的地址加()是调用函数func(); // 直接通过函数名调用(*func)(); // 通过解引用函数指针调用(&func)(); // 通过取函数地址并调用return 0;
}
1.2 函数指针
-
函数指针:它是指针,指向函数的指针
-
语法格式:
返回值 (*函数指针变量)(形参列表);
- 函数指针变量的定义,其中返回值、形参列表需要和指向的函数匹配
#include <stdio.h>// 定义一个简单的加法函数
int add(int a, int b) {return a + b;
}int main() {// 定义的同时赋值,初始化// 声明一个函数指针p1,它指向一个接受两个int参数并返回int的函数// 将add函数的地址赋给p1,这样p1就成为了add函数的别名int (*p1)(int, int) = add; // p1就是add的别名printf("%d\n", p1(1, 2)); // 通过函数指针调用add函数// 先定义后赋值// 声明一个函数指针p2,它指向一个接受两个int参数并返回int的函数int (*p2)(int, int);// 将add函数的地址赋给p2// p2 = &add; // 这种写法也是正确的,&add表示取add函数的地址p2 = add; // 函数名本身就是函数的地址,所以可以直接赋值printf("%d\n", p2(60, 50)); // 通过函数指针调用add函数return 0;
}
1.3 回调函数
- 函数指针变量做函数参数,这个函数指针变量指向的函数就是回调函数
- 回调函数可以增加函数的通用性:在不改变原函数的前提下,增加新功能
#include <stdio.h>// 定义一个简单的加法函数
int add(int a, int b) {return a + b;
}
// 定义一个简单的减法函数
int sub(int a, int b) {return a - b;
}
// 定义一个回调函数,接受两个整数和一个函数指针作为参数
// 函数指针p指向一个接受两个int参数并返回int的函数
int cocl(int x, int y, int (*p)(int, int)) {// 通过函数指针调用相应的函数int temp = p(x, y);return temp;
}
int main() {// 这是加法// 调用cocl函数,传入10和20以及add函数的地址int temp = cocl(10, 20, add);printf("%d\n", temp); // 输出加法结果// 调用减法// 调用cocl函数,传入10和20以及sub函数的地址int temp2 = cocl(10, 20, sub);printf("%d\n", temp2); // 输出减法结果return 0;
}
三、结构体
3.1 结构体的使用
#include <stdio.h>// 定义一个结构体Student,包含姓名、年龄和性别
struct Student {char name[20]; // 姓名int age; // 年龄char sex; // 性别
};int main() {// 定义一个Student结构体变量s并初始化struct Student s = {"feifei", 19, 'm'};// 通过普通变量访问结构体成员,使用.操作符printf("%s %d %c\n", s.name, s.age, s.sex);// 定义一个指向Student结构体的指针pstruct Student *p;// 将s的地址赋给指针pp = &s;// 通过指针访问结构体成员,使用->操作符printf("%s %d %c\n", p->name, p->age, p->sex);return 0;
}
3.2 结构体值传参
- 传值是指将参数的值拷贝一份传递给函数,函数内部对该参数的修改不会影响到原来的变量
#include <stdio.h>
#include <string.h> // 添加string.h头文件以使用strcpy函数// 定义一个结构体Student,包含姓名、年龄和性别
struct Student {char name[20]; // 姓名int age; // 年龄char sex; // 性别
};// 值传递:将结构体变量s的副本传递给函数
// 在函数内部对s的修改不会影响到main函数中的s
void f1(struct Student s) {strcpy(s.name, "zhansan"); // 修改姓名s.age = 18; // 修改年龄s.sex = 'm'; // 修改性别printf("函数体中修改:%s %d %c\n", s.name, s.age, s.sex);
}// 指针传递:将结构体变量s的地址传递给函数
// 在函数内部通过指针修改s的内容会直接影响到main函数中的s
void f2(struct Student *p) {strcpy(p->name, "mafeifei"); // 通过指针修改姓名p->age = 18; // 通过指针修改年龄p->sex = 'm'; // 通过指针修改性别printf("f2函数体中修改:%s %d %c\n", p->name, p->age, p->sex);
}int main() {// 定义一个Student结构体变量s并初始化struct Student s = {"feifei", 19, 'm'};// 通过普通变量访问结构体成员,使用.操作符// f1(s); // 值传递,不会修改main函数中的s// 通过指针传递,会修改main函数中的sf2(&s); // 传递s的地址printf("函数体执行后:%s %d %c\n", s.name, s.age, s.sex);return 0;
}
3.3 结构体地址传递
- 传址是指将参数的地址传递给函数,函数内部可以通过该地址来访问原变量,并对其进行修改。
#include <stdio.h>
#include <string.h>// 定义一个结构体Student,包含姓名、年龄和性别
struct Student {char name[20]; // 姓名int age; // 年龄char sex; // 性别
};// 通过地址传递修改结构体成员
void updateStudent(struct Student *s) {strcpy(s->name, "张三"); // 修改姓名s->age = 20; // 修改年龄s->sex = 'M'; // 修改性别
}// 打印结构体成员
void printStudent(struct Student s) {printf("姓名: %s, 年龄: %d, 性别: %c\n", s.name, s.age, s.sex);
}int main() {// 定义一个Student结构体变量并初始化struct Student student = {"李四", 18, 'F'};printf("修改前:\n");printStudent(student);// 通过地址传递修改结构体成员updateStudent(&student);printf("修改后:\n");printStudent(student);return 0;
}