c语言学习-6
六、函数
1.函数的嵌套调用
main.c
#include "func.h"
/*
一个C程序由一个主函数和若干其他函数构成。
一个较大的程序可分为若干程序模块,每个模块实现一个特定的功能。
在高级语言中用子程序实现模块的功能,而子程序由函数来实现。
函数间的调用关系是,由主函数调用其他函数,其他函数也可以互相调用。
同一个函数可以被一个或多个函数调用任意次。
为什么strcpy,没有头文件也可以运行,原因是这个函数的实现是在C库中(dll),头文件只是声明
C 语言的编译和执行具有以下特点
(1)一个 C 程序由一个或多个程序模块组成,每个程序模块作为一个源程序文件。
对于较大的程序,通常将程序内容分别放在若干源文件中,再由若干源程序文件组成一个 C 程序。
这样处理便于分别编写、分别编译,进而提高调试效率。一个源程序文件可以为多个C 程序共用。
(2)一个源程序文件由一个或多个函数及其他有关内容(如命令行、数据定义等)组成。
一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位而不是以函数为单位进行编译的。
main.c 和 func.c 分别单独编译,在链接成为可执行文件时,
main 中调用的函数 printstar和 print_message 才会通过链接去找到函数定义的位置。
(3)C 程序的执行是从 main 函数开始的,如果在 main 函数中调用其他函数,
那么在调用后会返回到 main 函数中,在main 函数中结束整个程序的运行。
(4)所有函数都是平行的,即在定义函数时是分别进行的,并且是互相独立的。
一个函数并不从属于另一函数,即函数不能嵌套定义。函数间可以互相调用,但不能调用 main 函数。
main函数是由系统调用的,例 6.1.1 的 main 函数中调用 print_message 函数,
而 print_message 函数中又调用 printstar 函数,我们把这种调用称为嵌套调用。
*/
int main()
{
int a = 10;
a = printstar(a);//printstar(a)函数调用,a是一个实参
print_message();//调用print_message
printstar(a);
printf("a=%d\n", a);
return 0;
}
func.c
#include "func.h"
//printstar函数定义,就是函数实现
int printstar(int i)//i即为形式参数,也叫形参
{
printf("***************************\n");
printf("printstar %d\n", i);
return i + 3;
}
void print_message()//可以调用printstar
{
printf("how do you do\n");
printstar(3);
}
func.h
#pragma once
#include<stdio.h>//头文件放的是函数声明
/*
1>D:\cbook\第六章 函数\01.函数的嵌套调用\main.c(14,15): warning C4013: “printstar”未定义;假设外部返回 int
1>D:\cbook\第六章 函数\01.函数的嵌套调用\main.c(15,15): warning C4013: “print_message”未定义;假设外部返回 int
*/
int printstar(int i);//函数声明
void print_message();//函数声明
***************************
printstar 10
how do you do
***************************
printstar 3
***************************
printstar 13
a=13
D:\cbook\第六章 函数\Debug\01.函数的嵌套调用.exe (进程 17168)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
2.全局变量
#include<stdio.h>
#include<stdlib.h>
int i = 10;//全局变量,在函数外定义的变量叫全局变量
int a = 20;
void print(int m)//自定义的print函数
{
printf("print m = %d\n", m);
printf("print i = %d\n", i);
}
void printa(int m)//自定义的print函数
{
printf("printa m = %d\n", m);
printf("printa a = %d\n", a);
}
int main()
{
printf("main i = %d\n", i);
printf("main a = %d\n", a);
i = 5;
print(i);
int a = 2;
printa(a);
return 0;
}
main i = 10
main a = 20
print m = 5
print i = 5
printa m = 2
printa a = 20
D:\cbook\第六章 函数\Debug\02.全局变量.exe (进程 1228)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
3.递归调用
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
/*
求n的阶乘,递归
函数自己调用自己就是递归
*/
int f(int n)
{
if (1 == n)
{
return 1;//一定写结束条件
}
return n * f(n - 1);//第一步写好公式
}
/*
走楼梯
假如有n 个台阶,一次只能上1 个台阶或2 个台阶,请问走到第n 个台阶有几种走法?
为便于读者理解题意,这里举例说明如下:
假如有3 个台阶,那么总计就有3 种走法:
第一种为每次上1 个台阶,上3 次;
第二种为先上2 个台阶,再上1 个台阶;
第三种为先上1 个台阶,再上2 个台阶。
*/
int step(int n)
{
if (1 == n)
{
return 1;
}
if (2 == n)
{
return 2;
}
return step(n - 1) + step(n - 2);
}
int main()
{
int n, ret;
printf("求n的阶乘:\n");
scanf("%d", &n);//请输入数字的大小
ret = f(n);
printf("ret=%d\n", ret);
printf("走楼梯:\n");
scanf("%d", &n);//请输入台阶数
ret = step(n);
printf("ret=%d\n", ret);
return 0;
}
求n的阶乘:
5
ret=120
走楼梯:
3
ret=3
D:\cbook\第六章 函数\Debug\03.递归调用.exe (进程 11808)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
求n的阶乘:
6
ret=720
走楼梯:
5
ret=8
D:\cbook\第六章 函数\Debug\03.递归调用.exe (进程 7720)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
4.局部变量与全局变量
main.c
#include "func.h"
/*
局部变量离自己最近的大括号有效
*/
extern int k;
void print1()
{
printf("print1 k=%d\n", k);
}
int k = 10;//static修饰全局变量,该变量不能被其他文件借用
int main()
{
int i = 10;
{
int j = 5;
}//局部变量的有效范围是离自己最近的花括号
printf("i=%d,k=%d\n", i, k);
print1();
print();
return 0;
}
func.c
#include "func.h"
extern int k;//借用main.c文件中的全局变量k
//static修饰函数,函数只能在本函数、本文件内使用
void print()
{
static int t = 0;//只初始化一次
t++;
printf("print execute %d\n", t);
printf("print k=%d\n", k);
}
func.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
void print();
i=10,k=10
print1 k=10
print execute 1
print k=10
D:\cbook\第六章 函数\Debug\04.局部变量与全局变量.exe (进程 12144)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
5.指针的传递题目
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
/*
Description
输入一个整型数,存入变量i,通过子函数change把主函数的变量i除2,然后打印i,
例如如果输入的为10,打印出5,如果输入的为7,打印出3
Input
一个整型数
Output
对应整型数除2后的商
*/
void change(int *p)//相当于p=&i,*p=*&i等价于i
{
*p = *p / 2;
}
int main()
{
int i;
scanf("%d", &i);
change(&i);
printf("%d\n", i);
return 0;
}
10
5
D:\cbook\第六章 函数\Debug\05.指针的传递题目.exe (进程 16672)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
7
3
D:\cbook\第六章 函数\Debug\05.指针的传递题目.exe (进程 18776)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
6.malloc的使用题目
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
/*
Description
输入一个整型数,然后申请对应大小空间内存,然后读取一个字符串,字符串的输入长度小于最初输入的整型数大小,
最后输出输入的字符串即可(无需考虑输入的字符串过长,超过了内存大小);
注意下面问题:
char *p;
scanf("%d",&n);
p=malloc(n);
scanf("%c",&c);//注意在scanf和gets中间使用scanf("%c",&c),去除换行
gets(p);
Input
一个整型数和一个字符串,例如
10
hello
Output
输出输入的字符串,上面输入的是hello,那么输出hello
*/
//malloc可以帮我们实现动态数组
int main()
{
char* p;
int n;//储存申请多少个字节的空间大小
scanf("%d", &n);
p = (char*)malloc(n);//malloc申请空间的单位是字节
char c;
scanf("%c", &c);//注意在scanf和gets中间使用scanf("%c",&c),去除换行
//gets(p);//如果不通过上面的scanf去消除\n,gets不会卡住
fgets(p, n, stdin);
puts(p);
return 0;
}
10
hello
hello
D:\cbook\第六章 函数\Debug\06.malloc的使用题目.exe (进程 11668)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
7.走台阶问题
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
/*
Description
假如有n个台阶,一次只能上1个台阶或2个台阶,请问走到第n个台阶有几种走法?
为便于读者理解题意,这里举例说明如下︰
假如有3个台阶,那么总计就有3种走法:
第一种为每次上1个台阶,上3次;
第二种为先上2个台阶,再上1个台阶;
第三种为先上1个台阶,再上2个台阶。
输入为n,输出为走到第n个台阶有几种走法
Input
比如输入是3
Output
如果输入是3,走到第3个台阶的走法总计有3种,1,1,1和1,2和2,1,输出为3
*/
//递归调用
//递归的判断条件一定是写到递归公式之前的
int step(int m)
{
//递归的结束条件
if (1 == m || 2 == m) {
return m;
}
//写递归公式
return step(m - 1) + step(m - 2);
}
int main()
{
int n;//存储台阶
scanf("%d", &n);
int result = step(n);
printf("走到第%d个台阶有%d种走法\n", n, result);
return 0;
}
5
走到第5个台阶有8种走法
D:\cbook\第六章 函数\Debug\07.走台阶问题.exe (进程 4260)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
七、结构体
1.结构体
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
/*
* struct 结构体明
* {成员列表};
*/
//结构体所占用空间是68个字节,因为存在对齐,对齐的目的是
//为了提高cpu访问内存的效率
//先声明一个结构体类型
struct student {
int num;//num是结构体成员
char name[20];
char sex;
int age;
float score;
char addr[30];
};//结构体类型声明,注意最后一定要加分号
int main()
{
//后定义变量名
//struct student student1, student2;
struct student s = { 1001,"lele",'M',20,85.4,"Shenzhen" };//定义及初始化
struct student sarr[3];
int i;
//sarr[0].num = 1002;
//sarr->num = 1003;
printf("%d %s %c %d %5.2f %s\n", s.num, s.name, s.sex, s.age, s.score, s.addr);
for (i = 0; i < 3; i++)
{
scanf("%d%s %c%d%f%s", &sarr[i].num, &sarr[i].name, &sarr[i].sex, &sarr[i].age, &sarr[i].score, &sarr[i].addr);
}
for (i = 0; i < 3; i++)
{
//printf("%d %s %c %d %5.2f %s\n", sarr[i].num, sarr[i].name, sarr[i].sex,sarr[i].age, sarr[i].score, sarr[i].addr);
printf("输出 sarr[%d] = {%d,%s,%c,%d,%5.2f,%s}\n", i, sarr[i].num, sarr[i].name, sarr[i].sex, sarr[i].age, sarr[i].score, sarr[i].addr);
}
return 0;
}
/*
1002 lily M 20 85.40 Shenzhen
1003 Marry M 25 95.30 Guangzhou
1004 hehe M 18 92.50 Xiamen
*/
1001 lele M 20 85.40 Shenzhen
1002 lily M 20 85.40 Shenzhen
1003 Marry M 25 95.30 Guangzhou
1004 hehe M 18 92.50 Xiamen
输出 sarr[0] = {1002,lily,M,20,85.40,Shenzhen}
输出 sarr[1] = {1003,Marry,M,25,95.30,Guangzhou}
输出 sarr[2] = {1004,hehe,M,18,92.50,Xiamen}
D:\cbook\第七章 结构体\Debug\01.结构体.exe (进程 11504)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
2.结构体指针
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
//结构体指针
struct student {
int num;
char name[20];
char sex;
};
int main()
{
struct student s = { 1001,"lidongmei",'M' };
struct student* p;//定义结构体指针
int num, i;
p = &s;
printf("%d %s %c\n", p->num, p->name, p->sex);//结构体变量成员选择,方式一
printf("%d %s %c\n", (*p).num, (*p).name, (*p).sex);//指针的成员选择,方式二
//结构体数组初始化
struct student sarr[3] = { 1001,"lilei",'M',1005,"zhangsan",'M', 1007,"lisi",'M', };
p = sarr;
printf("-----------------------------\n");
num = p->num++;//num=p->num;p->num++ 1002
printf("num=%d,p->num=%d\n", num, p->num);//1001,1002
for (i = 0; i < 3; i++)
{
printf("输出 sarr[%d] = {%d,%s,%c}\n", i, sarr[i].num, sarr[i].name, sarr[i].sex);
}
num = p++->num;//num=p->num;p++ 1005
printf("num=%d,p->num=%d\n", num, p->num);//1002,1005
for (i = 0; i < 3; i++)
{
printf("输出 sarr[%d] = {%d,%s,%c}\n", i, sarr[i].num, sarr[i].name, sarr[i].sex);
}
return 0;
}
1001 lidongmei M
1001 lidongmei M
-----------------------------
num=1001,p->num=1002
输出 sarr[0] = {1002,lilei,M}
输出 sarr[1] = {1005,zhangsan,M}
输出 sarr[2] = {1007,lisi,M}
num=1002,p->num=1005
输出 sarr[0] = {1002,lilei,M}
输出 sarr[1] = {1005,zhangsan,M}
输出 sarr[2] = {1007,lisi,M}
D:\cbook\第七章 结构体\Debug\02.结构体指针.exe (进程 18136)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
3.typedef的使用
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
/*
typedef的作用就是起别名
*/
//给结构体类型起别名,叫stu,起了结构体指针类型的别名,叫pstu
typedef struct student {
int num;
char name[20];
char sex;
}stu,* pstu;//pstu等价于struct student *
typedef int INTEGER;//为什么要对int起别名,为了实现代码即注释
int main()
{
stu s = { 1001,"lidongmei",'M' };
pstu p;//stu* p1,那么p1也是一个结构体指针
INTEGER i = 10;
p = &s;
printf("i=%d,p->num=%d\n", i, p->num);
return 0;
}
i=10,p->num=1001
D:\cbook\第七章 结构体\Debug\03.typedef的使用.exe (进程 6604)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
4.C++的引用
main.cpp
#include<stdio.h>
#include<stdlib.h>
//把&符号写到形参的位置是C++的语法,称为引用,这个时候操作b和在主函数里边使用a等价的
void modify_num(int &b)
{
++b;
}
void modify_pointer(int *&q)//在子函数操作q和主函数操作q手法一致
{
//不能将“void *”类型的值分配到"int *”类型的实体
//说明要进行强制类型转换
q = (int*)malloc(20);
q[0] = 5;
}
//C++
int main()
{
int a = 10;
modify_num(a);
printf("a=%d\n", a);
int *p = NULL;
modify_pointer(p);
printf("p[0]=%d\n", p[0]);
return 0;
}
a=11
p[0]=5
D:\cbook\第七章 结构体\Debug\04.C++的引用.exe (进程 19748)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .