C语言学习第二周总结

更新的有点慢了,主要是学习方面有一些问题,想先学扎实点,好在最后成功拿下,来看看第二周的总结吧

C预言的三种基本控制结构

顺序结构

选择结构

if语句

(1)
if(表达式语句)
{

}
只要表达式语句为真,就执行语句
比如:

int a=200;
int b=100;
if(a > b)
{
	a = a + b;
	printf("%d\n",a);
}

(2)
if (表达式语句)
{
语句1;
}else{
语句2;
}
当表达式的值为真时执行语句1,否则执行语句2
(3)
If(表达式语句1)
{
语句1;
}else if(表达式2){
语句2;
……
}else if(表达式n)
{
语句n;
}else{
语句m;
}
比如:

if(num<50)
{
    cost = 100;
}else if(num<100)// [50,100)
{
	cost = 50;
}else if(num<200)
{
	cost = 30;
}else if(num<300)
{
	cost = 20;
}else
{
	cost = 0;
}

(4)
if的嵌套:条件里面再分条件
形式:
if()
if() 语句
else 语句
else
if() 语句
else 语句

	else的匹配问题:
		else总是与它上面的,最近的,同级未匹配的if语句配对

switch语句(开关语句)

形式:
	switch(表达式)
	{
		case 常量表达式1:
			语句1
		case 常量表达式2:
			语句2
			break;
		...
		case 常量表达式n
			语句n
		default:
			语句m			
	}

(1)"表达式"的值必须为整数值(整型,字符型,枚举),即可以列举的值
(2)“常量表达式"的值必须为整数值,且每个case后面的常量表达式的值必须不相同。
(3)当表达式的值与某一个case后面的常量表达式的值相同时,就会打开"开关”,执行冒号后面的语句,直到有break语句就提前跳出switch,否则就轮到下一个case,且不会比较常量表达式的值,就直接执行冒号后面的语句(因为开关已经打开了)。
当所有的语句都执行完了,就结束了switch。
(4)break;提前跳出switch语句,常用的用法是每个case语句后都有一个break语句。
(5)当所有case后面的常量表达式都不匹配时,就会执行default后面的语句,且打开"开关

循环结构

goto语句

无条件跳转语句
goto 语句标号;
“语句标号”:再处于严重,把一个名字(C语言标识符)与某一行的地址相关联。
格式:在一行的开始处定义一个名字,然后再加一个冒号
look:
n++;
goto loop;
作用:
(1)与if语句向上跳转构成循环结构
(2)向后跳转可以跳出多重循环体等语句
注意:goto本身没有任何问题,但是goto语句是无条件跳转语句,应用太灵活,会使程序的可读性变差,应限制使用。

while语句

while(表达式)
	语句
	  
当表达式的值为真(非0),就执行语句,否则跳过循环体,
当进入循环体执行完语句后,再重复判断表达式的值...

"表达式":任意合法的表达式都可以
"语句":单语句,只有一个分号
	  复合语句,{}
	  
编程建议:不管while后面有没有语句,请先写一个{},再到{}里面填内容

do while语句

do
	语句
while(表达式);
先无条件执行一次循环体语句,然后再判断表达式的值,如果为真(非0),就继续执行
循环体语句...直到表达式的值为0,那么循环就不做了。
编程建议:不管do后面有没有语句,请先写一个{},再到{}里面填内容

for语句

for(表达式1;表达式2;表达式3)
语句
★执行过程:首次进入for循环体先执行一次表达式1,再判断表达式2的值,若值为真(非0),
执行语句,最后执行表达式3,一个循环结束。第二次循环起就不再执行表达式1,直接判断
表达式2的值,若为真就继续循环,若为假就跳出for循环。

  for循环体完全等效于如下的while循环体
  表达式1;
  while(表达式2)
  {
	语句
	表达式3;
  }
  唯一的区别是for循环体中的表达式123都可以为空,表达式2为空表示判断条件永远为真,
  而while的表达式2不能为空。
★while循环与for循环的区别:一般来说,while更注重循环条件,for更注重循环次数。
  当然它们是可以相互替换的。
for循环的嵌套:
  int i,j,a=0;
  for(i=0;i<5;i++)//外循环
  {
  	for(j=0;j<4;j++)//内循环
  	{
  		printf("%d %d %d\n",i,j,a++);
  	}
  }

break和continue语句

break语句:
(1)用于switch语句中,用于提前跳出switch语句
(2)用于循环体中(while/do~while/for),用于跳出它所属的循环
continue语句:
continue语句只能用于循环体中(while/do~while/for),用于提前结束本次循环,进入下次循环。

函数:function,功能模块

C语言中,函数是完成某个特定功能的指令序列的封装
函数可以实现代码复用,以及模块化设计
结构化程序设计主张把一个大任务,分成多个小任务的函数来完成。

“函数就是实现某个功能的指令序列”
static、 auto、 extern、 register、 return

函数设计

需求分析: 你得要知道完成什么事情
然后完成这个事情需要什么资源。
具体的功能实现–>算法
反馈一个结果

函数的实现(定义)

返回类型 函数名(输入参数列表)
{
语句
}

“返回类型”:函数返回值(return语句后面那个表达式的类型),一般是"单值"类型,
即基本类型或指针类型,函数也可以没有返回值,即void。如果不指定返回类型,默认为int型。
“函数名”:C语言标识符。把该名字与此函数相关联起来。
“输入参数列表”:功能模块的输入。格式如下:
参数类型1 参数名1,参数类型2 参数名2,…

int sum(int a, int b)
{
	int c = a + b;
	return c;
}

函数的调用

主调函数:调用其它函数的函数。
被调函数:别被人调用的函数
“实际参数”:在函数调用过程中,主调函数传递给被调函数的输入参数值,
我们称为实际参数。
“形式参数”:被调函数在定义时的参数。
函数的调用过程:
(1)把实参的值赋值给相应的形参。
(2)运行函数主体中的语句,直到遇到return语句返回或者执行完所有语句返回。
(3)函数调用返回后,该函数表达式的值为return后面表达式的值(也可以没有返回值)。

数据传递:
主–>被调函数: 参数
被–>主调函数: 返回值
主<->被调函数: 全局变量

Linux虚拟存储系统

一个系统中的进程是与其他进程共享CPU和主存资源的,为了更加有效地管理内存并且少出错,现代系统提供了一种对主存的抽象概念,叫虚拟内存(VM)。
进程是操作系统对一个正在运行的程序的一种抽象表示。
在这里插入图片描述
Linux为每个进程维护一个单独的虚拟地址空间。
请添加图片描述
1G = 1024MB = 10241024KB = 102410241024B = 1024102410248bit
虚拟地址空间的大小由系统的位数决定,若系统为32bit,则虚拟空间的大小为0x0~0XFFFFFFFFByte = 3G。虚拟机中的物理内存表示同一时间能够访问的最大空间。

虚拟地址空间最上面的区域保存操作系统中的代码和数据。地址空间的最底部区域是禁止访问区域(32bit系统00x08048000;64bit系统00x00400000)。上图地址从下往上增大。

程序代码和数据:在进程运行时就被规定了大小。.init(系统初始化代码)、.text(用户代码)和.bss(未初始化的静态数据)、.data(已初始化的静态数据)、.rodata(常量)这几个部分按照可执行目标文件初始化。
堆:在运行时动态地扩展和收缩。

共享库:用于存放标准C库和数学库等这样共享库的代码和数据的区域。
栈:用户栈在程序执行期间可以动态地扩展和收缩。当每次程序调用函数时,栈就会增长;当函数返回时,栈就会收缩。
内核虚拟存储器:操作系统的内核保留区,协调硬件和操作系统软件之间精密复杂的交互。
请添加图片描述
在这里插入图片描述

内存管理和递归函数

内存管理

在这里插入图片描述

(1)auto关键字

auto自动变量:普通局部栈变量,是自动存储,这种对象会自动创建和销毁 ,建议这个变量要放在堆栈上面,调用函数时分配内存,函数结束时释放内存。一般隐藏auto默认为自动存储类别。我们程序都变量大多是自动变量。

局部变量: 也叫自动变量, 它声明在函数开始, 生存于栈, 它的生命随着函数返回而结束。

auto用来修饰变量。

在这里插入图片描述

(2)static关键字

static用来修饰变量、函数。

static变量的使用
在这里插入图片描述
static函数只能在本文件中使用,其他文件无法使用

(3)extern关键字

外部变量声明,是指这是一个已在别的地方定义过的对象,这里只是对变量的一次重复引用,不会产生新的变量。系统的寄存器是有限制的,声明变量时如:register int i.这种存储类型可以用于频繁使用的变量。
extern用来修饰变量、函数。

(4)register关键字

寄存器变量,请求编译器将这个变量保存在CPU的寄存器中,从而加快程序的运行。register用来修饰变量。
第一句话:register是不能取址的。(&)
第二句话:声明变量为register, 编译器并不一定会将它处理为寄存器变量。
第三句话:现在一般的编译器(arm-linux-gcc)都忽略auto和register申明,现在的编译器自己能够区分最好将那些变量放置在寄存器中,那些放置在堆栈中;甚至于将一些变量有时存放在堆栈,有时存放在寄存器中。

(5)const关键字

const 是定义常变量的关键字。
const 定义的是变量,但又相当于常量;说它定义的是常量,但又有变量的属性,所以叫常变量。
①.符号常量和const关键字
const int a = 10;相当于int const a = 10;
用 const 定义的变量的值是不允许改变的,即不允许给它重新赋值,即使是赋相同的值也不可以。所以说它定义的是只读变量。这也就意味着必须在定义的时候就给它赋初值。

1.	#define PI1 3.14
2.	const double PI2 = 3.14;
3.	 
4.	/*两种方式都可以用来定义全局变量。不过,第二种要比
5.	第一种方式要好,使用宏定义的变量,其信息一般以表格
6.	的形式储存在系统中,当我们在调试程序时,就有可能使
7.	得这个宏定义的变量反复出现在符号表中。而const修饰
8.	的变量会一直出现在符号表,使得我们调试方便许多。*/
#include <stdio.h>

#define PI1 3.14
const double PI2 = 3.14;

int main(int argc, char *argv[])
{	
	printf("%.2f, %.2f\n", PI1, PI2);
	
	return 0;
}

②.修饰全局变量

#include  <stdio.h>

const char n=8;//相当于char const n=8;   .data    

int main(void)
{
	
	//测试被锁死的n变量对应地址标识的值是否能否被修改
	n = 7;
	/*
		测试结果:
		example.c: In function ‘main’:
		example.c:13:4: error: assignment of read-only variable ‘n’
		  n = 7;
		  
		结论:当全局变量使用const关键字之后,n变量对应地址标识的会被锁死,无法修改。
	*/
	printf("n = %hhd\n", n);
	
}

③.修饰局部变量

#include <stdio.h>

int main(int argc, char *argv[])
{	
	const char n=8;//相当于char const n=8;
	
	//测试被锁死的n变量对应地址标识的值是否能否被修改
	n = 7;
	/*
		测试结果:
		example.c: In function ‘main’:
		example.c:13:4: error: assignment of read-only variable ‘n’
		  n = 7;
		  
		结论:当局部变量使用const关键字之后,n变量对应地址标识的会被锁死,无法修改。
	*/
	printf("n = %hhd\n", n);

	return 0;
}

④.修饰指针

/*用于修饰指针*/
2.	const int *p1 = NULL;   //常量整形指针
3.	int *const p2 = NULL;   //整形常量指针
4.	 
5.	/*常量整形指针,不能通过这个指针修改它所指向的变量,
6.	指针本身是可变的。
7.	整形变量指针,指针不可修改,但是指向的变量可以修改*/
/*
	const char *p1形式可以锁死p1对应的地址标识中的地址标识中的值,无法锁死p1对应的地址标识中的地址标识。
	char *const p2形式能够锁死p2对应的地址标识中的地址标识,不能够锁死p2对应的地址标识中的地址标识中的值。
*/

#include <stdio.h>

#define CONTRL_P1 0  //const char *p1形式学习
#define CONTRL_P2 1  //char *const p2形式学习

int main(int argc, char *argv[])
{	
	char n = 8;//相当于char const n=8;
	char m = 10;
	
	const char *p1 = &n;

	#if CONTRL_P1
		printf("&n--%p;&m---%p;&p1---%p; p1---%p\n", &n, &m, &p1, p1);
	#endif

	#if CONTRL_P1
		p1 = &m;
		/*
			测试结果: const char *p1形式无法锁死p1对应的地址标识中的地址标识
			测试依据:通过p1 = &m;语句执行前后p1对应的地址标识中的地址标识是否发生改变。
		*/
		printf("p1---%p\n", p1);
	#endif	
	
	#if CONTRL_P1
		*p1 = 9;
		/*
			测试结果:const char *p1形式能够锁死p1对应的地址标识中的地址标识中的值。
			若代码中定义了const char *p1形式,还去编写*p1 = 9;语句则会出现如下错误:
			
			example.c: In function ‘main’:
			example.c:18:6: error: assignment of read-only location ‘*p1’
			  *p1 = 9;
				  ^
			测试依据:通过写*p1 = 9;语句在执行x86main文件时是否由出错。
		*/
		printf("p1---%p\n", p1);
	#endif		
	
	
	char *const p2 = &n;

	#if CONTRL_P2
		printf("&n--%p;&m---%p;&p2---%p; p2---%p\n", &n, &m, &p2, p2);
	#endif	
	
	#if CONTRL_P2
		p2 = &m;
		/*
			测试结果:char *const p2 形式能够锁死p2对应的地址标识中的地址标识。
			若代码中定义了char *const p2 形式,还去编写p2 = &m;语句则会出现如下错误:
			example.c: In function ‘main’:
			example.c:55:6: error: assignment of read-only variable ‘p2’
			   p2 = &m;
				  ^
				  
			测试依据:通过p2 = &m;语句在执行x86main文件时是否由出错。
		*/
		printf("p2---%p\n", p2);
	#endif		
	
	#if CONTRL_P2
		*p2 = 8;
		/*
			测试结果:char *const p2 形式不能够锁死p2对应的地址标识中的地址标识中的值。
			
			测试依据:通过写*p1 = 8;语句在执行x86main文件时是否由出错。
		*/
		printf("p2---%p\n", p2);
	#endif		
	

	return 0;
}

递归(n!)

递归函数,是指在定义的时候,直接或间接调用自身的函数。
C语言递归函数设计:
(1)问题模型本身要符合递归模型(递推模型)
(2)问题的解,当递归到一定层次时,答案是显而易见的,且能结束函数。
(3)先明确函数要实现的功能与参数的关系,暂不管功能具体的实现。
(4)呈现第n层与第n-1层的递推关系。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

0泡果奶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值