day19-c函数,结构体

day19-c函数,结构体




一、函数:

完成特定功能的程序模块

1. 函数的分类:

库函数,给用户直接调用的函数(调用时要加对应的头文件)

自定义函数:用户自己写的函数

2. 怎么去写一个函数:

(1)函数的声明:定义在头文件下,主函数上,提前告诉计算机,我后面有这个,要给这个函数开辟空间,如果函数的功能写在主函数前面,可以不要声明

一般形式:存储类型 数据类型 函数名(数据类型 形参1,数据类型 形参2.。。。);(一定要加分号)

(2)函数的功能:

存储类型 数据类型 函数名(数据类型 形参1,数据类型 形参2.。。。)
{
功能模块;
返回值;返回值可以没有,根据功能来定,如果有返回值,返回值的数据类型要和函数的数据类型保持一致
}

(3)函数的调用

函数名(实参1,实参2.。。。)

实参的数据类型和个数和顺序要和形参保持一致

3. 函数的传参:

  • 值传递:
  • 地址传递–地址具有唯一性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 7 #include <stdio.h>
8 int swap(int *a,int *b)
9 {
10 int t;
11 t=*a;
12 *a=*b;
13 *b=t;
14
15 }
16 int main(int argc, char *argv[])
17 {
18 int x=12,y=34;
19 swap(&x,&y);
20 printf("x=%d----y=%d\n",x,y);
21
22 return 0;
23 }
  • 数组传递–数组传数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 7 #include <stdio.h>
8 #include <string.h>
9 char *mystrcat(char p[32],char q[32]);//函数的声明
10 int main(int argc, char *argv[])
11 {
12 char a[32]="hello";
13 char b[32]="world";
14 puts(mystrcat(a,b));//函数的调用
15
16 return 0;
17 }
18 char *mystrcat(char p[32],char q[32])//函数的功能
19 {
20 int i;
21 int len=strlen(p);
22 for(i=0;p[i];i++)
23 {
24 p[len]=q[i];
25 len++;
26 }
27 return p;
28 }
29

4.指针函数

本质是一个函数,特殊之处返回值必定是一个地址

一般形式:存储类型 数据类型 *函数名(数据类型 形参1,数据类型 形参2.。。。)

1
2
3
4
5
6
7
8
9
10
11
12
char *mystrcat(char p[32],char q[32])//函数的功能
19 {
20 int i;
21 int len=strlen(p);
22 for(i=0;p[i];i++)
23 {
24 p[len]=q[i];
25 len++;
26 }
27 return p;
28 }
29

5.函数指针:

本质是一个函数,只是这个指针指向的是函数的入口地址,就是函数名

一般形式:存储类型 数据类型 (*指针变量名)(数据类型 形参1,数据类型 形参2.。。。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 7 #include <stdio.h>
8 int swap(int *a,int *b)
9 {
10 int t;
11 t=*a;
12 *a=*b;
13 *b=t;
14
15 }
16 int add(int *a,int *b)
17 {
18 return *a + *b;
19 }
20 int main(int argc, char *argv[])
21 {
22 int x=12,y=34;
23 int (*p)(int *a,int *b)=swap; //把swap赋给p函数指针
24 p(&x,&y);//调用p
25 p=add;
26 printf("sum=%d\n",p(&x,&y));
27
28 return 0;
29 }

6. 函数指针数组

本质是一个数组,只是这个数组里面放的是指向函数的指针

一般形式:存储类型 数据类型 (*数组名【下标】)(数据类型 形参1,数据类型 形参2.。。。)

例如:int (*p【3】)(int ,int);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

/*===============================================
* 文件名称:jisuan.c
* 创 建 者: 张城
* 创建日期:2023年03月16日
* 描 述:
================================================*/
#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;
}

7. 函数的多文件封装

(1)头文件–可以放库头文件和所有功能函数的声明

1
2
3
4
5
6
7
8
9
#ifndef __JISUAN_H__---防止头文件重复包含
#define __JISUAN_H__
#include <stdio.h>--库头文件
int add(int a,int b);--函数的声明
int jian(int a,int b);
int cheng(int a,int b);
int chu(int a,int b);
#endif

(2)功能文件–自定义的头文件和所有功能函数代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
7 #include "jisuan.h"
8 int add(int a,int b)
9 {
10 return a+b;
11 }
12 int jian(int a,int b)
13 {
14 return a-b;
15 }
16 int cheng(int a,int b)
17 {
18 return a*b;
19 }
20 int chu(int a,int b)
21 {
22 return a/b;
23 }
24

(3)主函数文件—自定义头文件和函数的调用

1
2
3
4
5
6
7
8
9
10
 7 #include "jisuan.h"
8 int main(int argc, char *argv[])
9 {
10 int x=12,y=34;
11 printf("sum=%d\n",add(x,y));
12
13 return 0;
14 }
编译:至少编译两个.c文件,头文件默认在当前目录下去找
调用外部头文件:-I

8. 递归函数:

直接或间接调用函数本身的函数

两个条件:调用自己,要有结束标志

每次调用自己都会给函数开辟空间,所以每次调用的变量都被会保留在当前函数内,没有到结束标志回归不会被释放

循环能做的,递归函数都能做,但是递归函数能做的,循环不一定能做,所以我们能用循环就用循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 7 #include <stdio.h>
8 int add(int n)
9 {
10 if(n>0)
11 {
12 return n+add(n-1);
13 }
14
15 }
16 int main(int argc, char *argv[])
17 {
18 int n;
19 scanf("%d",&n);
20 printf("sum=%d\n",add(n));
21
22 return 0;
23 }

9. 回调函数

把一个函数作为一个参数,用函数指针来接受的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
7 #include <stdio.h>
8 int callback(int a,int b)
9 {
10 return a+b;
11 }
12 int gohome(int a,int b,int (*p)())
13 {
14 return p(a,b);
15
16 }
17 int main(int argc, char *argv[])
18 {
19 int x=12,y=34;
20 printf("sum=%d\n",gohome(x,y,callback));
21 return 0;
22 }

10. 动态开辟空间函数

malloc

在堆区开辟空间,需要我们手动开辟,也需要我们手动释放

头文件:#include <stdlib.h>

函数原型:void *malloc(size_t size);

参数:size:需要开辟空间的大小

返回值:开辟成功返回开辟的首地址,失败返回NULL

释放空间地址:

void free(void *ptr);

参数:ptr:要释放的首地址

释放的是开辟的空间地址,而我们的指针还在,此时没有指向,变成野指针,需要给它赋值为NULL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
7 #include <stdio.h>
8 #include <stdlib.h>
9 int main(int argc, char *argv[])
10 {
11 int *p = (int *)malloc(sizeof(int));
12 if(NULL == p)
13 {
14 printf("create error\n");
15 }
16 *p=12;
17 printf("*p=%d\n",*p);
18 free(p);
19 p=NULL;
20
21 return 0;
22 }

二、关键字:

1. const–变量常量化

const修饰指针:

int const *p=&a—-p的内容不能改

int *const p=&a;—-p的指向不能改

2. typedef—重命名

已知数据类型的重命名

1
2
3
4
5
6
7
8
9
 7 #include <stdio.h>
8 typedef int A;
9 int main(int argc, char *argv[])
10 {
11 A a=12;
12 printf("a=%d\n",a);
13 return 0;
14 }
~

3. define—-宏定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
7 #include <stdio.h>
8 #define A char *
9 typedef char * B;
10 int main(int argc, char *argv[])
11 {
12 A a,b; //这样定义后会发现b是char类型而不是char*
13 B c,d; // a=8,b=1,c=8,d=8
14 printf("a=%ld\n",sizeof(a));
15 printf("b=%ld\n",sizeof(b));
16 printf("c=%ld\n",sizeof(c));
17 printf("d=%ld\n",sizeof(d));
18
19 return 0;
20 }

练习:利用宏定义求出两个数之间的最大值

1
2
3
4
5
6
7
8
9
10
7 #include <stdio.h>
8 #define MAX (5>3)?5:3
9 typedef char * B;
10 int main(int argc, char *argv[])
11 {
12 printf("MAX=%d\n",MAX);
13
14 return 0;
15 }
~

4. static—静态区域存储

修饰全局变量,限制作用域
修饰局部变量,延长声明周期(只会被初始化一次)
修饰函数,限制作用域

5. extern—调用外部文件变量

只能调用全局变量

定义: int extern a;

编译的时候要和调用的文件一起编译

6. struct—结构体

本质还是一个数据类型,只是里面可以放很多成员,这些成员数据类型可以一样,也可以不一样,可以是已知的数据类型,也可以是构造类型

一般形式:

1
2
3
4
5
struct 结构体名{
数据类型 成员1;
数据类型 成员2;
......
};--分号不能省略

结构体的初始化:(按照顺序初始化)

全局变量初始化:在头文件下,主函数上,就是在构造完这个结构体立马定义一个变量

局部变量初始化,在主函数里面定义一个结构体变量

结构体的成员的调用:变量名.成员

结构体的赋值:

1、结构体赋结构体

2、每个成员单独赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 7 #include <stdio.h>
8 #include <string.h>
9 struct zc{
10 int age;
11 char name[32];
12 char sex[32];
13 }a={18,"zhangcheng","man"};//全局变量初始化
14 int main(int argc, char *argv[])
15 {
16 struct zc b={20,"cheng","man"};//局部变量初始化
17 struct zc c;
18 c.age=22;
19 strcpy(c.name,"zhang");
20 strcpy(c.sex,"women");
21 printf("a.age=%d\n",a.age);//成员的调用
22 printf("b.age=%d\n",b.age);
23 printf("c.sex=%s\n",c.sex);
24 return 0;
25 }

  • 结构体的大小:

规则:

  如果是64位操作系统默认按照8byte对齐,但是如果最大的数据类型小于8byte,就按照最大数据类型大小对齐(就是最大成员数据类型的倍数)

  如果是32位操作系统默认按照4byte对齐,但是如果最大的数据类型小于4byte,就按照最大数据类型大小对齐(就是最大成员数据类型的倍数)

  如果对齐字节能够放下后面的成员,就会在当前字节放

  并且存储遵循偶数存储原则

例:
struct temp{char a;short b;char c;int d}
结构体大小为12字节。因为以里面最大的int开辟空间为4字节
第一个4字节有a,b,因为偶数原则,a占1,b占3,4
第二个4字节有c,占1
第三个4字节有d占1,2,3,4

static 修饰的不占空间

  • 空类 占1个字节 (占位符)
  • 成员函数、静态成员、友元函数 占 0字节
  • 虚函数占 4字节 (虚函数表占4字节)
  • 非静态成员 满足字节序对齐
Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2024 nakano-mahiro
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信