C语言学习基础(day06)(达内)

day06 while 循环和 do 循环 、 缓冲区 、 一维数

每日英语:

index:索引号,偏移量

array:数组

size:大小

回顾:

1. 数据类型转换

隐式转换:小转大,有转无,整转浮

强制转换:目标数据类型变量 = (目标数据类型)源类型变量

​ 推荐使用:提高代码的可读性

2. C语言的三大结构

顺序结构,分支结构,循环结构

2.1 顺序结构

​ 从上到下执行

2.2 分支结构

​ 功能:多选一

​ 两类:条件分支和开关分支

​ 条件分支:

1. if
2. if...else
3. if  else if ... else if
4. if ... else if ... else if ... else

​ 开关分支:

switch...case

2.3 循环结构

​ 功能:让一组数据重复多次执行

​ 三类:for循环,while循环,do…while循环

​ for 循环:

for(1; 2; 3) {
	4;
}

​ 七种形式

作业:求1+…+100的和 打印10x20的*方阵
/*案例演示*/

#include <stdio.h>

int main(void) {
	//1+2+...+100
	int i;
	int sum = 0;
	for(i = 1; i <= 100; i++) {
	sum += i;
}
	printf("sum = %d\n", sum);
		
	// 20*10方阵
	for(int m = 0; m < 10; m++){
		for(int n = 0; n < 20; n++) {
			printf("*");
	}
		printf("\n");
}	



	return 0;
}

2.4 循环结构之while循环

2.4.1 语法格式
while(循环控制表达式) {
	循环语句;
}

执行流程:

第一步骤:首先执行循环控制表达式

​ 如果它的执行结果为真,那么就执行循环语句

​ 如果它的执行结果为假,while循环直接结束

第二步骤:如果循环语句执行完毕,再接着执行第一步骤

例如:

int i = 0;
while(i < 5) {
	printf("i = %d\n", i);
	i++;
}

while死循环:

while(1) {
	printf("hello world");
}
2.4.2 注意

如果while循环语句只有一条,{}可以不用加(由衷建议最好加上去)

参考代码:while.c
/*while演示*/

#include <stdio.h>
int main(void) {
	int i = 0;
	while(i < 5) {
	printf("i = %d\n", i);
	i++;
	}

/*
// while循环实现1+2+...+100
	int j = 1;
	int sum = 0;
	while(j < 101) {
	sum += j;
	j ++;
}
	printf("sum = %d\n", sum);
*/

	printf("\n");
	
	i = 0;
	while(++i < 5) { // 先计算i++,后赋值
	printf("i = %d\n", i);
}
	
	printf("\n");

	i = 0;	
	while(i++ < 5) { // 先看i=0,表达式为真,后计算i+1=0+1=1
	printf("i = %d\n", i);
}


	return 0;	
}

结果:

i = 0
i = 1
i = 2
i = 3
i = 4

i = 1
i = 2
i = 3
i = 4

i = 1
i = 2
i = 3
i = 4
i = 5

2.5 循环结构之do-while循环

2.5.1 语法格式
do {
	循环语句;
}while(循环控制表达式); //切记此分号不可省略

执行流程:

第一步骤:先执行循环语句; //第一次没有做判断直接运行

第二步骤:然后执行循环控制表达式

​ 如果为真,继续执行循环语句,后续依次重复,知道循环控制表达式为假,循环立即结束

**切记:**do…while 循环语句至少执行一次!

例如:

do {
	printf("hello,world\n");
}while(0);
结果:打印一次hello world

例如:

int i = 0;
do {
	printf("i = %d\n", i);
	i++;
}while(i < 5);
参考代码:dowhile.c
/*do...while演示*/

#include <stdio.h>

int main(void) {
	do {
	printf("hello,world\n");
}while(0);	

	int i = 0;
	do {
	printf("i = %d\n",i);
	//i++;
}while(++i < 5);

	int m = 0;
	int sum = 0;
	do {
	sum += m;
	m++;
}while(m < 101);
printf("sum = %d\n", sum);

	return 0;
}

结果:
hello,world
i = 0
i = 1
i = 2
i = 3
i = 4
sum = 5050

3. goto语句

3.1 作用

可以让cpu跳转到任何一条语句去执行

应用:linux内核操作系统代码大量使用goto语句

3.2 语法:

goto 标签名;	//就是要跳转运行的地方
				标签命名符合标识符的命名规则

3.3 两种使用形式:

3.3.1 形式1:
标签吗:
	语句
...
goto 标签名;

例如:

label:
	printf("1.\n");
goto label;
printf("2.\n");

**执行流程:**先打印1,然后执行goto label,跳转到label标签继续执行,继续打印1,然后执行goto

​ 继续执行goto label,最后形成死循环

3.3.2 形式2:
goto 标签名;
...
标签名:
	语句;

例如:

goto label;
printf("2.\n");
label:
	printf("1.\n");	// 直接跳转到label标签,打印1
参考代码:goto.c
/*goto语句演示*/

#include <stdio.h>

int main(void) {

	// 形式1标签在goto语句前面
	int a = 1;
label:
	printf("1.\n");
	if(1==a) {
		a = 0; // 目的是退出goto形成的死循环
		goto label;
}
	printf("2.\n");

	//形式2:标签在goto语句的后面
	goto label2;
	printf("3.\n");
label2:
	printf("4.\n");



	// 利用goto实现1+2+...+100
	int n = 1;
	int sum = 0;
next:
	if(n <= 100) {
		sum += n;
		n++;
        goto next;
	}
	printf("sum = %d\n", sum);

	
	return 0;
}


结果:
1.
1.
2.
4.
sum = 5050

3.4 linux内核源码中goto语句经典使用模板

需求:运行一个程序,程序要分配三块内存,只要有一块内存分配失败,程序立刻结束,并且把之前分配成功的内存释放桂怀 给操作系统

参考代码:goto2.c
/*goto经典模板演示*/
#include <stdio.h>

int main(void) {
	int ret = 0; // 0:表示分配内存成功,1:表示分配失败
	
	printf("分配第一块内存\n");

	if(1==ret) {
		goto free1;
}

	printf("分配第二块内存\n");
	ret = 1;   // 模拟第二块内存分配失败
	if(1==ret) {
		goto free2;
}

	printf("分配第三块内存\n");
       //ret = 1; 模拟第三块内存分配失败
	if(1==ret) {
		goto free3;
}
	printf("三块内存分配成功\n");
	return 0;
	
free3:
	printf("释放第二块内存\n");
free2:
	printf("释放第一块内存\n");
free1:
	return ret;	// 给操作系统返回错误

}c

4. 空语句

4.1 语法:

仅仅包含一个;

例如:

;	//空语句,起到一个延时的作用,因为Cpu执行空语句,也需要消耗时间

4.2 应用场景

用于实现一个空循环

例如:

for(;;); // 空死循环,让CPU跑到这里别再往下运行了,并且CPU在这里玩命的空转

int i = 100000;
for(; i >= 0; i--);  // 空循环100000次,每次CPU会消耗时间,此代码起到延时作用
						也就是让CPU在这里稍微等一等,这个延时时间不精确
						如果要的到一个精确的延时时间,后续课程会讲

4.3 不要做一下可悲的事情

int i;
for(i = 0; i < 5; i++); {  // 不小心加了一个分号,结果是有效循环变空循环
	printf("i = %d\n", i)
}
int i = 0;
while(i < 2); {    //有效循环变空循环
	printf("i = %d\n", i);
}

切记:在while / for 循环中的圆括号后面误写了;就会意外的将有效循环变空循环

参考代码:empty.c
/*空语句演示*/

#include <stdio.h>
int main(void) {

/*
	// 空死循环	
	printf("1\n");
	for(;;);  //让CPU在这里玩命空转,再也不向下运行
	printf("2\n");
*/


// 有效空循环,起到延时作用
	int i = 900000000;
	printf("1\n");
	for(; i >= 0; i--);
	printf("2\n");

	//注意:正常代码
	printf("3\n");
	unsigned long a = 900000000;
	for(; a > 0; a--);
	printf("4\n");


	//注意:死循环代码
	printf("3\n");
	unsigned long a = 900000000;
	for(; a >= 0; a--); // 无符号数减到0再减1变负值,又回滚到0了,而判断是基于:m>=0,永远成立
	printf("4\n");
	return 0;
}


	// 可悲的事情  for格式的
	int c = 0;
	for(; c < 5; c++);   // 有效的循环变为了空循环
		printf("c = %d\n", c);  //c = 5
	
	//while格式的
	int d = 0;
	while(d < 5); {
		printf("d = %d\n", d);  // 不打印
}

第七课:数组

1. 明确

计算机程序最终都是玩内存,万内存前提是先分配内存,目前掌握的分配内存的方式就一种:定义变量

例如:

int a;
int b;
int c;
....
int zzzzz..;
如果要大量数据类型一致的变量,按照以上编码实现,代码极其冗余和繁琐
问:能够有一种方法来解决以上问题呢?既可以分配大量内存也可以保证数据类型一致,关键还可以让代码变得简单呢?
答:有,这就是C语言第二种分配内存方法:数组

2. 数组特点

  1. 也是一种分配内存你的方法
  2. 并且分配的内存数据类型是一致的,
  3. 可以大量分配内存
  4. 并且分配的内存是连续的

3.定义数组(数组分配内存)的语法格式

元素数据类型 数组名[数组长度(又称数组元素个数)] = {初始化列表,如果有多个,用逗号分开};

例如:

int a[5] = {1, 2, 3, 4, 5};
语义:连续分配5个元素的内存空间,每个元素的数据类型都是int类型,每个元素占4字节内存空间
	 所以最终连续分配了20个字节内存空间,并且每个元素的值,也就是内存存储的值,分别是1, 2, 3, 4, 5
	 类似:int a = 1; int b = 2; int c = 3; int d = 4;
此时此刻脑子立马浮现一个内存分分布示意图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IZRO6ljG-1619017973440)(E:\C语言\c-study-notes\tarena\img\数组.png)]

4. 玩数组的注意事项

  1. 数组分配内存是连续的

  2. 数组下标就是数组中元素的编号(专业术语叫索引号,index),下标从0开始

    例如:

    int a[5] = {1, 2, 3, 4, 5};
    
    元素数值下标叫法
    10第0个元素
    21第1个元素
    32第2个元素
  3. 数组长度又称数组元素个数,而不是整个数组占用内存的大小

    例如:

    int a[5];	// 数组长度 = 数组元素个数 = 5,而数组分配内存大小是20个字节
    
  4. 数组名就是整个数组分配的内存的首地址,它等于数组第0个元素的首地址

  5. 数组中元素的访问是通过运算符"[]"结合下标来进行访问的

    格式:数组元素值 = 数组名[数组元素对应的下标];

    例如: int a[5] = {1, 2, 3, 4, 5}

    第0个元素值 = 1 = a[0];

    第1个元素值 = 2 = a[1];

    第2个元素值 = 3 = a[2];

    **结论:**以第三个元素为例:得到一下规律:

    打印第三个元素值: printf("%d\n", a[3]);

    修改第3个元素的值为250(修改对应的内存值):a[3] = 250; // 结果是a[3]由原来的4变成250

    获取第三个元素值对应的内存首地址:printf("%p\n", &a[3]);

    通过第三个元素的首地址来获取对应的数值: printf("%d\n", ***&a[3]) // ***&a[3]最终等价于a[3]

  6. 切记切记:千万注意数组的越界访问(笔试题必考)

    例如:

    int a[5] = {1, 2, 3, 4, 5}   // 目前操作系统给你连续分配嘞20个字节内存
    printf("%d\n", a[4]);    // 打印第四个元素值,合法
    printf("%d\n", a[5]);	// 打印不存在的第五个数组,也就是查看访问第5个元素的内存
    						// 显然就是内存越界访问,但是获取的值乱七八糟的
    a[5] = 250;		// 越界访问,关键还对无权访问的内存进行非法修改,操作系统只能干丢你的程序
    					也就是程序立马崩溃
    

参考代码:array.c

/*数组演示*/
#include <stdio.h>

int main(void) {
	int a[6] = {1, 2, 3, 4, 5, 6};
	
	// 正向打印每个元素值
	for(int i = 0; i < 5; i++) {
	printf("a[%d] = %d\n", i, a[i]);
}

	// 将每个元素扩大10倍
	for(int i = 0; i < 5; i++) {
		a[i] *= 10;
}
	// 逆向打印数组的值
	for(int i = 4; i >= 0; i--) {
		printf("a[%d] = %d\n", i, a[i]);
}

	// 求内存地址
	for(int i = 0; i < 5; i++) {
		printf("a[%d]的内存地址是%p\n", i, &a[i]);
}
	// 通过首地址获取对应的值
	for(int i = 0; i < 5; i++) {
		printf("%d\n", *&a[i]);
}
	// 通过数组元素首地址修改其值
	*&a[3] *= 10;
	printf("第三个元素值是%d\n", *&a[3]);
	
	
	*&a[1] *= 10;
	printf("第二个元素值是%d\n", *&a[1]);

	*&a[5] /= 2;
	printf("第五个元素值的%d\n", *&a[5]);

	for(int i = 0; i < 6; i++) {
	printf("a[%d] = %d\n", i, a[i]);
}
	//越界访问
	printf("a[6] = %d\n", a[6]); // 越界访问获取一个乱七八糟的值
	a[6] = 250;  // 越界访问,修改无权访问的内存,程序直接崩溃


	return 0;
}

结果:

a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
a[4] = 50
a[3] = 40
a[2] = 30
a[1] = 20
a[0] = 10
a[0]的内存地址是0xbf96b2a4
a[1]的内存地址是0xbf96b2a8
a[2]的内存地址是0xbf96b2ac
a[3]的内存地址是0xbf96b2b0
a[4]的内存地址是0xbf96b2b4
10
20
30
40
50
第三个元素值是400
第二个元素值是200
第五个元素值的3
a[0] = 10
a[1] = 200
a[2] = 30
a[3] = 400
a[4] = 50
a[5] = 3
a[6] = -319214336
*** stack smashing detected ***: ./array terminated
已放弃 (核心已转储)

5. 数组定义的形式:

形式1:

int a[5] = {1, 2, 3, 4, 5};

形式2:

int a[5] = {1, 2, 3};  // a[0]=1,a[1]=2,a[2]=3,其余元素都是0,即a[3]=a[4]=0

形式3:

int a[5] = {0}; // 全0

形式4:

int a[5] = {};   //全是0

形式5

int a[5];	// 只定义不初始化,结果每个元素值都是乱七八糟的随机数,很危险

形式6:

int a[] = {1, 2, 3, 4, 5};	//gcc编译器自动计算出元素个数位5

形式7:

int a[5] = {1, 2, 3, 4, 5, 6, 7, 8};  // gcc直接将无效的元素忽略,6,7,8不要

错误形式:

int a[];   // gcc迷茫了,立马报错

参考代码:array1.c

/*定义数字形式演示*/

#include <stdio.h>
int main(void) {
	
	//形式1:只定义不初始化
	int a[5];
	for(int i = 0; i <5; i++) {
	printf("a[%d] = %d\n", i, a[i]);
}
	printf("我是傻逼\n");

	//形式2
	int b[5] = {1, 2, 3};
	for(int i = 0; i <5; i++) {
	printf("b[%d] = %d\n", i, b[i]);
}

	printf("我是傻逼\n");

	//形式3
	int c[5] = {0};
	for(int i = 0; i <5; i++) {
	printf("c[%d] = %d\n", i, c[i]);
}
	
	printf("我是傻逼\n");

	
	// 形式4
	int d[5] = {};
	for(int i = 0; i <5; i++) {
	printf("d[%d] = %d\n", i, d[i]);
}
	printf("我是傻逼");
	
	int e[] = {1, 2, 3, 4, 5};
	for(int i = 0; i <5; i++) {
	printf("e[%d] = %d\n", i, e[i]);
}

	printf("我是傻逼");
	
	int f[5] = {1, 2, 3, 4, 5, 6, 7, 8};
	for(int i = 0; i <5; i++) {  // 如果写成i<8,就成越界访问了
	printf("f[%d] = %d\n", i, f[i]);
}

	printf("我是傻逼");
	int g[];
	for(int i = 0; i < 5; i++) {
	printf("g[%d] = %d\n", i, g[i]);   //报错error: array size missing in ‘g’
}

	return 0;
}

结果:

a[0] = 0
a[1] = -1216923024
a[2] = -1216812024
a[3] = -1216815872
a[4] = -1079563492
我是傻逼
b[0] = 1
b[1] = 2
b[2] = 3
b[3] = 0
b[4] = 0
我是傻逼
c[0] = 0
c[1] = 0
c[2] = 0
c[3] = 0
c[4] = 0
我是傻逼
d[0] = 0
d[1] = 0
d[2] = 0
d[3] = 0
d[4] = 0
我是傻逼e[0] = 1
e[1] = 2
e[2] = 3
e[3] = 4
e[4] = 5
我是傻逼f[0] = 1
f[1] = 2
f[2] = 3
f[3] = 4
f[4] = 5
我是傻逼

6. 切记求数组元素个数的公式

元素个数 = sizeof(数组名) / sizeof(数组第0个元素)

例如:

int a[5] = {1, 2, 3, 4, 5};得到一下公式:
数组首地址 = 数组名a = 数组第0个元素首地址=&a[0]
整个数组占用的内存大小 = sizeof(a)=20个字节
第0个元素占用的内存大小=sizeof(a[0]) = 4字节
元素个数=szieof(a) / sizeof(a[0])=20/4=5个元素
如果两个元素地址相减得到的是两个元素之间相差的元素个数,而实际的地址相差元素个数*4
例如:
	&a[4] - &a[1] = 3(第4个元素和第1个元素之间相差3个元素,实际地址相差12个字节)

参考代码:array2.c

/*数组公式演示*/

#include <stdio.h>

int main(void) {
	int a[5] = {1, 2, 3, 4, 5};
	
	printf("数组a的地址是%p\n", a);
	
	int i;
	for(i = 0; i <5; i++) {
	printf("第%d个元素的地址是%p\n", i, &a[i]);
}
	printf("数组占用的内存大小是%d\n", sizeof(a));
	
	printf("第4个元素地址减去第1个元素地址=&a[4] - &a[1] = %d\n", &a[4] - &a[1]);

	printf("数组第0个元素占用的内存大小是%d\n", sizeof(a[0]));

	printf("数组元素的个数%d\n", sizeof(a) / sizeof(a[0]));


	return 0;
}

结果:

数组a的地址是0xbf7ff898
第0个元素的地址是0xbf7ff898
第1个元素的地址是0xbf7ff89c
第2个元素的地址是0xbf7ff8a0
第3个元素的地址是0xbf7ff8a4
第4个元素的地址是0xbf7ff8a8
数组占用的内存大小是20
第4个元素地址减去第1个元素地址=&a[4] - &a[1] = 3
数组第0个元素占用的内存大小是4
数组元素的个数5

7. 人生认识的第二个标准库函数:scanf

函数功能:从键盘上捕获用户输入的数字,然后将这些数值保存到变量中

使用语法:scanf(“占位符”, 变量的地址);

例如:

int a = 0;
scanf("%d", &a);   //只要程序运行到这里,此时程序停止不前,等待用户从键盘上输入数值
					//一旦输入完毕按回车键,此函数执行完毕,并且将输入的数值保存到变量a中,此时程序继续向下执行
int a, b, c;
scanf("%d%d%d", &a, &b, &c);   //此时用户从键盘输入的方式是:100 空格 200 空格 300 空格  最后回车
							// 结果是: a = 100, b = 200, c = 300
							// 如果要输入多个数值,中间用空隔分开

参考代码:scanf.c

/*scanf函数演示*/
#include <stdio.h>

int main(void) {
	int a = 0;
	printf("请输入一个数字:");
	scanf("%d", &a); // 从键盘捕获输入的数字保存到变量a中
	printf("a = %d\n", a);


	int b ,c, d;
	printf("请输入三个数字(中间用空格分开):");
	scanf("%d %d %d", &b, &c, &d);
	printf("b = %d, c = %d, d = %d\n", b, c, d);
	return 0;
}

8. 变长数组(了解)

8.1 定义

数组元素个数不定

例如:

int a[5];   //定长数组,长度固定

int len;
scanf("%d", &len);
int a(len);   // 变长数组
参考代码:array3.c
/*变长数字演示*/
#include <stdio.h>

int main(void) {
	int len;
	
	printf("请输入数组长度:");
	scanf("%d", &len);

	int a[len];   //定义边长数组

	// 给元素赋值
	for(int i = 0; i < len; i++) {
		a[i] = i + 1;
}

	//打印元素值
	for(int i = 0; i <len; i++) {
		printf("a[%d] = %d\n", i, a[i]);
}

	return 0;
}

9. 多维数组

重点研究到二维数组就可以了,之前的数组又称为一维数组

9.1 定义:

二维数组中每个元素是一个一维数组,也就是二维数组由一维数字组成

二维数组本质还是一维数字,只是形式上将一维数字再次进了分组而已

通过分组得到了所谓的二维数组

在这里插入图片描述

9.2 定义二维数组语法格式

也就是用二维数组分配内存

元素数据类型 二维数组名[二维数组长度] [一维数组长度] = {初始化列表};

例如:5是二维数组的个数5个,3是每个二维数组里面一维数组的个数是3个

int a[5][3] = {{1,2,3},{4,5,6},{7,8,9},{10,11,12},{13,14,15}};

语义:

  1. a代表一个二维数组,包含5个元素,每个元素又是一个一维数组,而每个一维数字又包含3个数字,每个数字的数据类型是int

    结论:二维数组名a就是二维数组的首地址

  2. a[0]代表二维数组a的第0个元素,也就是a[0]就是一个一维数组

    并且它是二维数组a中第0个元素的首地址

    也就是a = a[0]

    a[1]就是二维数组中第1个元素的首地址

    a[2]就是二维数组中第2个元素的首地址

  3. a[0] [0]代表二维数组中第0个元素(一维数组)的第0个元素值

    结论:a[i] [j]代表二位数中第i个一维数组元素的第j个元素值

在这里插入图片描述
关注公众号,获取更多精彩内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值