一.循环,递归,遍历,迭代
1.循环
一组被重复执行的语句称之为循环体,能否继续重复,决定循环的终止条件。循环结构是在一定条件下反复执行某段程序的流程结构,被反复执行的程序被称为循环体。
c语言中的循环语句
第一种
for( ; ; )
{}
第二种
while()
{}
第三种
do
{}
while();
2.递归——重复调用自己
程序调用自身的编程技巧称为递归
递归的缺点:
递归算法解题相对常用的算法如普通循环等,运行效率较低。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。
6-6 爬楼梯(递归版) (10分)
楼梯有 n 级台阶,每一步可以跨越 1 ~ 3 级台阶。请问一共有多少种上法。
函数原型
double Climb(int step);
说明:参数 step 是台阶数。若 step>0,则函数值是爬楼梯的方法总数。若 step≤0,则函数值为 0。
裁判程序
#include <stdio.h>
double Climb(int step);
int main()
{
int n;
scanf("%d", &n);
printf("%g\n", Climb(n));
return 0;
}
/* 你提交的代码将被嵌在这里 */
测试数据
输入样例 输出样例
-5 0
0 0
1 1
2 2
3 4
4 7
39 12960201916
要求:不得使用循环语句。
double Climb(int step)
{
if(step<=0)
return 0;
else if(step<=2&&step>0)
return step;
else if(step==3)
return 4;
else
return Climb(step-1)+Climb(step-2)+Climb(step-3);
}
简单解释
假如皇上想知道全国的人口总数,他会交给大臣,大臣会交给知县,知县会交给办事人员,办事人员会直接统计出数据交给知县,知县整合所有数据交给大臣,大臣整合所有数据交给皇上,最后皇上整合所有大臣的数据就知道全国的总人数
3.遍历——全部访问
所谓遍历(Traversal),是指沿着某条搜索路线,依次对树(或图)中***每个节点均做一次访问***。
4.迭代——不断覆盖旧值
迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。
重复执行一系列运算步骤,从前面的量依次求出后面的量的过程。此过程的每一次结果,都是由对前一次所得结果施行相同的运算步骤得到的。例如利用迭代法求某一数学问题的解。
对计算机特定程序中需要反复执行的子程序(一组指令),进行一次重复,即重复执行程序中的循环,直到满足某条件为止,亦称为迭代。
二.指针
1.指针也叫内存地址,指针变量是用来存放内存地址的变量
2.不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同
3.指针是一个占据存储空间的实体在这一段空间起始位置的相对距离值。在C/C++语言中,指针一般被认为是指针变量,指针变量的内容存储的是其指向的对象的首地址,指向的对象可以是变量(指针变量也是变量),数组,函数等占据存储空间的实体。
4.在计算机中, 所有的数据都是存放在存储器中的, 不同的数据类型占有的内存空间的大小各不相同。内存是以字节为单位的连续编址空间, 每一个字节单元对应着一个唯一的编号, 这个编号被称为内存单元的地址。比如: int类型占4个字节, char类型占1个字节等。**内存为变量分配存储空间的首个字节单元的地址, 称之为该变量的地址。**地址用来标识每一个存储单元, 方便用户对存储单元中的数据进行正确的访问。在高级语言中地址形象地称为指针。
地址与指针
指针相对于一个内存单元来说,指的是单元的地址,该单元的内容里面存放的是数据。在C语言中,允许用指针变量来存放指针,因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。
指针变量及其定义
指针变量是存放一个变量地址的变量,不同于其他类型变量,它是专门用来存放内存地址的,也称为地址变量。定义指针变量的一般形式为:类型说明符*变量名。
类型说明符表示指针变量所指向变量的数据类型;表示这是一个指针变量;变量名表示定义的指针变量名,其值是一个地址,例如:charp1;表示p1是一个指针变量,它的值是某个字符变量的地址。
5.注意事项
不允许把一个数赋予指针变量
错误
分析:前面例子中定义了一个指针变量pointer,但是不能直接把200赋值给指针变量pointer。后面的例子中定义了一个整型变量a,并赋初始值为200,又定义了一个指针变量pointer,这个变量指向整型数据,pointer中只能用来存放整型变量的地址,而不能直接把整型变量a赋值给这个指针变量pointer。所以可以把a的地址赋值给pointer;应改成
改正
改变实参不代表改变形参
错误
改正
temp=*X;
*X=*Y;
*Y=temp;
字符串指针
错误
string1是一个指针变量,指向字符串"I love China!",指针变量string1存放的是这个字符串的首地址。所以输出的是一个字符串,应改写成printf(“%s\n”,string1);
改正
int main()
{
char *string1="I love China!";
printf("%s\n",string1);
return 0;
}
调用函数指针
错误·
函数指针变量不能进行算术运算,这是与数组指针变量不同的。数组指针变量加减一个整数可使指针移动指向后面或前面的数组元素,而函数指针的移动是毫无意义的。函数调用中“(指针变量名)”的两边的括号不可少,其中的“”不应该理解为求值运算,在此处只是一种表示符号。要把“z=*pomax(x,y);”改成“z=(*pomax)(x,y);”。
改正
#include<stdio.h>
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
int main()
{
int (*pomax)(int,int);
int x,y,z;
pomax=max;
printf("intput tow numbers:\n");
scanf("%d,%d",&x,&y);
z=(*pomax)(x,y);
printf("maxnum=%d",z);
return 0;
}
#include <stdio.h>
int main()
{
int a = 20,b = 30;
int t = a;
a = b;
b = t;
printf("%d %d\n", a, b);
return 0;
}
程序执行结果为:
30 20
。
#include <stdio.h>
void mySwap(int, int);
int main()
{
int a = 20,b = 30;
mySwap(a, b);
printf("%d %d\n", a, b);
return 0;
}
void mySwap(int m, int n)
{
int t = m;
m = n;
n = t;
}
程序执行结果为:
20 30
。
#include <stdio.h>
void mySwap(int*, int*);
int main()
{
int a = 20,b = 30;
mySwap(&a, &b);
printf("%d %d\n", a, b);
return 0;
}
void mySwap(int *m, int *n)
{
int t = *m;
*m = *n;
*n = t;
}
程序执行结果为:
30 20
。
#include <stdio.h>
void mySwap(int*, int*);
int main()
{
int a = 20,b = 30;
mySwap(&a, &b);
printf("%d %d\n", a, b);
return 0;
}
void mySwap(int *m, int *n)
{
int *t = m;
m = n;
n = t;
}
程序执行结果为:
20 30
。
变量其实同时有地址和值两个属性,它的数据值,存储在某个内存地址中,这个值被称为变量的右值。我们也可认为右值的意思是被读取的值。它的地址值,即存储数据值的那块内存的地址,被称为变量的左值。
为什么使用scanf函数变量前面要加&,printf函数变量前面不加&?
因为scanf要变量里写入数值,所以它必须知道那个变量的地址,至于变量里存的是什么值,它是不关心的,反正写入之后就被覆盖成新的值了。 对比printf,是要输出变量里的数值,它只管要那个值,至于地址在哪,它也无所谓。
而变量名字写在函数参数里面时,和写在赋值号右边一样,也是代表值,可是像scanf这种函数,它确实需要地址,编译器只认为在赋值符左边的才是左值,那在scanf函数里,没法自动获得左值,这时就需要我们手动加一个取地址运算符&,明确告诉编译器去获取变量的地址给这scanf来用。
图1
再仔细看看上面的图片,我们会发现这里的**&a是int ∗类型,a是int类型**。&其实是取地址运算符,&a得到的就是变量a分配的内存地址。&a表达式的值是地址,所以咱们在编译器里看到&a是int ∗类型,即指针类型。
类似的,我们可以使用下面语句定义一个指针变量:
int *p;
和普通变量不同,指针变量赋值必须指向一个已经分配的内存地址,例如:
int a = 2;
int *p = &a;
我们定义了一个变量a,其值为2。我们又定义了一个指针变量p(这里的∗是指针变量定义符号),将其初始化为变量a的地址。 如果我们想使用指针变量p来修改变量a的值要怎么做呢?使用指针运算符,如下:
*p = 3;
这里的∗是运算符。因为p是a的地址(p指向变量a),∗p就是访问p指向的对象,∗p赋值为3,就是a赋值为3。
#include <stdio.h>
int main()
{
int a[10000];
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
for (int i = n - 1; i >= 0; i--)
printf("%d ", a[i]);
return 0;
将上面的数组元素访问从下标引用改成指针引用方式。
我们已经知道a是数组名,实际上就是数组分配内存的首地址,也是数组元素a[0]的地址。也就是说如果我们定义:
int *p = a;
则指针变量p的值就是数组a的首地址,也是a[0]的地址,此时我们执行∗p运算,则得到a[0]的值。如果我们执行p++运算,p 就指向了a[1]的地址,同理,p=p+i,p就指向数组第i个元素的地址。此时,我们再执行∗p,则得到了a[i]的值。
#include <stdio.h>
int main()
{
int a[10000];
int *p;
int n;
scanf("%d", &n);
for (p = a; p < a + n; p++)
scanf("%d", p);//此处为a[i]地址,即当前指针变量p指向的地址
for (p = a + n - 1;p>=a; p--)//从后往前输出
printf("%d ", *p);//输出当前元素值
return 0;
}
三.结构体
结构体课前预习
C 语言中提供了一种为某一已知类型添加别名的方式——typedef(表示 type define)
使用的方法是:
typedef 原类型名 类型别名
那复数的类型定义可以改为
typedef struct complex{
int real;
int imag;
}complex;
三种相同解法
第一种
struct student
{
char number[5];
char name[11];
int shu1;
int shu2;
int shu3;
}stud[11];//stud是变量名
int main()
{
scanf("%s %s %d %d %d",&stud[i].number,&stud[i].name,&stud[i].shu1,&stud[i].shu2,&stud[i].shu3);
}
第二种
typedef struct student
{
char number[5];
char name[11];
int shu1;
int shu2;
int shu3;
};
int main()
{
struct student stud[11];
scanf("%s %s %d %d %d",&stud[i].number,&stud[i].name,&stud[i].shu1,&stud[i].shu2,&stud[i].shu3);
}
第三种
typedef struct student
{
char number[5];
char name[11];
int shu1;
int shu2;
int shu3;
}student1;//student1是类型名,类型起别名
int main()
{
student1 stud[11];
scanf("%s %s %d %d %d",&stud[i].number,&stud[i].name,&stud[i].shu1,&stud[i].shu2,&stud[i].shu3);
}
如果是结构体指针,使用箭头
结构体指针名->结构体成员元素名
x->real
(*结构体指针名).结构体成员元素名(也可,但是一定要加括号,“*”优先级小于圆点运算符)
(*x).real
也可用下面这种方法
x[i].real
指针结构体的多种写法
四.
1.c语言中函数参数传递方式是值传递
五.
5-2,5-5