初识C语言(三)--最终章,万字解析,趣味讲解完C语言的最后知识点

发布于:2022-10-20 ⋅ 阅读:(671) ⋅ 点赞:(0)

前言
👻作者龟龟不断向前
👻简介宁愿做一只不停跑的慢乌龟,也不想当一只三分钟热度的兔子。
👻专栏C++初阶知识点

👻工具分享

  1. 刷题: 牛客网 leetcode
  2. 笔记软件:有道云笔记
  3. 画图软件:Xmind(思维导图) diagrams(流程图)

在这里插入图片描述

如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持博主🙊,如有不足还请指点,博主及时改正

初识C语言(三)

🚀1.函数

​ 如果你是一个懒人,而你的main函数里面有大量的重复操作,比如大量的加法计算。如下

#include<stdio.h>

int main()
{
	int a = 1, b = 2, c = 3, d = 4, e = 5, f = 6;
	int sum1 = a + b;
	int sum2 = c + d;
	int sum3 = e + f;
	printf("sum1 = %d\n", sum1);
	printf("sum2 = %d\n", sum2);
	printf("sum3 = %d\n", sum3);
	return 0;
}

​ 而且这仅仅是一个加法,如果以后是更加复杂的操作,而且要重复的进行很多次,这是懒人所不能接受的的?严重破坏了懒人主义,我马爹也不同意,这个时候我们可以设计一个函数,每次要进行这个操作的时候,调用这个函数帮我们完成即可。

在这里插入图片描述

🍉函数的用法

#include<stdio.h>

int Add(int x,int y)
{
	return (x + y);
}

int main()
{
	int a = 1, b = 2, c = 3, d = 4, e = 5, f = 6;
	int sum1 = Add(a, b);
	int sum2 = Add(c, d);
	int sum3 = Add(e, f);
	printf("sum1 = %d\n", sum1);
	printf("sum2 = %d\n", sum2);
	printf("sum3 = %d\n", sum3);
	return 0;
}

函数的特点就是简化代码,代码复用。 目光要放长远,虽然这个例子我们还没有看到函数的太大作用,但是函数绝对是程序中必不可少的环节。

🍉函数的执行流程

​ VS编译器,我们点击F10(有些同学需要配合Fn键使用)进入调试

在这里插入图片描述

​ 目前程序已经执行到了int sum1 = Add(a,b)这个语句,这个时候sum1的值也没有计算出来,还是随机值。我们点击F11进入函数内部!

在这里插入图片描述

​ 当我们继续点击F11,出了函数,return (x+y)就是将 x + y的值返回,而sum1接受了这个返回值,从而将sum1计算出来了。

在这里插入图片描述


🚀2.数组

顾名思义:一组相同类型元素的集合

问题:要存储1-10的数字,怎么存储?

虽然也可以如下暴力解决

#include<stdio.h>

int main()
{
    int a1 = 1;
    int a2 = 2;
    //…………创建10int类型的变量,放1~10的数字
    return 0;
}

​ 很明显上述的代码显得非常搓,给人的印象很不好,那么数组的概念就可以很好地解决上述问题

🍉数组的创建/数组元素访问

数组的定义/创建:类型 数组名 [元素个数]–int arr[10]这里的元素个数必须是常量,整数

数组的元素访问:数组名 [下标],arr[6]注意下标是从0开始的,例如第7个元素的下标是第6和元素,这里的下标可以是变量,也可以是常量

注意:数组的定义和数组元素的访问都有[],但他们的作用是不同的

#include<stdio.h>

int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	printf("%d\n", arr[6]);//下标访问操作符(常量变量都可以)
	int i = 0;
	while (i < 10)
	{
		printf("%d ", arr[i]);
		++i;//不要忘记调整i哟
	}
	printf("\n");

	return 0;
}

​ 上述数组的初始化可以直接赋值,将一个集合(这个跟我们数学上的集合枚举法写法是已知的)赋值给数组。

在这里插入图片描述

在这里插入图片描述

​ 当然了,上述数组创建的时候元素个数是可以不写的,编译器可以通过初始化时的元素个数,计算出来[]里面的值。


🚀3.操作符

🍉C操作符大全

​ C语言之所以很灵活,一方面就取决于其丰富的操作符!

算术操作符

+ - * / %

移位操作符

>> <<

位操作符

& ^ |

赋值操作符

= += -= *= /= &= ^=  |=    >>=   <<=

单目操作符

!           逻辑反操作
-           负值
+           正值
&           取地址
sizeof      操作数的类型长度(以字节为单位)
~           对一个数的二进制按位取反
--          前置、后置--
++          前置、后置++
*           间接访问操作符(解引用操作符)
(类型)       强制类型转换

关系操作符

>
>=
<
<=
!=   用于测试“不相等”
==   用于测试“相等”

逻辑操作符

&&     逻辑与
||     逻辑或

条件操作符

exp1 ? exp2 : exp3

逗号表达式

exp1, exp2, exp3, …expN

下标引用、函数调用和结构成员

[] () . ->

​ 当然这些操作符也不要求死记硬背,也不要求现在就要都会用,咱们以后的学习中都会学习到,用到的。


🍉常见操作符的用法介绍

🍇/–求商 %–求余数(算术操作符)
#include<stdio.h>

int main()
{
	int m = 17;
	int n = 4;
	int q = m / n;//商
	int r = m%n;//取模--余数
	printf("%d\n", q);
	printf("%d\n", r);
	return 0;
}

扩展

这里给大家扩展一下,%操作符还是很常用的,经常会用到以下问题里面:

  1. 判断一个数是否能被另一个数整除–应用:用逢7,计算闰年
  2. 求两个数的最大公因数
  3. …………
🍇&&–并且 ||–或者(逻辑操作符)

exp1 && exp2–这个表达式中,exp1exp2都为真,整个表达式才为真,否则为假

exp1 && exp2–这个表达式中,exp1exp2只要有一个为真,整个表达式才为真,否则为假

计算机看不懂“真”,“假”,计算机判断条件真假是,0为假,非0为真

而我们传说中的if语句和while语句,if(条件),while(条件),他们他们的条件为真时,{}里面的语句才能被执行

&& 和 ||的理解有很多种

  1. 有些同学理解成botheither
  2. 有些同学理解成并且/都或者
  3. 有些同学理解成电路中的串联并联

大家用适合自己理解的去理解即可

在这里插入图片描述

举例1:

#include<stdio.h>

int main()
{
	int a = 1;
	int b = 2;
	if (a && b)
	{
		printf("你们两都来啦\n");
	}
	else
	{
		printf("你们两怎么没都来");
	}
	return 0;
}
int main()
{
	int a = 0;
	int b = 0;
	if (a || b)
	{
		printf("你们两至少有一个来了\n");
	}
	else
	{
		printf("你们两怎么没有一个人来\n");
	}
	return 0;
}

举例2:



int main()
{
	int age = 0;
	printf("请输入你的年龄\n");
	scanf("%d", &age);
	//if (18 <= age <= 36)//逻辑相差很大
    if (age >= 18 && age <= 36)
	{
		printf("你是青年\n");
	}
	else
	{
		printf("你不是青年\n");
	}
	return 0;
}

这里比较容易错,大家如果想要表示一个18-36的范围,千万不要像数学中一样直接来一手18 <= age <= 36,这是一个错误的逻辑,&&是一个二元操作符,一次只能进行两个操作数的操作,无法达到我们一次到位的效果

正确写法: if (age >= 18 && age <= 36)


🍇条件操作符exp1 ? exp2 : exp3

​ 下面的图片可以大致解释条件表达式的意思

龟龟小故事:小明和小刚同时爱上了小红,小红也不知道怎么选择,于是绝对让小明和小刚做一个竞争,

小明赢了,那么小红就选择小明,小明输了(小刚赢了),小红就选择小刚

在这里插入图片描述

在这里插入图片描述

代码举例:

#include<stdio.h>

int main()
{
	int a = 3;
	int b = 10;
	int c = a > b ? a : b;
	printf("c = %d\n", c);
	return 0;
}

上述代码中,如果 a > b,则c = a,否则 c = b


正经解释:

条件表达式也叫做三目表达式,因为操作数有三个

exp1?exp2:exp3,如果exp1表达式的值为真,那么整个表达式的值就是exp2的值,否则整个表达式的值就是exp3的值

所以我们的求两数的最大值的函数可以稍微改善一下了:

改善前:

int Max(int x, int y)
{
	if (x > y)
	{
		return x;
	}
	else
	{
		return y;
	}
}

改善后:

int Max(int x, int y)
{
	return x > y ? x : y;
}

龟龟小故事的结局与条件表达式无关😅

在这里插入图片描述


🚀4.C关键字大全

​ 我们按照类型,循环,分支对关键字进行一个分类

void

1.与类型有关的关键字

修饰类型 auto static const extern sizeof signed typedef unsigned
内置类型 char double short float int long register
自定义类型 union struct enum

2.与分支有关的关键字

case default else goto if switch

3.与循环有关的关键字

break continue do for while

剩下的关键字还有

void return volatile

​ 这些关键字也是不需要死记硬背,后面我们都会用上


🚀5.scanf的返回值

​ 有些同学可能会有疑惑,scanf–不是scan format不是格式化输入函数吗?怎么还有返回值

🍉成功输入

​ 确实是有的,scanf的返回值为成功输入的元素个数

#include<stdio.h>

int main()
{
	int a = 0;
	int b = 0;
	int ret = scanf("%d %d", &a, &b);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述


🍉输入失败

​ 有成功输入的情况肯定也有输入失败的情况

在这里插入图片描述

​ 上述操作我们只成功输入了一个数据,那么返回的是1

那要是全部输入失败了呢?

在这里插入图片描述

​ 我们发现全部输入失败,scanf会返回-1,而不是0。起始scanf在全部输入失败时返回的时EOF,而EOF被做了一些操作

在这里插入图片描述

我们将鼠标放在EOF上面,发现define EOF -1EOF时#define定义的标识符常量。EOF的值本质就是-1


🍉解决OJ测试题多组测试用例输入

这里给大家推荐OJ刷题网站:牛客网,leetcode

在这里插入图片描述

这里我们看到的是多组输入,单组输入其实是无法解决该问题的

解决方法:

#include<stdio.h>

int main() {
    int num = 0;
    while(scanf("%d",&num) != EOF)//将scanf放在这里
    {
        if(num >= 60)
        {
            printf("Pass\n");
        }
        else
        {
            printf("Fail\n");
        }
    }
    return 0;
}

🚀6.关键字register的用法(如今作用不大)

🍉计算机存储金字塔层次结构

​ 首先我们得介绍寄存器,高速缓存,内存,硬盘的区别

图解计算机存储金字塔层次结构

在这里插入图片描述

​ 图中的L1,L2,L3就是高速缓存

平时我们所说的512GB就是电脑的硬盘,16GB叫做内存(手机上习惯叫运行内存)当然了我们现在还有所谓的网盘,网盘就在硬盘之下了。

一般的我们写代码向内存申请一块空间,然后创建一个变量,这个变量是存在内存中的,如果我们想提高效率,提高访问速度,可以在变量的类型前面,使用**register修饰** ,这样就可以 建议编译器将变量放到寄存器存储,从而提高访问效率吧

#include<stdio.h>

int main()
{
	register int a = 0;
	return 0;
}

register int a = 0;–当然了这只是对编译器的一种建议,现在编译器还是比较聪明的,**会自动将一些下面要用到的变量存在寄存器。**可以理解register现在几乎作用不大。


🚀7.关键字typedef

功能就好像是:

在这里插入图片描述

在这里插入图片描述

就是取了个别名,但是并不是可以给任何东西取别名

typedef 顾名思义是类型定义,这里应该理解为类型重命名

例如我们可以将一些复杂的类型名重命名以下

#include<stdio.h>

typedef unsigned int uint;
int main()
{
	unsigned int a = 0;
	uint b = 0;
	return 0;
}

🚀8.关键字static

🍉static修饰的局部变量

题目测试:

#include<stdio.h>

void test()
{
	int a = 0;
	++a;
	printf("%d ", a);
}

int main()
{
	int i = 0;
	while (i < 10)
	{
		test();
		++i;
	}

	i = 0;
	while (i < 10)
	{
		test();
		++i;
	}
	printf("\n");
	return 0;
}

​ 同学们可以思考一下上述代码的输出结果是什么?

答案是是个1,你答对了吗?

1 1 1 1 1 1 1 1 1 1

原因解释:

​ 因为我们讲过,局部变量的作用域是局部变量所在的局部范围–也叫代码块,当出了这个代码块,变量就会销毁,所以我们每次进入test函数,a都是重新创建的a,然后从0到1。

​ 如果我们想要达到输出1~10的效果,就要让a的生命周期延长,使其出了代码块也不销毁,static就起作用了。

#include<stdio.h>

void test()
{
	static int a = 0;
	++a;
	printf("%d ", a);
}

int main()
{
	int i = 0;
	while (i < 10)
	{
		test();
		++i;
	}

	i = 0;
	while (i < 10)
	{
		test();
		++i;
	}
	printf("\n");
	return 0;
}

​ 这里还是稍微解释一下static修饰的局部变量生命周期延长了。

🍉内存分区

在这里插入图片描述

int a = 0;这个局部变量a是放在栈里面的,出了代码块a就会销毁,而static int a = 0这个a是放在静态区的,静态区的生命周期是从变量创建开始到程序结束,不归栈管,出了代码块a也没有销毁


🍉static修饰的全局变量

​ 之前我们讲过全局变量的作用域是整个工程,具有外部链接属性,外部源文件也是可以使用当前源文件的全局变量的

在这里插入图片描述

在这里插入图片描述

​ 如果我们将上述的global使用static修饰,那么global的外部连接属性也就消失了,也相当于作用域被缩小了

static int global = 100;

在这里插入图片描述


🍉static修饰函数

​ 我们知道函数是具有外部链接属性的,而经过static修饰的函数,也会达到跟static修饰全局变量一样的效果,外部连接属性消失

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


🍉static的作用总结

1.static修饰局部变量,使得变量的内存存储方式改变了,从栈区存到了静态区,生命周期被延长,出了局部范围也不销毁

2.static修饰全局变量,使得变量的作用域缩小了,失去了外部链接数序,只能在本源文件中使用该变量

3.static修饰函数,使得变量的作用域缩小了,失去了外部链接数序,只能在本源文件中使用该函数

​ 全局变量和函数的下场就像,两个活泼的孩子,最近来疫情了,static妈妈管住他们,不让他们再往外面跑。居家隔离

在这里插入图片描述


🚀9.#define定义标识符和宏

🍉定义标识符

#define M 100
#include<stdio.h>

int main()
{
	int a = M;
	int arr[M];
	printf("%d\n", a);
	printf("%d\n", M);
	return 0;
}

​ 这个很简单咱们在讲常量的时候已经讲过了。


🍉定义宏

宏的用法跟函数的用法差不多,就是定义的时候有些许差别,咱们点到位置

#define ADD(x,y) (x+y)
#define MAX(x,y) (x>y?x:y)
#include<stdio.h>

int main()
{
	printf("%d\n", ADD(3, 5)*8);
	printf("%d\n", MAX(10, 20)*8);
	return 0;
}

🚀10.C指针

​ 想要清楚指针,我们进一步的了解内存

🍉内存

内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。 所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。 为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。

例如我们拿32位机器举例,x86–32位机器,拥有32根地址线,这些地址线一共有两种状态,通路与断路,1/0,根据数学知识

一共就存在00000000 00000000 00000000 00000000 ~ 11111111 11111111 11111111 11111111共2^32中状态

就有2^32次方个内存编号,由于二进制的繁琐型,我们通常使用十六进制来表示地址

在这里插入图片描述

思考:32位机器下的内存大小是多少?

答案是4GB,清楚内存单位的换算即可

​ 我们也可以使用敲代码,讲变量的地址打印在屏幕上:

在这里插入图片描述


​ 而且我们可以调开vs的内存窗口看看效果

在这里插入图片描述

有些同学可能会疑惑,为什么两次运行程序,a的地址会不一样呢?

同学们思考一下,你去一家酒店开房,每次去,老板给你开的房都是同一间吗?不太可能对吧因为其他房间可能被占用着

同样的,每次申请空间也并不是一直给你的是那块内存,可能那块内存其他程序正在使用着


🍉指针变量

​ 那我们使用能使用变量将地址存储起来呢?答案是可以的,但是有两个问题:指针变量是什么类型?指针变量的大小是什么?

🍇指针变量的类型
#include<stdio.h>

int main()
{
    int a = 10;
    int* p = &a;
    return 0;
}

​ 如果指针变量存储的是一个int类型的地址,那么指针变量的类型就是int*,同理我们可以推出如何存储double,char类型的数据

#include<stdio.h>

int main()
{
    int a = 1;
    double b = 2.2;
    char c = 'w';
    
    int* pa = &a;
    double* pb = &b;
    char* pc = &c;
    return 0;
}

🍇指针变量的大小

​ 是否还记得sizeof操作符

int main()
{
	int a = 1;
	double b = 2.2;
	char c = 'w';

	int* pa = &a;
	double* pb = &b;
	char* pc = &c;

	printf("%u\n", sizeof(pa));
	printf("%u\n", sizeof(pb));
	printf("%u\n", sizeof(pc));
	printf("%u\n", sizeof(int*));
	printf("%u\n", sizeof(double*));
	printf("%u\n", sizeof(char*));

	return 0;
}

答案都是:4(32位机器下)

%u – 打印32位机器下的无符号整形

%zd–打印64位机器下的无符号整形

这跟指针变量里面放的什么有关系,指针变量里面存放的是地址,地址是内存编号

32位机器下是32个bit的0/1数字,32位机器下是64个bit的0/1数字,根据 1byte = 8bit

所以32位机器下指针大小是4byte,所以64位机器下指针大小是8byte


🍇指针的使用

同学们想一想,如果你知道了你朋友家的地址,你把这个地址记在你的小本本上,那么你是不是可以就可以通过这个小本本上面的地址找到你朋友家。如果朋友家没锁,是不是还可以干点其他事儿,当然这是现实生活中不允许的对吧

同样的,如果我得到了a的地址,那么我是否可以通过这个地址找到a,对a进行一些操作呢,答案是可以的

#include<stdio.h>

int main()
{
	int a = 1;
	double b = 2.2;
	char c = 'w';

	printf("a = %d\n", a);
	printf("b = %.2f\n", b);
	printf("c = %c\n", c);

	int* pa = &a;
	double* pb = &b;
	char* pc = &c;
	*pa = 8;
	*pb = 4.4;
	*pc = 'z';
	printf("a = %d\n", a);
	printf("b = %.2f\n", b);
	printf("c = %c\n", c);
	return 0;
}

在这里插入图片描述


🚀11.结构体struct

前面我们介绍数据类型的时候提到了内置类型–C语言提高给我们可以直接使用的类型

例如int,double,char,生活中有一些数据我们使用内置类型无法定义,例如学生,汽车等

那么我们就要自己设计类型–自定义类型啦,开造

ps:我们可以自定义类型,但是不可以自己造一个关键字的哈

使用关键字struct

struct Stu
{
    char name[20];//名字
    int age;      //年龄
    char sex[5];  //性别
    char id[15]//学号
};

如果我们自己定义的类型:

#include<stdio.h>

struct Stu
{
    //成员变量
    char name[20];//名字
    int age;      //年龄
    char sex[5];  //性别
    char id[15]//学号
};

int main()
{
    //打印结构体信息
    struct Stu s = {"张三"20"男""20180101"};
    //.为结构成员访问操作符
    printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id);
    //->操作符
    struct Stu *ps = &s;
    printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps-
    >id);
    
    return 0;
}

struct Stu这个整体是一个类型名

在结构体变量右面加.可以访问其成员变量

在结构体指针变量(存放结构体变量的地址)右面加->可以访问其成员变量

点赞

本文含有隐藏内容,请 开通VIP 后查看

网站公告


今日签到

点亮在社区的每一天
去签到