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);

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式石油工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值