C高级
指针
-
指针:轻量级、
-
指针定义:
- 存储类型 数据类型 *指针变量名;
-
指针初始化:
- 存储类型 数据类型 *指针变量名=地址;
-
指针赋值:
- 把一个已知变量的地址赋值给指针
- 把已知数组首地址赋值给指针
- 把同级指针赋值给指针
- 把NULL赋值给指针
函数
- 完成特点的程序模块
- 函数的分类
- 库函数:给用户直接调用的函数(调用时需要加对应的头文件)
- 自定义函数:用户中间写的函数
函数指针数组
本质是一个数组,只是这个数组里面放的是指向函数的指针
一般形式:存储类型 数据类型 (*数组名【下标】)(数据类型 形参1,数据类型 形参2.。。。)
例如:int (*p【3】)(int ,int);
#include <stdio.h>
int add(int a,int b)
{
return a+b;
}
int jian(int a,int b)
{
return a-b;
}
int cheng(int a,int b)
{
return a*b;
}
int chu(int a,int b)
{
return a/b;
}
int main(int argc, char *argv[])
{
int (*p[4])(int a,int b)={add,jian,cheng,chu};
int i,j;
char c;
scanf("%d%c%d",&i,&c,&j);
switch(c)
{
case '+':printf("%d\n",p[0](i,j));break;
case '-':printf("%d\n",p[1](i,j));break;
case '*':printf("%d\n",p[2](i,j));break;
case '/':printf("%d\n",p[3](i,j));break;
}
return 0;
}
函数的多文件封装
-
头文件–可以放库头文件和所有功能函数的声明
#ifndef _JISUAN_H ---- 防止头文件重复包含 #define _JISUAN_H #include <stdio.h> ---- 库头文件 int add(int a,int b); ---- 函数的声明 #endif -
功能文件— 自定义的头文件和所有功能函数代码
#include "jisuan.h" int add(int a,int b) { return a+b; } int jian(int a,int b) { return a-b; } int cheng(int a,int b) { return a*b; } int chu(int a,int b) { return a/b; } -
主函数文件—自定义头文件和函数的调用
#include "jisuan.h" int main(int argc, char *argv[]) { int x=12,y=34; printf("sum=%d\n",add(x,y)); return 0; } 编译:至少编译两个.c文件,头文件默认在当前目录下去找 调用外部头文件:-I
递归函数
- 直接或间接的调用函数本身的函数
- 两个条件:调用自己,要有结束表示
- 每次调用自己都会给函数开辟空间,所以每次调用的变量都会被保留在当前函数内,
- 循环能做的递归能做,但递归能做的循环不能做
回调函数
-
把函数作为一个参数,用函数指针来接受的函数
#include <stdio.h> int add(int a,int b){ return a+b; } int sub(int a,int b){ return a-b; } int mul(int a,int b){ return a * b; } int div(int a,int b){ return a / b; } int calc(int a,int b,int(*p)(int a,int b)){ return p(a,b); } int main(){ int a=21,b=7; printf("result = %d",calc(a,b,sub)); }
动态开辟空间函数
malloc
-
在堆区中开辟空间,需要我们手动开辟,也需要我们手动释放
//头文件 #include<stdlib.h> //函数原型 void *malloc(size_t size); //参数 size:需要开辟空间的大小 //返回值:开辟成功返回开辟的地址,失败返回空 //释放空间地址: void free(void *ptr); //参数:ptr:要释放的首地址,是开辟的指针并不是指针本身所在的栈空间 //最后为了避免野指针,所以需要把NULL赋值给指针 -
练习
#include <stdio.h> #include <stdlib.h> int main(){ int *p=(int *)malloc(sizeof (int)*10); int *pp=p; for (int i = 0; i < 10; ++i) { *p=rand(); p++; } for (int i = 0; i < 10; ++i) { printf("%d ",*pp); pp++; } free(p); p=NULL; pp=NULL; return 0; }
关键字
const — 变量常量化
- const修饰指针
- int const *p =&a —p的内容不能改
- int *const p =&a —p的地址不能改
define —宏定义
#include <stdio.h>
#define A char *
typedef char * B;
int main(int argc, char *argv[])
{
A a,b;
B c,d;
printf("a=%ld\n",sizeof(a));
printf("b=%ld\n",sizeof(b));
printf("c=%ld\n",sizeof(c));
printf("d=%ld\n",sizeof(d));
return 0;
}

上面结果为上图 ,所可以看到b=1,表示把A a,b;中的A替换成了char *, 故为char *a,b;类型为char 而不是char *
typedef ---- 重命名
已知数据类型的重命名
#include <stdio.h>
typedef int A;
int main(int argc, char *argv[])
{
A a=12;
printf("a=%d\n",a);
return 0;
}
define 和typedef二者区别

-
练习 — 利用宏定义求出两个数之间的最大值
#include <stdio.h> #define MAX(x,y) x>y?x:y int main(){ int a=5,b=7; printf("%d\n",MAX(a,b)); }
static —静态区域存储
作用
- 修饰全局变量,限制作用域
- 修饰局部变量,延长生命周期(只会被初始化一次)
- 修饰函数,限制作用域
extern — 调用外部文件变量
-
只能调用全局变量
-
编译的时候要和调用的文件一起编译
#extern2.c
int a=88;
#extern1.c
#include <stdio.h>
extern int a;
int main(int argc,char *argv[]){
printf("%d\n",a);
return 0;
}
//编译时要同时都要编译
gcc exten1.c extern2.c
./a.out
//输出结果为
88
struct — 结构体
-
本质还是一个数据类型,只是里面可以放很多成员,这些成员数据类型可以医院也可以不一样,也可以是已知的数据类型,也可以是构造类型
-
struct 结构体名{ 数据类型 成员1; 数据类型 成员2; .... }; ---分号不能省 -
全局初始化:在头文件下,主函数上,就是在构造完这个结构体后立马初始化
-
局部初始化:在函数体中定义一个结构体变量

-
结构体赋值
- 结构体赋结构体
- 成员单独赋值
- 注意给字符数组赋值用strcpy()赋值
-
⭐️结构体大小(笔试会考)
- 如果是64位系统系统默认安装8byte对齐,但是如果最大的数据类型小于8byte,就按照最大成员的数据类型的长度去计算(就是最大成员变量的倍数)
- 如果是32位系统系统默认安装4byte对齐,但是如果最大的数据类型小于4byte,就按照最大成员的数据类型的长度去计算(就是最大成员变量的倍数)
- 偶数地址存储
- 如果对齐自己能够放下后面的成员,就会在当前字节放,否则就空出来重新开辟
结构体嵌套
练习
#include <stdio.h>
typedef struct stu{
int s_id;
char std_name[20];
}STU;
typedef struct tea{
int t_id;
char tea_name[20];
STU stus[20];
}TEA;
int main() {
STU stu1={10001,"张三"};
STU stu2={10002,"李四"};
STU stu3={10003,"王五"};
TEA tea={10001,"zzz",{stu1,stu2,stu3}};
printf("the teacher name is %s\n",tea.tea_name);
printf("the teacher have this students\n");
for (int i = 0; i < 3; ++i) {
printf("NO.%d student's name is %s\n",i,tea.stus[i].std_name);
}
return 0;
}
结构体指针
- 本质是一个指针,只是这个指针指向了一个结构体
//一般形式
struct 结构体名 * 指针变量;
//初始化
struct 结构体名 * 指针变量名=结构体地址;
//访问内容,两种方式
(*p).成员
p->成员
练习
#include <stdio.h>
typedef struct stu{
int s_id;
char std_name[20];
}STU;
typedef struct tea{
int t_id;
char tea_name[20];
STU stus[20];
}TEA;
int main() {
STU stu1={10001,"张三"};
STU stu2={10002,"李四"};
STU stu3={10003,"王五"};
TEA tea={10001,"zzz",{stu1,stu2,stu3}};
struct tea *p_tea=&tea;
printf("the teacher name is %s\n",p_tea->tea_name);
printf("the teacher have this students\n");
for (int i = 0; i < 3; ++i) {
printf("NO.%d student's name is %s\n",i,p_tea->stus[i].std_name);
}
return 0;
}
结构体数组
-
本质是一个数组,只是这个数组里面的元素都是结构体
//一般形式 struct 结构体名 变量名[下标] ; int main(int argc, char *argv[]) { STU a={12,"zhangsan",45}; STU b={13,"lisi",47}; STU c={15,"wangwu",50}; STU s[3]={a,b,c};//初始化 printf("s[1].name=%s\n",s[1].name); return 0; }
共用体 ---- union
-
成员们一起用一片空间地址
-
共用体不能初始化
-
赋值只能成员赋值,而且每次只能赋值一个,前面的赋值都会被覆盖
共同体的大小:
- 也要遵循结构体大小的规则,但是只会给最大成员开辟对于的空间地址
-
枚举型 ---- enum成员没有初始化,从0开始往后面赋值
- 如果有成员赋值,成员后面一次递增+1
- 成员都是常量,不能修改它的值
练习
- 一周的星期几
#include <stdio.h>
int main(){
enum monday=1,thursday,tuesday,friday,wednesday,saturday,sunday};
printf("friday is %d\n",friday);
return 0;
}
gdb调式工具
- 帮我们找出代码的问题
使用方法
- gcc -g 编译的文件名 ,回生产一个a.out的执行文件 这个文件就可以调式
- gdb a.out
选项
- r ---- 运行程序
- l ---- 查看程序代码
- b ---- 设置断点,让程序跑到设置那一行
- c ---- 继续运行程序,直到下一个断点,如果没有直接运行完
- p 变量名 ---- 查看变量值
- n ---- 一行一行的运行(next),但是不进入函数
- s ---- 按行执行,但会进入函数
- delete ---- 删除所有断点
- q ---- 退出
| 命令 | 简介 | gdb功能 | 使用方法及备注 |
|---|---|---|---|
| run | r | 运行 | 调试开始 |
| break | b | 设置断点 | b断点处 |
| info | i | 查看信息 | 查看断点i b,等后面详细列举 |
| delete | d | 删除断点 | delete断点编号 |
| disable | disable | 禁用断点 | disable断点编号 |
| backtrace | bt,where | 查看栈帧 | bt N显示开头N个栈帧, bt -N最后N个栈帧 |
| p | 打印变量 | p argc打印变量,后面详细介绍 | |
| x | x | 显示内存 | x 0x1234567,后面详细介绍 |
| set | set | 改变变量值 | set variable <变量> = <表达式>;比如 set var test=3 |
| next | n | 执行下一行 | n;执行到下一行,不管下一行多复杂 |
| step | s | 执行下一行 | s;若下一行为函数,则进入函数内部 |
| continue | c,cont | 继续 | c为继续的次数,可省略,表示继续一次 |
| finish | finish | 执行完成当前函数 | |
| until | until | 执行完成代码块 |