C语言笔记(三)
复习
1、main函数参数
int main(int argc, char *argv[])
{
//argc 和 argv都是命令行参数
//argc 表示命令行参数的个数
//char *argv[] 是一个指针数组,用来保存命令行字符串的地址
printf("%d\n", argc);
for(int i = 0; i < argc; i++)
{
puts(argv[i]);
}
return 0;
}
2、函数
- 用来实现某个特定功能的模块(代码块)
- 使得代码简洁
[1]函数的定义
<返回类型> 函数名(形参列表)
{
函数体;
return 返回值;
}
返回类型:
基本数据类型: char short int long float double
指针类型: char * ,short *, int *, long *.....
空类型: void 代表函数没有返回值
函数名
标识符
表示一个函数的入口地址
形参列表:
可以没有, 如果有参数,参数可以为不同数据类型的定义(不能初始化) 多个参数用 , 号隔开
形参和实参的数据类型要相对应
函数体:
实现功能的代码块;
return:
表示一个函数的结束,一般情况下,return后会一个返回值
返回值:
函数在调用的时候,会得到一个返回数据
函数的 <返回类型> 要与函数的 返回值 类型相同
函数的三要素:
返回类型、形参列表、返回值
[2]函数的声明
告知编译器,该定义的函数会在后续程序中调用
<返回类型> 函数名(形参列表);
函数声明一般在头文件和主函数中间(函数声明在调用之前),也可以放在头文件中
[3]函数的调用
函数名(实参列表);
[4]参数传递
值传递
将实参的值拷贝一份给形参,形参的改变不会影响实参
地址传递
将实参的地址拷贝给指针变量,用指针变量接受地址值,操作实参的地址,可以影响实参
练习:封装一个函数,打印二维数组的元素
指针函数
本质是一个函数,函数的返回值为地址
指针函数不能返回局部变量的地址
定义:
<返回类型> *函数名(形参列表)
{
函数体;
return 返回值;
}
int * func()
{
int a = 10;
return &a; //指针函数不能返回局部变量的地址
}
1、malloc动态开辟
- 在堆区开辟size大小的空间
void *malloc(size_t size);
返回值:
堆区开辟空间的地址
参数:
size:开辟空间的大小(字节为单位) sizeof(int) ---> 4bytes
头文件:
#include <stdlib.h>
int *func()
{
int *p = (int *)malloc(sizeof(int));
return p;
}
int main()
{
int *p = func();
*p = 10; //将堆区开辟的空间赋值为10
printf("%d\n", *p) //打印为10
return 0;
}
2、free
#include <stdlib.h>
int *func();
int main(int argc, char *argv[])
{
int *q = func(); //malloc手动开辟
*q = 10;
printf("%d\n", *q);
free(q); //free手动释放
q = NULL;
return 0;
}
int *func()
{
int *p = (int *)malloc(sizeof(int));
return p;
}
练习:使用指针函数封装strcat strcpy
作业:
封装一个函数,使用 指针 对字符串逆序输出(不能直接打印)
有n个人围城一圈。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来的第几号的那个人
Day12
复习
1、指针函数
本质:是一个函数
2、指针函数的定义
<返回类型> *函数名(形参列表)
{
函数体;
return 返回值;
}
指针函数的返回值是一个地址
不能返回局部变量的地址
int *func()
{
int a = 10;
return &a; //不能返回局部变量的地址
}
3、堆区动态开辟
void *malloc(size_t size);
返回值为堆区开辟空间的地址
int *p = (int *)malloc(sizeof(int) * 10);
void free(void *ptr);
free(p);
p = NULL;
1、函数指针
- 本质是一个指针,函数指针指向一个函数的入口地址
1、函数指针的定义
<数据类型> (*指针变量名)(参数列表);
int add(int a, int b)
{
return a + b;
}
void func()
{
printf("-----\n");
}
int main(int argc, char *argv[])
{
//定义一个函数指针变量p,并初始化
int (*p)(int, int) = add;
//func函数是一个void (*)() 类型的函数,与指针p的数据类型不匹配
p = func;
printf("%d\n", add(10,20));
printf("%d\n", p(10,20));
printf("%d\n", (*p)(10,20));
return 0;
}
注意: 函数指针的数据类型 要与 指针所指向函数的数据类型一致
练习:定义一个函数指针,调用 strcat strcpy 函数,使用函数指针调用这两个函数
2、函数指针数组
- 本质是一个数组,数组中的元素是同类型的函数指针
1、函数指针数组的定义
<数据类型> (*数组名[元素个数])(参数列表);
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 main(int argc, char *argv[])
{
int (*a[4])(int, int) = {add, sub, mul, div}; //定义并初始化一个函数指针数组 a ,里面存放add sub mul div 函数的入口地址
printf("%d\n", a[0](10, 20));
printf("%d\n", (*a[0])(10, 20));
printf("%d\n", (*a)(10, 20));
printf("%d\n", a[1](10, 20));
printf("%d\n", (*(a + 1))(10, 20));
printf("%d\n", a[2](5, 5));
printf("%d\n", a[3](5, 5));
return 0;
}
3、回调函数
- 当函数作为另一个函数的参数被调用时,称这个函数为回调函数 callback
void print(char *s)
{
puts(s);
}
void func(void (*p)(char *), char *s)
{
p(s);
}
int main(int argc, char *argv[])
{
func(print, "hello"); //直接调用func函数,func函数内部调用print打印“hello”
return 0;
}
4、递归函数
递归函数实现0~100的和
int func(int n)
{
if(n == 0)
return 0;
return n + func(n - 1);
}
递归函数实现n的阶乘
int func1(int n)
{
if(n == 1 || n == 0)
return 1;
return n * func1(n - 1);
}
递归函数实现输出斐波那契数列的第n项
1 1 2 3 5 8 13 21 34 55...
int func2(int n)
{
if(n == 1 || n == 2)
return 1;
return func2(n - 1) + func2(n - 2);
}
5、取别名
typedef 取别名
对已知的数据类型取别名
typedef 数据类型 别名;
typedef int INT;
typedef unsigned int u32;
对于复杂的数据类型
直接在定义前加 typedef ,取的别名就是定义的变量名
typedef int (*PFUNC)(int, int);
int add(int a, int b)
{
return a + b;
}
//int (*p)(int, int) = add;
PFUNC q; // PFUNC 是一个函数指针的数据类型 该数据类型与add函数的类型相同 p表示用该数据类型定义的一个变量
q = add;
typedef int (*P)[3]; //对一个行指针的数据类型取别名 取的别名为 P
int main(int argc, char *argv[])
{
int a[2][3] = {1,2,3,4,5,6};
P p;
p = a;
printf("%d\n", p[1][0]);
return 0;
}
Day13
复习
1、函数指针
本质是一个指针,该指针指向函数的地址
定义:
<数据类型> (*指针变量名)(参数列表);
int add(int a, int b)
{
return a + b;
}
int (*p)(int, int); //定义一个函数指针p,该指针数据类型与add函数数据类型一致
p = add;
p = &add;
int n = p(10,20);
printf("%d\n", n);
2、函数指针数组
本质是一个数组,满足数组的特性
定义:
<数据类型> (*数组名[元素个数])(参数列表);
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 (*a[4])(int, int) = {add, sub, mul, div};
访问:
a[0](10,20) ---> add(10,20)
3、递归函数
直接或间接的调用函数本身
int func1(int n)
{
if(n == 0)
return 0;
return n + func1(n - 1);
}
int func2(int n)
{
if(n == 0 || n == 1)
return 1;
return n*func2(n - 1);
}
int func3(int n)
{
if(n == 1 || n == 2)
return 1;
return func3(n - 1) + func3(n - 2);
}
4、回调函数
当一个函数以参数的形式传递给另一个函数时,称这个函数为回调函数
void print(char *s)
{
puts(s);
}
void func(void (*p)(char *), char *s)
{
p(s);
}
func(print, "hello");
4、取别名
对数据类型取别名
typedef 数据类型 数据类型别名
typedef int INT;
INT a = 10; //int a = 10 ---> INT a = 10
int add(int a, int b)
{
return a + b;
}
typedef int (*PFUNC)(int, int);
PFUNC p = add;
结构体
- 是用户自定义数据类型,可以定义不同数据类型的变量
1、结构体的定义
1、定义结构体
语法形式:
struct <结构体名> {
数据类型 成员变量1;
数据类型 成员变量2;
数据类型 成员变量3;
......
};
struct student {
int id;
char name[10];
int age;
float score;
};
数据类型在不定义变量时,不占内存空间
2、定义结构体变量
struct 结构体名 结构体变量名;
struct student lisi; //定义一个结构体变量lisi
3、结构体变量初始化
//定义一个结构体变量lisi,并按照顺序初始化结构体成员变量
struct student lisi = {29, "lisi", 18, 59};
//不按顺序初始化结构体变量的方式
struct student Zsan = {
.id = 30,
.age = 18,
.name = "Zsan",
.score = 100
};
4、结构体成员变量的访问
结构体变量名.成员变量名
//结构体成员变量的访问 : 结构体变量名.成员变量名
printf("%d %s %d %.1f\n", lisi.id, lisi.name, lisi.age, lisi.score);
lisi.score = 60;
printf("%d %s %d %.1f\n", lisi.id, lisi.name, lisi.age, lisi.score);
//zsan.name = "laoder"; error
strcpy(zsan.name,"laoder");
printf("%d %s %d %.1f\n", zsan.id, zsan.name, zsan.age, zsan.score);
5、结构体数据类型的大小
以字节对齐的方式存储
整个结构体的大小是 结构体成员变量中最大数据类型 的整数倍
2、结构体指针
1、结构体指针定义
struct 结构体名 *结构体指针变量名;
struct student *p;
eg:
struct student lisi = {29, "lisi", 18, 100};
//定义一个结构体指针变量p,保存结构体lisi变量的地址
struct student *p = &lisi;
//通过结构体指针访问结构体内部成员: 结构体指针变量名->成员变量名
printf("%d %s %d %.1f\n", p->id, p->name, p->age, p->score);
p->id = 40;
strcpy(p->name,"zs");
p->age = 21;
p->score = 90;
printf("%d %s %d %.1f\n", p->id, p->name, p->age, p->score);
2、堆区开辟结构体空间
struct student *p = (struct student *)malloc(sizeof(struct student));
p->id = 14;
strcpy(p->name, "lisi");
p->age = 20;
p->score = 90;
printf("%d %s %d %.1f\n", p->id, p->name, p->age, p->score);
free(p);
p = NULL;
3、结构体数组
是一个数组,该数组存储同类型的结构体变量
struct student a[3] = {{10, "lisi", 18, 60}, {11, "fyy", 18, 100}, {12, "GGbond", 18, 100}};
for(int i = 0; i < 3; i++)
{
printf("%d %s %d %.1f\n", a[i].id, a[i].name, a[i].age, a[i].score);
}
4、取别名
typedef struct student{
int id;
char name[10];
int age;
float score;
}S_stu,*P_stu; //struct student 类型取别名为 S_stu
//struct student * 类型取别名为 P_stu
S_stu lisi = {20, "lisi", 18, 100}; //<---> struct student lisi = {20, "lisi", 18, 100};
P_stu p = &lisi; //<---> struct student * p = &lisi;
Day14
复习
结构体
用户自定义的数据类型,可以存放大量不同数据类型成员变量
1、结构体的定义
struct <结构体名> {
数据类型 成员变量1;
数据类型 成员变量2;
数据类型 成员变量3;
......
};
eg:
struct student {
int id;
char name[10];
int age;
float score;
};
2、结构体变量的定义
struct student {
int id;
char name[10];
int age;
float score;
//struct student lisi;
};
struct student lisi; //定义一个结构体变量lisi
初始化:
struct student Zs = {19, "zhangsan", 18, 90.0};
struct student XiaoM = {
.id = 20,
.age = 18,
.score = 100,
.name = "xiaoming"
};
3、结构体成员访问
结构体变量名.成员变量名;
lisi.id = 21;
printf("%d\n",lisi.id);
strcpy(lisi.name, "laodeng");
4、结构体的大小
数据类型在未定义变量时,不会占用内存空间
字节对齐
结构体的大小为 结构体成员变量最大数据类型的整数倍
5、结构体指针
struct <结构体名> * 指针变量名;
struct student *p;
struct student *q = &lisi;
成员访问:
指针变量名->成员变量名;
q->id = 20;
6、堆区动态开辟
struct student *p = (struct student *)malloc(sizeof(struct student));
p->id = 20;
p->age = 18;
p->score = 100;
strcpy(p->name, "lisi");
free(p);
p = NULL;
7、结构体数组
struct <结构体> 数组名[元素个数];
struct student a[3] = {{19,"lisi",20,60.0}, {20,"zhangsan",20,70.0},{21,"xiaoming",20,80.0}};
a[0].id = 30;
8、取别名
typedef 数据类型 数据类型别名;
typedef struct student{
int id;
char name[10];
}S_stu, *P_stu;
S_stu lisi; // struct student lisi;
P_stu p; // struct student *p;
typedef int (*PFunc)(int, int);
int add(int a, int b)
{
return a + b;
}
int (*p)(int, int) = add;
PFunc q = add;
指针
- 指针就是地址,地址的取别名
- 内存由多个内存单元组成,内存单元由一个或多个字节组成,每个字节都有一个编号,这个编号就叫做地址。
- 指针变量,用来保存地址常量
- 指针、地址、指针变量统称为指针
<存储类型> <数据类型> * 变量名;
[1]指针的偏移
int a = 10;
int *p = &a;
p+1: 向高地址偏移4字节(根据指针指向的数据类型为单位偏移)
p-1: 向低地址偏移4字节
p++: p = p + 1 指向改变,向高地址偏移4字节
p--: p = p - 1
[2]指针的运算
* &
&:取地址
*:取内容,取该指针指向地址空间的内容
*p++ 与 *(p++)
指针都要发生偏移
*++p 与 *(++p)
指针要发生偏移
[3]指针指向普通变量
int a = 10;
直接访问: &a
间接访问:
int *p = &a;
*p = 20;
[4]指针指向字符串常量
char *p = "hello";
puts(p); //打印hello
*p = 'a'; //指针p指向字符串常量首地址,只可读不可写
[5]指针指向一维数组
int a[5] = {1,2,3,4,5};
int *p = a;
char str[10] = "hello";
char *p = str;
*p = 'a'; //可以修改
[6]指针指向二维数组
数组指针
<存储类型> <数据类型> (*变量名)[元素个数];
int a[2][3] = {1,2,3,4,5,6};
int (*p)[3] = a;
int a[2][3] = {1,2,3,4,5,6};
printf("%p\n", a);
printf("%p\n", a + 1);
int (*p)[3] = a;
printf("%p\n", p);
printf("%p\n", p + 1);
printf("%p\n", *p);
printf("%p\n", *p + 1);
printf("%d\n", *(*(p + 0) + 0));
printf("%d\n", *(*(p + 1) + 1));
printf("%d\n", *(p[1] + 1));
printf("%d\n", p[0][0]);
printf("%d\n", p[1][1]);
printf("%d\n", (*(p + 0))[0]);
printf("%d\n", (*(p + 1))[1]);
[7]特殊指针
void * : 万能指针,可以指向任何的数据类型,但是使用时需要强壮
int a = 10;
void *p = &a;
printf("%d\n", *(int *)p);
空指针:
int *p;//野指针
int *p = NULL;
NULL是零地址空间,该空间不可读不可写。
const 修饰指针
int a = 10;
int *p = &a;
const int *p = &a; //不能更改该指针指向空间的内容,但是指针指向可以更改
int * const p = &a; //不能更改指针的指向,但是指针指向空间的内容可以更改
const int * const p = &a; //既不能更改指向,也不能更改内容
数组指针
数组指针本质是一个指针,指向整片一维数组空间
int a[4] = {1,2,3,4};
int (*p)[4] = &a;
printf("%p\n", &a);
printf("%p\n", &a + 1);
指针数组
定义
<存储类型> <数据类型> *数组名[元素个数];
int *a[4];
int a = 10,b = 20,c = 30;
printf("%p\n", &a);
printf("%p\n", &b);
printf("%p\n", &c);
int *arr[3] = {&a, &b, &c};
printf("%d\n", *arr[0]);
printf("%d\n", *(*arr));
printf("%d\n", *(*(arr + 2)));
函数指针
<数据类型> (*指针变量名)(参数列表);
int add(int a, int b)
{
return a + b;
}
int (*p)(int, int) = add;
int (*p)(int ,int) = &add;
结构体指针
typedef struct student {
int id;
char name[10];
}S_stu,*P_stu;
int main(int argc, char *argv[])
{
//struct student *p = (struct student *)malloc(sizeof(struct student));
P_stu p = (P_stu)malloc(sizeof(S_stu));
p->id = 20;
strcpy(p->name, "lisi");
printf("%d %s\n", p->id, p->name);
free(p);
p = NULL;
return 0;
}
多级指针
- 指向指针的指针
<存储类型> <数据类型> **指针变量名; //二级指针
int a = 10;
int *p = &a;
int **pp = &p;
printf("%p\n", &p);
printf("%p\n", pp);
printf("%d\n", **pp);