复习
定义函数
4部分
返回值类型 函数名(参数列表(可以为空))
{
函数体
}
形参和实参
形参:定义在参数列表中的变量。和定义在{}里的变量只有唯一区别,形参需要被实参初始化。每个形参都要写类 型,形参之间用,隔开。
实参:调用函数时,写在()里的变量或者常量。
返回值
return 返回的值(可以是常量也可以是变量)。
一旦函数执行到return,函数马上结束。然后会把返回值给主调函数。主调函数如果使用返回值,可以将返回值赋值给某个变量,也可以无视返回值。
int fun()
{
return 1;
}
int main()
{
fun();//本次调用fun函数的返回值被主调函数无视了
int a = fun();//本次调用fun函数的返回值被主调函数赋值给了变量a。
return 0;
}
如果函数没有返回值。返回值类型写void
变量的生命周期和作用域
{}
变量的作用域在定义它的{}中。
局部变量和全局变量:
定义的变量没有被任何{}包含(也可以理解成没有被任何函数包含)是全局变量。否则就是局部变量。
初始值:全局变量如果不初始化,默认值是0。局部变量不初始化是随机值。
生命周期:全局变量在程序开始时创建,程序结束后释放;局部变量{}执行时创建,{}执行完毕释放。
作用域:全局变量整个程序都可以使用。局部变量只能在定义它的{}内使用。
重名:全局变量和局部变量在同一个作用域内都不能重名。可以在包含的小作用域中重名。
作业1:
定义函数判断一个整数是否为质数
参数:要判断的整数
返回值:0 表示不是质数 1 表示是质数
#include <stdio.h>
//在函数调用那一行的上面,必须有该函数的定义或者声明;不然编译器可能会报错。
int isPrime(int num);//函数声明 将定义函数的第一行赋值,在后面加;
int main()
{
int num;
for(num = 100;num < 201;num++)
{
if(isPrime(num))//直接判断函数的返回值 if(isPrime(num) == 1)
{
printf("%d\n", num);
}
}
return 0;
}
int isPrime(int num)
{
int i;
for(i = 2;i < num;i++)
{
if(num%i == 0)
{
return 0;//在这里已经判断出不是质数,函数可以立刻停止
}
}
//只要循环能走完,必然是质数
return 1;
}
作业2:
定义函数求任意整数的位数
例如:12345
返回:5
参数:要计算整数
返回值:整数的位数
#include <stdio.h>
//在函数调用那一行的上面,必须有该函数的定义或者声明;不然编译器可能会报错。
int numBits(int num);
int main()
{
printf("%d\n", numBits(12345));//直接打印函数返回值
return 0;
}
int numBits(int num)
{
int count = 0;
while(num != 0)
{
num /= 10;
count++;
}
return count;
}
作业3:
35选7使用函数封装。
#include <stdio.h>
#include <stdlib.h>
int results[7] = {0};
int userNums[7] = {0};
int isResutltExists(int num, int index);
/*
初始化结果数组 随机7个不重复的数放到数组results里
*/
void initResutlts()
{
int i;
//1 生成7个随机数
for(i = 0;i < 7;i++)
{
do
{
results[i] = rand()%35+1;//随机一个数
}while(isResutltExists(results[i], i));
}
}
/*
判断一个整数是否已经在results数组中存在
参数1:要判断的数
参数2:这个数在数组中的位置
返回值:1 存在 0 不存在
*/
int isResutltExists(int num, int index)
{
int j;
for(j = 0;j < index;j++)
{
if(results[j] == num)
{
return 1;//只要找到重复的,函数马上结束
}
}
return 0;
}
int isIn1to35(int num);
int isUsernumExists(int num, int index);
/*
用户输入函数 用户输入7个1~35之间不重复的数,存在userNums数组中
*/
void userInput()
{
int i;
for(i = 0;i < 7;i++)
{
//输入的数不在1~35之内或者 有重复都循环
do
{
scanf("%d", &userNums[i]);
}while(isIn1to35(userNums[i])==0 || isUsernumExists(userNums[i], i)==1);
}
}
/*
判断整数是否在1~35之间
参数:要判断的整数
返回值:0 不在 1 在
*/
int isIn1to35(int num)
{
if(num<1 || num>35)
{
printf("超出了1~35的范围\n");
return 0;
}
else
{
return 1;
}
}
/*
判断用户输入的数在 userNum数组中是否有重复
参数1:要判断的数
参数2:这个数在数组中的位置
返回值:1 存在 0 不存在
*/
int isUsernumExists(int num, int index)
{
int j;
for(j = 0;j < index;j++)
{
if(userNums[j] == num)
{
return 1;//只要找到重复的,函数马上结束
}
}
return 0;
}
/*
查找userNum和Results数组中重复数字的个数
返回值:相同数字的个数
*/
int sameInUsernumsAndResults()
{
int i, j;
int count = 0;
for(i = 0;i < 7;i++)
{
int j;
for(j = 0;j < 7;j++)
{
if(results[i] == userNums[j])
{
count++;
break;
}
}
}
return count;
}
/*
根据相同数字的个数计算开奖结果
*/
void showResult(int count)
{
switch(count)
{
case 7:
printf("1000 0000\n");
break;
case 6:
printf("10000\n");
break;
case 5:
printf("100\n");
break;
case 4:
printf("2\n");
break;
default:
printf("谢谢参与\n");
}
}
/*
显示两个数组中的元素
*/
void showArrs()
{
int i;
for(i = 0;i < 7;i++)
{
printf("%d %d\n", results[i], userNums[i]);
}
}
int main()
{
srand(time(0));
initResutlts();//生成随机数
userInput();//用户输入
int count = sameInUsernumsAndResults();//获得相同数字的个数
showResult(count);//显示开奖结果
showArrs();//显示数组信息
return 0;
}
1、指针变量
指针变量的本质是变量,变量用来存放数据,描述逻辑。
1. 什么是指针变量
指针变量是存放地址的变量。
变量的运算,是对变量中存放数据的运算。
int a;
printf("%d\n", a+1);//不知道结果是多少,因为变量里没有值。
2. 地址的运算
地址只有两种运算:
1、地址偏移 + -
int a;
printf("%p %p\n", &a, &a+1);
int 类型地址+1 是+4字节
char a;
printf("%p %p\n", &a, &a+1);
char类型地址+1 是+1字节
地址偏移一个单位,偏移的字节数是地址类型的大小。
2、间接运算 * 间接运算是一元运算(只有一个操作数) 乘法是二元运算(有两个操作数)
*在操作数的左边。
间接运算可以通过地址访问地址对应的变量。
间接运算的到的结果不是一个数值,得到的是地址对应的变量的本身。间接运算的结果是一个变量。
#include <stdio.h>
int main()
{
int a;
*(&a) = 10;//因为*(&a)运算得到变量a 所以把10赋值给了a
printf("%d\n", a);
return 0;
}
3. 定义指针变量
任何符号出现在声明的语句中,都不是运算符。声明是:解释标识符的身份。出现了新的标识符,就是声明语句。
int *p;//定义变量p,*说明p是指针变量,既然是指针变量,就要存放地址,所以我们还要告诉编译器p是存放什么类型地址的指针变量。int类型。
指针的声明属于复杂声明,一步定义不完,就是复杂声明。
这里的*是说明p是指针变量身份的符号,它不是运算符!!!
能够在声明的语句中表达标识符身份的符号,一共有三个!
*指针变量
[] 数组
() 函数
指针变量的名字可以随意命名(符合语法的前提下)。因为指针变量的英文名point,所以我们经常给指针变量命名p。
1. 初始化
int a;
int *p = &a;//这里的是给p初始化,因为这里的*不是运算符。
2. 赋值
int a;
int *p;
p = &a;//当指针变量p中存放a的地址,我们说:p指向a。
下面的示例毫无意义,仅仅是为了演示语法。
#include <stdio.h>
int main()
{
int a;
int* p = &a;//*是表达p是指针变量身份的符号
*p = 10;//*是间接运算符,*p等价于*(&a) 得到的结果是变量a本身
printf("%d\n", a);
return 0;
}
4.输出指针变量
%p %x
5.指针变量的意义
示例1:
在被调函数中使用主调函数中的变量,通过地址传递。
void putNum(int* num)//定义指针变量形参
{
*num = 10;//因为num指向main中的a,所以*num得到变量a,所以这里是给main中的变量a赋值
}
int main()
{
int a;
putNum(&a);//使用变量a的地址,对putNum函数的形参num初始化
printf("%d\n", a);
return 0;
}
练习1:
分别定义 int float 的变量,通过指针赋值,并输出变量的值和指针变量的值(地址)。 (熟悉指针)
#include <stdio.h>
int main()
{
int a;
float b;
int* pa;
float* pb;
pa = &a;//给指针变量赋值
pb = &b;
*pa = 10;//pa指向a,所以*p得到a
*pb = 10.2;
printf("%d %f\n", a, b);
printf("%p %p\n", pa, pb);
return 0;
}
练习2:
定义函数,通过指针变量实现两个整数的交换。
在被调函数中交换主调函数中的两个变量的值。
#include <stdio.h>
void swap(int* num1, int* num2)
{
int t = *num1;//*num1运算的结果是变量a本身
*num1 = *num2;//*num2运算的结果是变量b本身
*num2 = t;
/*
int t = a;
a = b;
b = t;
*/
}
int main()
{
int a = 10, b = 20;//变量一旦被创建,变量的地址就不能改变
swap(&a, &b);//调用函数时,用&a初始化num1,&b初始化num2,于是我们说num1指向a,num2指向b
printf("%d %d\n", a, b);
return 0;
}
2、指针与数组
1) 数组名就是数组的首地址(第一个元素的地址)。数组名字是地址常量。
2) 数组中的元素在内存中连续存放。
int main()
{
int a[10];//a是数组名,是数组的首元素地址,也就是a[0]的地址 ,a等价于&a[0],a是常量
int i;
for(i = 0;i < 10;i++)
{
printf("%p\n", &a[i]);
}
return 0;
}
运行结果:
因为int类型的元素,占4个字节,所以每个元素之间相差4个字节。
正好符合地址偏移的规律。
示例2:
#include <stdio.h>
int main( )
{
int a[10] = {12,23,34,45,56,67,78,9,98,33};
int *p;
p = a;//给指针变量p赋值,因为a是数组名,是数组首元素地址,所以不需要对a取地址
//数组名是数组元素类型的地址。
//当指针变量p存放数组a的首地址时,我们说指针变量p指向数组a。
int i;
for(i=0; i<10; i++)
{
//printf("%d", *(p+i)); //当i=0时,p+i指向a[0],当i=1时,p+i指向a[1]
printf("%d", p[i]); //p[i]等价于*(p+i) a[i] 可以写成 *(a+i)
}
return 0;
}
练习3:
使用指针表达每个元素的值,将数组元素求和。输出最终结果。
#include <stdio.h>
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,0};
int* p = a;//定义指针变量p指向数组a
int i;
int sum = 0;
for(i = 0;i < 10;i++)
{
//使用指针遍历数组,和使用数组名遍历数组是一样的,因为指针的运算就是地址的运算,数组名就是地址
sum += p[i];
}
printf("%d\n", sum);
return 0;
}
练习4:
定义字符数组,将字符串放在数组中,定义指针指向数组,并输出数组中保存的内容。
printf(“%s\n”,字符串首地址);
C语言对字符串的处理有两个要素:
-
字符串首地址 作为字符串的开始
-
‘\0’作为字符串的结尾
int main() { char buf[] = "hello world"; //定义字符数组buf,用字符串常量"hello world"初始化这个数组,默认初始化,数组长度为12,因为得给'\0'留空间 char* p = buf;//定义字符类型指针指向字符类型数组buf //%s 需要字符串两个要素,首地址 '\0' %s从我们给的地址开始一个字节一个字节算,算到'\0'结束 printf("%s\n", p+5);//输出数组中存放的字符串 从空格开始输出 printf("%s\n", buf);//输出数组中存放的字符串 return 0; }
示例3:
数组作为函数的参数,一般传递两个参数
1 数组的首地址
2 数组的长度
#include <stdio.h>
/*打印任意整型数组
参数1:是指针变量,存放数组的首地址
参数2:存放数组长度
*/
void print_array(int *p, int n)
{
int i;
for(i=0; i<n; i++)
{
printf("%d, ", p[i]);//p[i]通过指针变量遍历数组
}
}
int main()
{
int a[6] = {1,2,3,4,5,6};
int b[8] = {6,6,6,6,6,6,6,6};
print_array(a,6);//使用a初始化p,p遍历数组a
print_array(b,8);//使用b初始化p,p遍历数组b
return 0;
}
练习5:
编写函数,实现功能: 对数组求和,并返回给主调函数。
将主调函数中的数组传给被调函数。
#include <stdio.h>
int arrSum(int* arr, int len)
{
int sum = 0;
int i;
for(i = 0;i < len;i++)
{
sum += arr[i];
}
return sum;
}
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,0};
int sum = arrSum(a, 10);
printf("%d\n", sum);
return 0;
}
练习6:
编写函数,实现对任意整型数组的升序排序。
#include <stdio.h>
void sort(int* arr, int len)
{
int i, j;
for(i = 0;i < len-1;i++)//外层循环反复冒泡
{
for(j = 0;j < len-1-i;j++)//内层循环冒一个泡
{
if(arr[j] > arr[j+1])
{
int t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
}
}
int main()
{
int a[10] = {12,2,31,14,25,86,17,8,92,20};
sort(a, 10);
int i;
for(i = 0;i < 10;i++)
{
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
作业1:
编写函数fun:从n个学生的成绩中统计出低于平均分的学生人数,
由返回值返回,平均分存放在形参 ptr_aver 所指的存储单元中(即平均分由参数返回)。
函数最终会有两个结果,人数通过返回值给主调函数,平均分通过形参里的指针变量ptr_aver 给主调函数。
函数原型为:int fun( float *s, int n, float *ptr_aver );
例如:若输入8名学生的成绩如下 : 80.5 60 72 90.5 98 51.5 88 64
则低于平均分的学生人数为:4 (平均分:75.5625)。
作业2:
编写一个函数,将数组中n个数按反顺序存放
假设主调函数中有数组int a[8] = {3,4,5,6,7,9,9,2};
执行完被调函数,数组变成2 9 9 7 6 5 4 3
作业3:
编写函数,实现字符串追加 功能。但不能调用字符串函数;
char s1[20] = “hello ”;
char s2[] = “world”;
调用函数后,将world放到s1数组的”hello ”后面
知识点:当数组存放字符串时,作为函数的参数只需要传递首地址,因为字符串有’\0’作为结尾。
实现strcat的功能,但是不能调用strcat
t;
}
}
}
}
int main()
{
int a[10] = {12,2,31,14,25,86,17,8,92,20};
sort(a, 10);
int i;
for(i = 0;i < 10;i++)
{
printf("%d “, a[i]);
}
printf(”\n");
return 0;
}
## **作业1:**
编写函数fun:从n个学生的成绩中统计出低于平均分的学生人数,
由返回值返回,平均分存放在形参 ptr_aver 所指的存储单元中(即平均分由参数返回)。
函数最终会有两个结果,人数通过返回值给主调函数,平均分通过形参里的指针变量ptr_aver 给主调函数。
函数原型为:int fun( float *s, int n, float *ptr_aver );
例如:若输入8名学生的成绩如下 : 80.5 60 72 90.5 98 51.5 88 64
则低于平均分的学生人数为:4 (平均分:75.5625)。
## **作业2:**
编写一个函数,将数组中n个数按反顺序存放
假设主调函数中有数组int a[8] = {3,4,5,6,7,9,9,2};
执行完被调函数,数组变成2 9 9 7 6 5 4 3
## **作业3:**
编写函数,实现字符串追加 功能。但不能调用字符串函数;
char s1[20] = “hello ”;
char s2[] = “world”;
调用函数后,将world放到s1数组的”hello ”后面
知识点:当数组存放字符串时,作为函数的参数只需要传递首地址,因为字符串有’\0’作为结尾。
实现strcat的功能,但是不能调用strcat