₀前言
🍎说明⇢关键字实际上就是编译器预先定义了一定的意义(物理意义)的字符串。
⒈数据类型关键字⑿个
char:声明字符型变量或函数 short:声明短整形变量或函数 int:声明整形变量或函数 long:声明长整形变量或函数 signed:声明有符号类型变量或函数 unsigned:声明无符号类型变量或函数 float:声明浮点型变量或函数 double:声明双精度变量或函数 struct:声明结构体变量或函数 union:声明共用体(联合)数据类型 enum:声明枚举类型 void:声明函数无返回值或无参数,声明无类型指针.
⒉控制语句关键字⑿个
(一)循环语句(5个) for:一种循环语句 while:循环语句的循环条件 break:跳出当前循环 continue:结束当前的循环,开始下一轮的循环 do...while:循环语句的循环体 (二)条件语句(3个) if:条件语句 else:条件语句否定分支 goto:无条件跳转语句 (三)开关语句(3个) switch:用于开关语句 case:开关语句分支 default:开关语句中"其它"分支 (四)返回语句(1个) return:函数的返回值语句(可以带参数也可以不带参数)
⒊其它关键字⑶个
const:声明只读变量 sizeof:计算数据类型长度 volatile:说明变量在程序执行中可被隐含地改变
⒋存储类型⑸个
auto:声明自动变量,一般不使用 extern:声明变量是在其他文件中声明 register:声明寄存器变量 static:声明静态变量 typedef:对类型进行重命名
✔说明→存储类型关键字是不可以同时出现的,也就是说在一个变量定义的时候是只能有一个。
① auto ⇿ 局部变量
📃概述→编译器默认所有变量都是
auto
的。注⇢默认的是局部变量的才是 auto 语句。⒈描述 ⇨ auto 用于定义一个局部变量为自动的,这意味着每次执行到定义变量的时候都会产生一个新的变量,并且对其进行初始化。
⒉注意 ⇨ 其实 auto 是可以进行省略,如果不是特别去指定的话,局部变量的存储模式是默认为自动的,例如:int a = 10;其实你这样写就是 auto int a = 10;只不过默认的。
⒊代码示例演示 ⇨ C语言修饰当中仅仅使用 auto 变量修饰局部变量是默认为整形,但是 auto 在全局变量的时候会出现编译错误。
#include<stdio.h> auto a = 0; int main(void) { }
🍏出现错误⇢全局变量范围的声明不能包含此存储类型。
#include<stdio.h> int main(void) { auto a = 0; return 0; }
🍊编译结果运行成功。
🍅拓展知识点如下↓
局部变量⇢包含在代码块中的变量叫做局部变量。局部变量具有临时性。进入代码块,自动形成局部变量,退出代码块自动释放。
全局变量⇢在所有函数外定义的变量,全局变量是具有全局性。
区别⇥局部变量具有全局型可以在全局使用、局部变量具有局限性只能在代码块当中使用。
代码块→用大括号{}扩起来(包围)就被称之为是代码块。
注:如果全局变量和局部变量冲突的话我们都是以局部优先为标准的。
作用域⇢该变量的有效区域范围,局部变量的有效范围通常都是在本函数或者是代码块当中是有效的。全局变量的作用域是全局有效的。
最后⇢在现在我们基本上不会定义 auto 因为在局部变量当中默认就是 auto,当然 auto 也是一个比较老的关键字,在编写程序代码的时候我们通常都是把它省略掉的。
② break ⇿ 终止
⒈描述 ⇨ 有时候会遇到这样的情况,不管表达式的检验的结果如何,都是需要进行强制终止循环。这个时候我们就需要用到
break
语句。⒉注意 ⇨ 语句终止并跳出循环,继续执行后面的代码。当然我们在使用break语句一定要注意的是break只能跳出当前所在的作用域的循环语句当中。示例代码如下↓
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { while (1) { while (1) { break; } printf("1"); } return 0; }
运行结果如下🖊
程序一直打印数字⒈
代码解析-这里break只能跳出内层循环但是由于外面还有一层循环它是不能跳出的。如果break在这里是在外面的一层while(1);语句当中那么它是可以跳出的注意看它的作用域[()]。
🍎注⇢break语句只能适用于循环语句和switch()分支语句当中。
⒊代码示例演示如下↓
#include<stdio.h> int main(void) { while (1) { printf("謓泽\n"); break; } return 0; }
运行结果如下🖊↓
謓泽
🍎代码解析-在上述代码之中 while 语句当中的表达式是一个条件永远为真的循环。但由于其中使用break语句,会使得程序在循环当中跳出,只运行了①次。
🍏拓展知识点→break语句也是可以配合switch分支语句进行使用如下代码↓
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { int a = 0; printf("Input integer number:"); scanf("%d",&a); switch(a) { case 1: printf("Monday\n"); break; case 2: printf("Tuesday\n"); break; case 3: printf("Wednesday\n"); break; case 4: printf("Thursday\n"); break; case 5: printf("Friday\n"); break; case 6: printf("Saturday\n"); break; case 7: printf("Sunday\n"); break; default:printf("error\n"); break; } return 0; }
运行结果如下🖊↓
Input integer number:4
Thursday
代码解析-在上述代码当中我们很容易知道当到case 4语句的时候执行打印语句内容。遇到break语句就会直接退出switch()分支语句。
🍊注→swtich()语句经常配合break来一起使用在代码当中的。
③ case ⇿ 匹配
⒈描述⇢
case
通常用作于 switch 语句当中,case 后面是接常量表达式以及字符型常量。⒉作用⇢这个 case 都是配合作用于 switch 关键字的。case 只要匹配上,则其他 case 不再进行匹配,直接顺序执行所有的代码。直到遇到 break 或者整个结束。
⒊注意⇢在 case 表达式的条件后面有一个冒号":",在编写程序的时候千万不要忘记哟。
代码示例演示→如下所示↓
#include<stdio.h> int main(void) { switch(1) { case 1 : printf("1"); // 打印“ 1 ” case 2 : printf("2"); // 然后打印“ 2 ” 继续 } return 0; }
表达式求值为等于一个 常量表达式 在转换到 表达式 的提升类型后的值,则转移控制到标号为该 常量表达式 的语句。
若 表达式 求值为不匹配任何 case 标号的值,而存在
default:
标号,则转移控制到标号为default:
的语句。若 表达式 求值为不匹配任何
case:
标号的值,且不存在default:
标号,则不执行 switch 体的任何部分。🍊注→必须要有break语句它才能够退出当前的case语句,不然就会执行case下面的语句。
🍈说明→case语句⇲
case 10: printf("..."); break; //正确 case 8+9: printf("..."); break; //正确 case 'A': printf("..."); break; //正确,字符和整数可以相互转换 case 'A'+19: printf("..."); break; //正确,字符和整数可以相互转换 case 9.5: printf("..."); break; //错误,不能为小数 case a: printf("..."); break; //错误,不能包含变量 case a+10: printf("..."); break; //错误,不能包含变量
🍋细节→在 [case] 语句表示条件后有一个冒号" : "我们在编写程序的时候不要忘记。
🍅拓展知识点⇢switch case 在有些场景下是可以配合 enum 枚举类型进行使用的。
④ char ⇿ 字符型
⒈描述 ⇨
char
字符型变量是用来存储字符常量的变量,字符型变量在内存空间所占字节大小为1个字节,%c 所对应的是打印字符的格式数据,取值范围是 -128 ~ 127。无符号字符unsigned char的取值范围:0~255。⒉作用 ⇨ 定义一个字符型变量的方法是使用关键字 char,例如:char str = 'A';
⒊注意 ⇨ 字符数据在内存中存储的是字符的 ASCll 码,即使是一个无符号整数,其形式与整数的存储形式一样,因为C语言的字符型数据与整形数据之间通用。
⒋实际上字符型是被称之为整形字符类型('单引号')如果不信你可以用sizeof关键字求下单引号字面值、常量看下它所在的字节大小是不是一个整形(⒋字节)的。注→在C++当中它还是一个字节的。
#include <stdio.h> int main(void) { char a = 'a'; printf("%c\n", a); return 0; }
🖊存储示例 a 字符的代码,在上面代码当中字符也是区分大小写的。
#include <stdio.h> int main(void) { char ch = 'A'; printf("%d\n", ch); return 0; }
🍊在上面带代码当中,例如,ASCLL 码中,整数 65 代表大写字母 A。因此,存储字母 A 当中实际上存储的是整数 65 的值。
🍏拓展从技术层面上来看,char 是整数类型。同时也是一种特殊的类型字符,这是因为:可以用单引号表示字符常量如→'A'、'8',单引号' '也是一个字符。
🍅注→字符'1',和阿拉伯数字的①是不一样的,字符是字符,阿拉伯数字是整数,示例代码如下所示↓
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { char a = '1'; printf("%d", a); }
📙编译运行结果如下→49『ASCll码的字符1对应着十进制的49,因为我们这里的是格式符是%d所以应该是字符转换十进制的形式』
拓展→我们可以通过一个程序看下char类型数字的1和char类型单引号的'1'是否是相等的。
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { char a = '1'; char b = 1; printf("a = %d\n", a); printf("b = %d\n", b); if (a == b) printf("YES\n"); else printf("No\n"); return 0; }
运行如下🖊
a = 49
b = 1
No
★上述代码值得去思考下,如果你明白了其中的道理,那么你也就明白了。
拓展知识点如下↓
假设以 signed char 为例。
0000 0000 ⇢ 其中最高比特位是符号位『红色位符号位』从而意为着只有⒎个数值位。
假设,以原码位标准如下↓
1111 1111 ⇥ -127
0111 1111 ⇥ +127
1→ 符号位表示负数、0→ 符号位表示正数。 注:负数是因为char实际上实际也是整形类型,而且是有符号的那么必然会出现负数的。
注:负数以补码为标准,正数原码、反码、补码、表达都是一样的。
说明⇢当 char 表示为负数的时候如下↓
表示的数字有2的七次方 2^7=128个 ,从 -127~-(负)0
其中-(负)0的原码是→1 000 0000 补码是→1 0000 0000,多出了一位比特位。
由于 char 只取 ⒏位,所以截断后变为 0000 0000,这与+0所表示的数是一样的,所以为了不能浪费 1000 0000 这个数值,我们规定用1 000 0000 这个位来表示 -128 的数字。
⑤ const ⇿ 修饰
⒈描述 ⇨ 有时候我们会想定义一个不能被修改的变量。那么举个例子吧(生活当中的一些依据)有些数据是可变的比如我们的年龄、工资等...那么有些数据是不能变的,你的亲生父母、血型等等...那么如果我们要用C语言描述的话就有这两个知识点常量和变量了。
常量:不能被改变的量 变量:可以被改变的量 1、整形常量(八进制、十进制、十六进制)注意:八进制中个位不能超过7,十六进制不能超过F,F表示15 2、浮点常量(1.2、1.0、1e-001) 3、字符常量('a'、'\n'、'\017'、'\0xf')
⒉作用 ⇨ 如果一个变量被
const
修饰,那么它的值就不能再被改变被赋值为"只读"属性。⒊注意 ⇨ 在数组当中的下标我们是不能使用 const 修饰的量的,可以说被修饰 const 其实本质还是个变量。
const dobule PAL = 3.14159; //正确的声明方式 const int MYLIST; //错误的声明方式
在创建常量时候必须要设置它的初始值这个是很容易出现的一个错误。
那么我们把变量 i 修饰成 const 类型修饰,然后把 i 的值进行改变看下程序的运行结果会发生什么(@^0^@)/,如下代码所示👇
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main(void) { const int i = 1; printf("%d\n", i); i = 10; return 0; }
运行结果🖊
在上述代码当中就可以说明从这里就可以说明定义const类型的变量是不能直接被修改的,不能进行二次赋值。
那么我们在举出一个代码的例子来看看是有关于const类型修饰数组下标的内容。
#include <stdio.h> int main(void) { const int num = 5; int arr[num] = { 0 }; return 0; }
运行结果🖊
错误表达式必须含有常量值。
证明了常属性—常属性就是不能被改变的属性,但是其实它的本质上还是变量。
所以我们数组的下标当中也必须要是常量虽说const它可以修饰常量,但是它实际上还是一个变量这个是我们要注意的。
🍎注→在上述代码当中用gcc的编译器上它是可以编译过去的,这种在Windows平台下大多数都是编译不了的,但是在Linux平台下都是可以编译过去的。
🍏拓展知识点→原因是在不同的编译器下对于C语言的支持标准都是不一样的。有的是基于支持标准C语言并且是可以在特定的平台下做扩展的。所以我们需要尽可能地保证使用标准C语言在这样子跨平台就显现出来它的好处了。
🍯重点→具有跨平台性。
🍊间接赋值
在const修饰的变量不可以直接被修改的,但是可以通过指针的方式进行间接修改。
示例代码如下↓
#include <stdio.h> int main(void) { const int i = 10; int* p = (int*)&i;//强转同一类型 printf("i1 = %d\n", i); *p = 20; printf("i2 = %d\n", i); return 0; }
运行结果🖊
i1 = 10
i2 = 20
🍏注⇢在上述代码的第四行当中我们是需要强制转换成与int* p同一类型整形指针的,不然编译器会发生warning(警告)
🍅被const变量修饰的意义→其实很简单就是我这个程序员不想修改这个变量,一旦我不小心进行了修改的话编译器会告诉我修改了这个变量。还有的话就是当我这个代码给别人的时候我用const进行修饰的时候当它改变我这个变量的时候就知道我在编写程序的时候实际上是我是不想修改这个变量的值的我设置为只读属性。
📒[const]修饰数组→在C语言中const还可以修饰数组,示例代码如下↓
const int Array[] = {1,2,3,4,5,6,7,8,9,10}; int const Array[] = {1,2,3,4,5,6,7,8,9,10};
const关键字修饰数组与修饰变量类似的,表名了此数组只能具有只读性,不可以被修改。如若我们一旦修改的话程序就会报错。如下例子所示↓
Array[0] = 'A';
运行结果🖊则程序会报错(左值指定的是const修饰的对象)
🧊第一种→[const]修饰指针
当你理解指针的时候就是可以把指针理解成地址的,说白了就是指针就是地址地址就是指针,指针变量说白了就是用来可以存储地址的。指针是一个地址,指针变量是一个变量。
C语言当中const修饰指针需要特别注意以下二点如下↓
⒈用来限定指向空间的值是不可修改的。
⒉限定指针式不可修改的。
示例代码如下↓
#include<stdio.h> int main(void) { int i = 5; int j = 10; const int* p1 = &i; printf("第一种:%d\n", *p1); //*p1 = 30; //错误,左值指定const对象,用const修饰(*)p1指向变量是不能修改的也就是取地址i。 p1 = &j; //p1的值是可以改变的因为本质上p1只是一个变量,所以 p1=&j. printf("第二种:%d\n", *p1); printf("j:%d\n",j); printf("i:%d\n", i); return 0;
运行结果🖊
第一种:5
第二种:10
j:10
i:5
如果我们在第八行添加这个代码的时候如下代码所示。
*p1 = 30;
运行结果🖊
错误,左值指定 const 对象,指向空间的值是不能修改的也就是取地址i
📝拓展知识点⇢任何一根变量名在不同的应用场景当中,代表不同的含义。示例代码如下↓
int x = 1; int y = x;
第一个x是:x的空间,变量的属性,左值。
第二个x是:x的内容,数据的属性,右值。
🍀 const 修饰指针四种情况
🍎注→在上述代码当中我们已经讲述了第一种情况解析来我们再来说说 const 修饰指针的另外的三种情况如下↓
🧊第二种→[const]修饰指针
int a = 10; int const *p = &a; *p = 20; p = 100;
📝说明→第二种的这种和第一种的修饰是一模一样的,p指向的变量是不可以直接被修改的。不过一般情况下都是把类型放在前面这样更加符合它的语义。
🧊第三种→[const]修饰指针
int a = 10; int * const p = &a; *p = 20; p = 100;
📝说明→在上述这个代码的const修饰的是p变量,前面(int *)代表的是类型。这里代表的是p的内容是不可被直接修改的,指针变量(*p)指向的内容是变量(a)是可以被进行修改的。
🧊第四种→[const]修饰指针
int a = 10; const int * const p = &a; *p = 20; p = 100;
📝说明→第一个const修饰的是*代表p指向的变量是不可以直接被修改的。第二个const修饰的是p变量代表的是p的内容是不可被直接修改的。
总结如下↓
⒈const修饰的(*)代表的是指向的值是不可被修改的,变量可以被修改。
⒉const修饰的是变量p代表的是变量是不可被修改的,指向的值是可以被修改。
🌺const 修饰函数参数
在const修饰符也可以修饰函数当中的参数,当不希望这个参数值在函数体内被意外修改的时候进行使用。如下代码所示↓
#include<stdio.h> void show(const int* p) { printf("*p = %d\n", *p); } int main() { int a = 20; int* p = &a; show(p); }
🍎说明→在上述代码当中函数的形参用const修饰了指针(*)说明了我们不想改变变量(p)所指向变量(a)的地址。这种被称之为预防性编程的一个概念。
🍏拓展知识点⇢在C语言当中任何函数都一定要形成临时变量包括指针变量。
🍊const修饰函数返回值
用一个代码例子来讲述下 如下代码所示↓
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> const int* fun() { static int a = 10; return &a; } int main(void) { const int* ret = fun(); return 0; }
🍎上述代码解析⇢这个代码的意义就是当我们不想通过指针通过返回值的方案修改函数内部当中的变量。
🍑注→在上述代码当中需要注意以下几点如下↓
⒈在函数当中对其变量a进行了静态局部变量修饰目的就是它是属于局部变量一旦出了函数当中就会销毁变量。所以我们需要让变量修饰静态局部使得延长它的生命周期。
⒉在主函数当中指针变量用cosnt进行了修饰就说明返回的时候我们是不能对其指向的地址进行修改变量的值,一旦对其进行修改编译器就会报错error。这个做法是达到目地的。
⒊在这里如果我们把主函数当中指针变量中const去掉不对其进行修饰,此时如果我们对其进行修改编译器只会报出警告warning并不会报错 作用 提醒你对其返回值进行了const修饰。
⑥ continue ⇿ 跳 回
述 ⇨ C语言的
continue
和 break 语句是有一些类似的!但是它并不是强制进行终止的。⒉作用 ⇨ 结束本次的循环,即跳过本次的循环体中尚未执行的部分,执行下一次的循环操作,这就是 continue 和 break 语句的最大区别。
⒊注意 ⇨ 很多刚学C语言初学者可能会不明白这两个关键字 continue 语句和 break 语句,其实这两个语句本质上都是非常容易理解的。
🍪 continue语句结束本次循环并不会终止循环。
🍪 break语句则是结束整个循环过程不再判断执行循环的条件是不是成立。
⒉句话
⒈continue 结束本次循环 。
⒉break 跳出循环。
📃代码示例演示如下↓
#include <stdio.h> int main(void) { int i; for (i = 0; i < 5; i++) { if (i == 1) { printf("謓泽\n"); continue; } printf("number = %d\n", i); } return 0; }
运行结果🖊
number = 0
謓泽
number = 2
number = 3
number = 4
📙代码解析如下↓
通过上述代码的运行结果我们可以知道i等于1的时候会执行continue语句使得本次循环结束所以在上述运行结果当中我们并没有执行numbre=1的原因就是这个而执行打印了泽奀。
但是循环本身并没有结束,因此程序会继续执行下去。直到不满足循环条件为止。
🍰拓展知识点→continue语句结束本次循环会回到条件判断表达式当中的地方进行判断而不执行continue后面的语句。📃代码示例演示如下↓
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int n = 1; do { printf("謓泽%d\n",n); n++; if (n) continue; printf("CCCC\n"); } while (n<5); return 0; }
运行结果🖊
謓泽1
謓泽2
謓泽3
謓泽4
⑦ default ⇿ 匹配失败
⒈描述⇢用作于 switch case 语句中的
defalut
当中。⒉作用⇢default 只有在 switch 语句当中才会执行,在 case 语句匹配失败的时候才会执行,当然也有特殊情况。
⒊注意⇢特殊情况是当我们的 case 匹配成功了,但缺少了 break 语句(下述代码我会举出例子的)
代码示例演示→如下所示⇲
#include <stdio.h> int main(void) { int i = 3; switch (i) { case 1: printf("C1"); case 2: printf("C2"); default: printf("謓泽"); } return 0; }
运行结果🖊
謓泽
🍊说明⇢ default 只有在 case 匹配失败的时候才会执行。
那么再举出一个例子带大家看看↓
#include <stdio.h> int main(void) { int i = 3; switch (i) { default: printf("泽奀"); case 1: printf("C1"); case 2: printf("C2"); } return 0; }
运行结果🖊
泽奀C1C2
🍏上述代码解析⇢这个代码和上面一个代码很像只不过改变了default的位置。
注意→当case不匹配的时候执行default,但是注意不是执行default就就退出了,它依旧会执行下面的语句,因为这个代码default在case语句前面,有些人可能会误以为没有匹配的case执行default就不会再执行case了,这只是可能如果出现了break和上面代码是不会执行的。
⑧ do ⇿ 至少执行一次
⒈描述⇢
do
通常配合 while() 循环进行使用。⒉作用⇢在 do 配合 while() 循环使用是至少能保证拥有一次循环的。
⒊注意⇢如果条件为真,那么就会一直进行循环条件,直到判断条件为假为止。
🍈拓展知识点如下↓『三种循环结构,分别是 do...while、while、for』
对于任何一种循环的时候一定是要有循环当中的判定条件的[注→死循环除外]
说明⇢一般正常的循环都要有④部分组成。
⒈循环条件初始化整形变量。
⒉while括号里面的表达式也就是循环判定。
⒊代码块当中执行的语句 例:输出printf()打印语句。
⒋循环条件更新 因为总有一次我们要退出循环[注→死循环除外]
🍏注→do...while()和for()与while()语句最大的区别是它是至少能执行一遍代码块的,而当我们的for()语句初始化和while()语句初始化一开始便不满足表达式为假的话,那么它是可以一次都不会执行代码块当中的内容的。
代码示例如下↓
#include <stdio.h> int main(void) { int i = 0; do { printf("謓泽"); } while (i); return 0; }
运行结果🖊
謓泽
代码解析⇢至少执行一次循环,再退出,这就是do...while语句循环和while语句循环最大的区别,如果这里是while语句的话一次都不会执行循环体。在这里我们也需要注意下在do...while()语句当中while()语句是有分号(;)的。
场景使用do...while() 「举例说明」
张三同学此时说:do...while()这个语句感觉都没有必要都不知道在什么场景下使用它了,我们还需要这个干嘛?
张三同学这你就不知道了吧(☆-v-)相比while()语句以及for()语句之下do...while()语句雀氏可能不是那么的起眼,但是任何一个关键字能拥有必然是有一个别人所没有拥有的能力。然而do...while()语句就有这个能力,在上面也说了它可以保证代码块至少执行一次。这个不就是for()和while()所没有的吗,正是因为这个do...while()语句在某些场合下是可以起到非常大的作用的。像一些项目或者玩游戏一上来是肯定会让你玩一下然后再进行判定,说白了就是先尝试做一次,然后再判定。
⑨ double ⇿ 双精度浮点型
⒈描述⇢双精度浮点类型的使用关键字是
double
,它在内存中占用的是 8 个字节。⒉作用⇢定义一个双精度类型变量,然后其赋值浮点型数字,最后通过输出语句将其显示在控制台上
⒊注意⇢double 类型在程序默认输出 6 位小数点,有效数字是 6~7,格式符为 %lf
示例代码如下↓
#include<stdio.h> int main(void) { double a = 3.14159; printf("双精度浮点型 = %lf\n",a); return 0; }
🖊编译运行结果→双精度浮点型 = 3.14159
小数的输出 如下所示↓
%f 以十进制形式输出 float 类型;
%lf 以十进制形式输出 double 类型;
%e 以指数形式输出 float 类型,输出结果中的 e 小写;
%E 以指数形式输出 float 类型,输出结果中的 E 大写;
%le 以指数形式输出 double 类型,输出结果中的 e 小写;
%lE 以指数形式输出 double 类型,输出结果中的 E 大写。
#include<stdio.h> int main(void) { double a = 3.24359223; printf("双精度浮点型 = %.40lf\n", a); return 0; }
运行结果🖊
双精度浮点类型 = 3.24359223...(以及小数点后面32位的0)
🍏注⇢40代表的是后续的小数点的数字保留多少位的数字。
拓展→浮点数的内容精度损失。此时,张三同学在它的编译器发现了一个问题。
上述的代码当中它发现在后面的小数点三十二位的0当中有一个数字不是0而是1。
謓泽这个是怎么回事呢?明明在实际的值当中并没有出现过1的这个数字阿。
张三同学出现这个问题我们就把它称之为四字:精度损失,从理论的角度上来说它打印出来的值因该是全0的数字的。那么想要知道为什么是这个样子的我们就需要了解一个概念就是数据的存储。数据存储这里就不详细的介绍了,在博主的[C 系列]的文章当中有一个已经讲的非常详细了。如果你对这个不了解的话可以去康康(●'◡'●)
🍎精度损失⇢上述的拓展当中我们提到过精度损失,接下来我们用代码来讲述下倒不如说是证明下这精度损失到底是不是为真正是浮点数类型为精度损失了。
#include <stdio.h> int main(void) { double x = 1.0; double y = 0.1; printf("x=%.20lf\n", x); printf("y=%.20lf\n", y); if ((x - 0.9) == 0.1) printf("No精度损失\n"); else printf("Yes精度损失\n"); return 0; }
运行结果🖊
x = 1.00000000000000000000
y = 1.00000000000000001000
Yes精度损失
结论⇢浮点数在进行比较的时候,绝对不能直接使用==号来进行比较。
因为⇢浮点数本身就是存在着精度损失,从而导致最终结果有细微的差别的。
⑩ else ⇿ False
⒈描述⇢
else
通常配合于 if 语句来进行使用。⒉作用⇢else 用在 if 语句当中进行选中二中选一,也可以进行多段分支语句进行使用。
⒊注意⇢else 通常是 if 表达式 为假,则执行 else 语句块的内容。
示例代码如下↓
#include<stdio.h> int main(void) { int i = 0; if(i) { printf("泽奀1"); } else { printf("泽奀2"); } retturn 0; }
在上面的代码中 if() 判断语句判断变量 i 为假,因为表达式当中值为假,则执行 else 的语句块内容。
拓展知识点⇢else 的配对问题🔥
不知道大家有没有对 if() 以及 else语句的匹配问题是否会有困惑,接下来我们就来说说它。
示例代码如下↓
#include<stdio.h> int main(void) { int x = 1; int y = 2; if (x == 10) if (y == 20) printf("你好\n"); else printf("你不好\n"); return 0; } /*相当于这样如下↓ #include<stdio.h> int main(void) { int x = 1; int y = 2; if (x == 10) { if (y == 20) printf("你好\n"); else printf("你不好\n"); } return 0; }*/
🖊运行结果⇢无
如↑代码の解析⇢第一个if()条件不满足,不满足就不执行第二个if()语句的内容。那么有很多小伙伴不知道的话就会想当然的认为是打印else语句当中的内容。但是实际上它在编译器上并不会输出任何的东西。原因是因为else语句采用的是[就近原则] 所谓的就近原则 就是:在你不带花括号{}的时候,else语句离哪个if()语句越近的话永远都是和最近的if()语句进行匹配。这就是所谓的"就近原则"。那么通过这个我们就知道else语句是和if(y==20)进行匹配的,所以这里if(x==10)为假就一定不可能执行else语句,当我们吧x==10改成if(x == 1)的时候就会执行else语句当中的内容。因此一句话的总结就是else语句的匹配采用就近原则。
那么我们来看看第二个示例代码。
#include<stdio.h> int main(void) { int x = 1; int y = 2; if (x == 1) if (y == 20) printf("你好\n"); else printf("你不好\n"); return 0; }
运行结果🖊
你不好
在上述代码当中我们可以知道else会采取就近原则和离近的if()语句来进行匹配,当第一个判断语句为真的时候。那么第二个判断表达式条件不满足就会不执行第二个if()语句则执行else语句当中的内容。
当然,如果你不喜欢这种的话也可以用花括号{}来编写,这样也是比较推荐的,因为更方便我们去观察让人容易读懂。示例代码如下↓
#include<stdio.h> int main(void) { int x = 1; int y = 2; if (x == 1) { if (y == 20) { printf("你好\n"); } else { printf("你不好\n"); } return 0; } }
这种就是比较好的编程习惯推荐都带上花括号{} (o゚v゚)ノ
⑩① enum ⇿ 枚举类型
⒈描述⇢声明外部变量和函数是一种基本 数据类型,它可以让数据更简洁,更易读。
定义:
enum
枚举名 {枚举元素1,枚举元素2,……};⒉注意⇢第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
⒊作用⇢枚举类型枚举的是一堆的常量,它和结构体和联合体都是不一样的。结构体和联合体所定义的都是一些变量的值,而枚举的内部存储的都是常量。常量与常量之间使用逗号(,)来进行隔开的,内部的这些常量都是可以当中数据来进行使用的。
代码示例演示→如下所示↓
#include<stdio.h> enum color { //枚举常量 black, white, gules, }; int main(void) { printf("%d\n", black); printf("%d\n", white); printf("%d\n", gules); return 0; }
运行结果🖊
0
1
2
当然枚举也是可以列举在我们生活当中的,例如:三原色、一个星期是多少天,一年有多少月份。这些都是可以采用枚举类型进行一一列举的。
🍅注→实际上枚举常量是可以进行赋初值的。如下代码所示↓
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> enum color { //枚举常量,进行赋初值。 black = 2, white = 4, gules = 6, }; int main(void) { printf("%d\n", black); printf("%d\n", white); printf("%d\n", gules); return 0; }
运行结果🖊
2
4
6
🍎枚举变量的定义
(一) 先定义枚举类型,再定义枚举变量
enum Color { red = 1,orange,yellow,green,cyan,blue,purple }; enum Color seven;
(二) 定义枚举类型的同时定义枚举变量
enum Color { red = 1,orange,yellow,green,cyan,blue,purple } seven;
(三) 省略枚举名称,直接定义枚举变量
enum { red = 1,orange,yellow,green,cyan,blue,purple } seven;
枚举的优点
我们可以使用#define宏定义常量,为什么还需要用到枚举,来说说枚举的优点↓
- 增加代码的可读性和可维护性。
- 和#define相比定义的标识符比较枚举有类型的检查,更加具有严谨性。
- 便于程序当中调试。
- 使用比较方便,依次就可以定义多个枚举常量。
- 封装性好。
在switch case 在有些场景下是可以配合 enum 枚举类型进行使用的。 示例代码如下↓
#include<stdio.h> enum Number { Exit, }; int main(void) { int num = 0; printf("Please input num value:"); scanf("%d", &num); switch (num) { case Exit:printf("退出exit!");break; } return 0; }
运行结果🖊
Please input num value:0
退出exit!
🍊enum说明⇢⒈本质上是定义制作一组强相关性的常量 比如:颜色枚举常量那么它都是同一类型的常量。⒉为什么不可以定义还需要用枚举呢? 结论你最终用所谓的整形或者是用所谓的枚举,当然这个实际上在C的编译器都是可以支持的。之所以用枚举常量定义有两个原因:①专业的技术做专业的事情,这里当然指的是枚举(enum)
#include<stdio.h> enum color { black, white, red }a; int main(void) { a = red;//a充当的是枚举变量颜色(拥有自说明性:写代码的人一下看的懂) printf("%d %d",a,red); return 0; }
②如上代码说明:枚举的代码具有自描述性,不用对代码进行过多的解释。
★拓展→常量不多且没有上面相关性可以使用宏定义,反之用枚举。
⑩② extern ⇿ 外部声明
⒈描述⇢
extern
变量称之为外部存储变量。⒉定义⇢extern 声明了此程序当中将要用到但尚未定义的外部变量。
⒊注意⇢通常,外部存储类型都用于声明在另一个转换但又中定义变量。
简单的来说实际上就是除非有extern关键字,否则都是变量的定义。像这样如下↓
int a; //声明,也是定义。 extern int a; //声明,不是定义,告知编译器你等下我是有这个变量的
🍊定义⇢本质就是开辟空间(定义只能有一次)。
🍏声明⇢告知,现在这个变量已经在某个地方已经定义好了,你现在不用定义了。前期的时候先不用管它,等下告知编译器我后面会定义这个变量的(声明可以多次)
#include <stdio.h> //外部变量声明 extern int x; extern int y; int addtwonum() { return x+y; } //全局变量的声明 int x = 1; int y = 2; int main(void) { int result; result = addtwonum(); printf("result = %d\n",result); return 0; }
在所有的函数外部定以的变量叫做:全局变量。
全局变量是可以在整个程序当中进行使用的。
当然在程序当中我们尽量在代码当中少用到全局变量比较。
注意:当局部变量和全局变量同名的话,局部变量的使用优先。
🖊运行结果如下↓
result = 3
那么如果我们没有加入extern 外部存储变量程序运行的时候会发生什么。如下所示↓
如果程序是这样的话即使我们没有用extern外部存储变量,程序也是可以运行成功的。因为我们在前面已经定义了x,y变量。
下面再举出一个例子,我们实现Add()函数用 extern() 外部声明变量来实现。
test.c 文件
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> extern Add(int x, int y); int main(void) { int x = 20; int y = 30; int ret = Add(x, y); printf("ret = %d\n", ret); return 0; }
Add.c文件
#define _CRT_SECURE_NO_WARNINGS 1 int Add(int x, int y) { return x + y; }
编译运行结果🖊
ret = 50
最后⇢我们还需要在注意一点,声明并不代表开辟了空间。如果我们只是声明了的话却没有进行定义也是不可以的🙅
注⇥在.h当中变量的声明必须带上extern!函数的声明可以带也可以不带extern.一般你这么定义函数那么你就在.h文件当中怎么去进行声明即可。
🍅拓展⇢一般来说用<>包括的是C语言当中的头文件或者是库函数当中的头文件," "是自己使用自己所自定义的头文件[约定俗称的规矩],实际上.c源文件,我们只需要在.h头文件当中加上去就可以了。而源文件可以直接不需要的指代头文件。
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> extern int x = 1; int main(void) { printf("%d",x); return 0; }
🍊上述代码当中就是声明变量却没有进行变量的定义,一句话『声明并没有开辟空间』
📃模块化extern的使用
📒如上证明变量和函数是可以进行跨文件的访问的。
⑩③ float ⇿ 单精度浮点型
⒈描述⇢单精度浮点类型的使用关键字是
float
,它在内存中占用的是 4 个字节。⒉作用⇢定义一个单精度类型变量,然后其赋值浮点型数字,最后通过输出语句将其显示在控制台上。
⒊注意⇢float 类型在程序默认输出 6 位小数点,有效数字15~16,格式符为%f的。
🍅注-在C语言当中我们通常用的都是双精度浮点型类型,因为编译器在默认浮点数类型的时候就是默认为double类型的。
#include<stdio.h> int main(void) { float a = 3.14159; printf("单精度浮点型 = %f\n",a); return 0; }
编译运行结果→浮点型 = 3.14159
小数的输出 如下所示↓
%f 以十进制形式输出 float 类型;
%lf 以十进制形式输出 double 类型;
%e 以指数形式输出 float 类型,输出结果中的 e 小写;
%E 以指数形式输出 float 类型,输出结果中的 E 大写;
%le 以指数形式输出 double 类型,输出结果中的 e 小写;
%lE 以指数形式输出 double 类型,输出结果中的 E 大写。
🍏拓展知识点⇢强制类型转换
当我们需要把一个类型转换成另外一个类型的时候,我们就需要用到强制类型转换。
(类型)值
在这里我们不能把小的类型去转换成一个比它类型大的值,不然就会出错的。
printf("%d\n",(short)32768);
运行结果 ⇢ -(负)32768
🍊注→当然强制类转换只是从那个变量计算出了一共新的类型的值,它并不会改变那个变量,无论是值还是类型都是不会改变的,只是计算了一个新的值出来。在这里要注意一点:强制类型转换的优先级是要比四则运算要高的,如果我们在使用强制类型转换需要注意这点。
⑩④ for ⇿ 循环「灵活」
⒈描述⇢循环允许您编写一个执行指定次数的循环控制结构,在循环语句当中for()语句是应用最灵活的。
⒉定义⇢
for
(循环变量赋初值、表达式1;循环条件、表达式2;循环变量、表达式3) 语句块当执行到 for() 语句的时,程序首先计算第一个表达值,接着判定第二个表达式的值。如果第二个表达式为真,程序就执行语句块的内容。当执行完语句块的内容回到表达式当中,接着并计算第三个表达式;然后检验判定第二个表达式是否为真,为真执行循环;如此反复,直到第二个表达式的值为假,则退出循环。
⒊注意⇢for()循环嵌套尽量三层即可,太多可能会懵。
拓展知识点如下↓『三种循环结构,分别是 do...while、while、for』
对于任何一种循环的时候一定是要有循环当中的判定条件的[注→死循环除外]
一般正常的循环都要有④部分组成。
⒈循环条件初始化整形变量。
⒉while括号里面的表达式也就是循环判定。
⒊代码块当中执行的语句 例:输出printf()打印语句。
⒋循环条件更新 因为总有一次我们要退出循环[注→死循环除外]
🍎注→对于for()循环来说在它的循环表达式有③个而while()和do...while()只有①个同时在循环当中也是for()循环用的最多了,因为它的语法结构是很紧凑的。
代码示例如下↓
#include<stdio.h> int main(void) { int i = 0; int sum = 0; for (i = 1; i <= 100; i++) { sum = sum + i; } printf("sum = %d\n",sum); return 0; }
运行结果🖊
sum = 5050
程序思路:
执行到 [for] 语句时,先给 i 赋初值 1,判断 i<=100 是否成立;因为此时 i=1,i<=100 成立,所以执行循环体。循环体执行结束后(sum的值为1),再计算 i++。
第二次循环时,i 的值为 2,i<=100 成立,继续执行循环体。循环体执行结束后(sum的值为 3),再计算 i++。
重复执行步骤 2,直到第 101 次循环,此时 i 的值为 101,i<=100 不成立,所以结束循环。
每条 [for] 语句包含 3 个用分号隔开的表达式。这 3 个表达式用一对圆括号括起来,其后紧跟循环语句或语句块。
省略表达式1的时候,其后的分号是不能省略的。
有些程序当中省略表达式2(循环条件)的话,可能会导致程序发生死循环
所谓的 "死循环" 就是:循环条件永远成立,循环会一直进行下去,永不结束。死循环对程序的危害很大,一定要避免。通常都是在 for 循环上的判断条件上出现"死循环"
省略表达式3的话(自增自减),就不会再修改表达式2的循环变量了,除非你在语句块中加上了自增自减运算符。
for 循环的嵌套
for()循环嵌套实际上就是在 for 语句里面再次进行for()语句的使用这种叫做 循环嵌套!
那么接下来举个代码示例,来讲解下什么是for()循环。如下代码所示↓
#include <stdio.h> int main(void) { int i, j; for(i=1; i<=4; i++) { //外层for循环-第一层 for(j=1; j<=4; j++) { //内层for循环-第二层 printf("%-4d", i*j); } printf("\n");//换行 } return 0; }
运行结果如下↓
1 2 3 4
2 4 6 8
3 6 9 12
4 8 12 16
🍊解析上述代码如下↓
外层 for 第一次循环时,i为1,内层 for 要输出四次 1*j 的值,也就是第一行数据;内层 for 循环结束后执行 printf("\n"),输出换行符;接着执行外层 for 的 i++ 语句。此时外层 for 的第一次循环才算结束。
外层 for 第二次循环时,i为2,内层 for 要输出四次 2*j 的值,也就是第二行的数据;接下来执行 printf("\n") 和 i++,外层 for 的第二次循环才算结束。外层 for 第三次、第四次循环以此类推。
通过上述代码我们可以看到,内层 for 每循环一次输出一个数据。而外层 for 每循环一次输出一行数据。
⑩⑤ goto ⇿ 跳转
⒈描述⇢
goto
语句允许把控制无条件转移到同一函数内的被标记的语句。⒉作用⇢goto 后的标识符就是要跳转的目标。当然这个标识符要在程序的其他位置给出并且其标识符要位于函数内部。
⒊注意⇢在任何编程语言中,都不建议使用 goto 语句。因为它使得程序的控制流难以跟踪说的通俗一点就是灵活性较强,使程序难以理解和难以修改。但是这并不是绝对的,很多大项目当中还是会经常使用 goto 语句的,但是这需要你对这些代码所在的位置以及作用域非常清晰。
🍊注→在有些公司当中是明确不允许规定使用 goto 语句的。究其原因是因为能力不怎么强的话会误用它的。因为这个 goto 语句实在是过于灵活了,因此在某一处代码块当中我们如果要使用到 goto 语句的话就免不了的会修改其它哪一出的代码。这样有的时候反而会让代码搞的越来越麻烦的。
⒈代码示例演示如下↓
- 先用标识符[NUM] 定义。
- 再用goto语句进行跳转。
#include<stdio.h> int main(void) { int number = 1; NUM:do { if (number == 5) { number += 1; goto NUM; } printf("number=%d\n", number); number++; } while (number < 10); return 0; }
运行结果🖊
number=1
number=2
number=3
number=4
number=6
number=7
number=8
number=9
📙在上述代码的解析→在上述代码当中我们是明确的可以看出 goto 语句的作用的,在判断条件当中的时候[number == 5]的时候会发生跳转(到NUM:当中)从而不执行后面的内容。这里的功能实际上和continue语句是非常类似的。但是它们本质上还是不一样的。
⒉代码示例演示如下↓
先用goto语句进行跳转到标识符[NUM] 语句当中。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int i = 0; goto NUM; for (i = 1; i <= 5; i++) { printf("謓泽%d!\n",i+1); } NUM: printf("C语言!\n"); return 0; }
运行结果🖊
C语言。
📙在上述代码的解析→在这里我们是可以直接根据goto语句也就是后面所指定的标签所对应的位置处也就是说会把循环语句当中的代码给省略掉了或者说跳过,然后再向下进行执行。
🍎注⇢goto语句它只能在本代码块内当中使用的。不能在函数当中或者其它文件跨文件使用。
⑩⑥ if ⇿ 判断
⒈描述 ⇨ if 语句为判断表达式,if() 语句的基本构成就是对条件进行判定。
⒉作用 ⇨ if 语句通过对表达式的判断,判断表达式非0即为真,0则为假。
⒊注意 ⇨ if 语句可以有多种的使用方式,要学会多多利用,这样对程序可以事半功倍。
🍊语句流程执行图如下↓
示例代码如下👇
#include<stdio.h> int main(void) { if (1) printf("泽奀\n"); return 0; }
运行结果🖊
泽奀
解析→在判断语句if()当中表达式为真,则执行if()语句所在的作用域的范围的语句。
#include<stdio.h> int main(void) { if (0) printf("泽奀\n"); return 0; }
运行结果🖊
泽奀
解析→在判断语句if()当中表达式为假,则不执行if()语句所在的作用域的范围的语句。
🍊双层以及多层if()语句的使用如下↓
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main(void) { int i; printf("请输入数字(1)/(0):"); scanf("%d", &i); if (i == 1) { printf("好好学习!早日成为C语言大佬!\n"); } if (i == 0) { printf("学习太累我还是去板砖吧!\n"); } if (i != 1 && i != 0) { printf("你输入的数字,错误!\n"); } return 0; }
运行结果🖊
假设输入 1 运行结果: 好好学习!早日成为C语言大佬!
假设输入 0 运行结果: 学习太累我还是去板砖吧!
假设输入其它数字,运行结果:你输入的数字,错误!
🍅注→如果像这种判断条件太多可以使用 switch() 分支语句会更加方便。
🍏补充知识点⇢if 语句通常是来配合 else 语句来进行使用的。
🍓拓展知识点⇢if 语句实际上也能用来当做单或多行注释的作用,示例代码如下↓
语句⇢C语言由⒈个分号;隔开的就是代表着一条语句
表达式⇢在C语言当中,用各种操作数把变量连接起来的时候,形成有意义的式子,就叫做表达式。对于操作符不了解的小伙伴可以看看博主这篇文章希望对你有所帮助。
#include<stdio.h> int main(void) { if (0) { printf("泽奀\n"); printf("泽奀\n"); printf("泽奀\n"); printf("泽奀\n"); } return 0; }
运行结果🖊
无
🔥说明⇢是不是起到了注释的作用,因为我们这里的if()语句当中的判断表达式为假值为(0) 当然这样的代码是不进行推荐的,因为这个代码它是很容易自身会出现这个问题的。之所以说是因为让大家知道有这样的一种方法。
⒉种条件 if else
⒊种条件以上 (if) (else if) (else) 注:if语句还可以配合嵌套来进行使用。
代码⇢我们用代码来实现下上述的一些知识点的使用如下↓
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main(void) { int n = 0; while (scanf("%d", &n) != EOF) { if (n == 1) printf("one\n"); else if (n == 2) { printf("two\n"); if (n % 2 == 0) //嵌套! printf("even\n"); } else printf("other numbers\n"); } return 0; }
运行结果🖊
1
one
2
two
even
3
other
🍻注⇢这里是多组输入的。
⑩⑦ int ⇿ 整形类型
⒈描述→整形变量或整形常量类型的使用关键字是
int
,它在内存中占用的是 4 个字节。⒉作用→定义一个整形类型变量,然后进行赋值整形数字(自右向左),最后通过输出语句将其显示在控制台上也可以这样理解→在内存中找一块区域,命名为变量,用它来存放整数。
⒊注意→int 类型是整形,格式符 %d。整形类型的取值范围-2147483648 ~ +2147483647
#include<stdio.h> int main(void) { int a = 520; printf("a = %d\n", a); return 0; }
运行结果🖊 → a = 520
🍊注:同时 int 类型同样也可以适用于格式符为 %c 格式符来进行printf的打印
#include <stdio.h> int main(void) { int i = 97; printf("%c", i); return 0; }
运行结果🖊
a
🍏上述代码解释→97(十进制)用十进制的ASCll码转换为字符对应着97⇢a(小写字母)
🍅拓展知识点↓
定义一个变量是需要类型的,这个是基本语法决定的,那么类型从而决定了变量开辟空间的大小。『一般来说我们用的最多的类型就是 int 类型』
C语言当中为何要有类型: 本质对内存进行合理化划分,按需求索取。
为什么在C语言当中有这么多种类型: 根据不同场景,根据不同场景来开辟内存空间的大小。实际上就是可以为了你的需求来减少你的内存空间的成本。本质用最小成本解决多样化当中的场景。
二进制的快速转换方法如下↓
假设是: 67 ⇢ 64 + 2 + 1 = 2^6+2^1+2^0 = 2x2x2x2x2x2+2x1+2的0次方=64+2+1=67
十进制转二进制:0000 0000 0000 0000 0000 0000 0010 0011
那么二进制转十进制也是非常好转化的直接用:BCD码(8421)的方法即可。
⑩⑧ long ⇿ 长整形
⒈描述⇢
long
int 跟 int 整形是差不多的,不同的区别是就在与取值范围,和 bit 大小不一样,针对不同取值,应用不同的数据类型变量。占用的字节为⑧字节,取值范围~2147483648~2147483647。
⒉作用⇢定义一个长整形类型变量,然后其赋值整形数字,最后通过输出语句将其显示在控制台上 也可以这样理解:在内存中找一块区域,命名为 变量,用它来存放长整数。
⒊注意⇢%ld 用来输出 long int 类型,ld 是 long decimal 的简写,当然 %d 也是可以的。
#include<stdio.h> int main(void) { int a = 1314520; printf("a = %ld\n", a); printf("a = %d\n", a); return 0; }
运行结果🖊⇥两次编译结果均为 a = 1314520
🍅拓展知识点↓『变量的命名规则,注意事项↓』
Ⅰ变量名不要定义的太复杂,简单的字母数字以及下划线即可。
Ⅱ我们在命名变量可以尽可能看需要进行变量的命名见名知意。
Ⅲ定义全局变量的时候变量名是可以带g的,以示代表全局变量的含义。
Ⅳ尽量以最少||最短的词汇来表达明确的意思。
⑩⑨ register ⇿ 寄存器
📃概述→
register
最快的关键字,寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。⒈作用⇢通过 register 变量,程序可以把某个局部变量指定存放在计算机的某个硬件处理器当中,而不是内存当中。这样做的好处是可以提高程序的运行程序的运行速度。不过,这只是反映某个程序员的主观意愿。
⒉描述⇢register 变量称为寄存器存储类变量。
⒊注意⇢编译器可以忽略 register 对变量的修饰。
🍏存储的金字塔如下↓
示例代码如下👇
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main(void) { register int i = 0; printf("%d", &i); int j = 100000; while (j) { j--; } return 0; }
📙上述代码解析↓
①.寄存器修饰变量的时候是不能取出地址的。
②.在多次重复的时候,就可以对其使用寄存器修饰,注:根据情况来~
拓展⇢如果想有效的利用寄存器 register 关键字,必须像汇编语言程序那样了解处理器内部结构,知道可用于存放变量的寄存器的数量、种类、以及工作方式。但是 不同计算机对于这些细节可能是不同的,因此,对于一个具备可以移植性的程序来说,register 作用并不大。
注⇢寄存器是离CPU越近,所以它的效率是非常的高的[最高]
🍊寄存器的本质→在硬件的层面上,提高计算机当中的运行效率。因为不需要从内存里读取数据的。
🍅寄存器的核心知识
⒈我们通常采用局部变量采用register的,全局变量会导致CPU当中的寄存器被长时间占用。
⒉一个变量被存放到寄存器当中,那么这个变量的效率就会大大的提高不需要缓存读取。
⒊如果要使用,最好不用大量的使用,因为寄存器的数量是有限的。
②⑩ return ⇿ 返回「栈帧」
⒈描述 ⇨
return
返回值这种形式就能够定义返回值。⒉作用 ⇨ return 返回值语句的作用是终止一个函数的执行,结束当前代码块中return后的语句,即return后的语句不再执行,从当前函数退出,为该函数返回一个指定的expression(表达式)的值。
⒊注意 ⇨ return 在自定义函数使用当中,自定义函数必须是返回类型整形类型(int)才能够进行返回到整形类型当中。
🌸注→在讲解 return 语句之前我们先来看看一组代码。示例代码如下↓
#include<stdio.h> char *fun() { char str[] = "Hello word"; return str; } int main(void) { char *s = fun(); printf("s = %s\n",s); return 0; }
运行结果🖊
乱码
📙在上述代码解析⇢上述的代码当中我们我们不难得知它会产生这样的运行结果,因为我们很清楚这个数组(str)字符串当中它是一份临时数组(地址)。因为它是在fun()函数当中的代码块当中进行定义的,临时数组就是:调用函数的时候形成该数组,返回的时候该数组的空间就会被释放出去了。当然在这里我们如果对其进行用static进性修饰的话,此时出了代码块当中空间就不会被释放出去了保留当前的数据。
🍏更深层次的理解如下↓
当我们调用完函数的时候其实此时数组当中的数据是还是存在的。那么如何证明呢很简单我们就来调试下看下此时s当中的值是怎么样的。
从上述代码当中我们可以看出在调用printf()打印函数之前对应曾经在fun()函数的数据是还在的,根本原因就是计算机并不会清空数据,虽然这块数据无效了。但是我们依旧还是可以看到的。
在上述的图片当中为什么在打印printf()函数之后s的数据会直接乱码呢。
🔥重点→根本原因就是printf()实际上也是函数,既然printf()它也是函数,那么printf()它同时也是要遵守一个规则的调用printf()的话就会形成栈帧,返回printf()的话就会释放栈帧。注→新的栈帧结构是会覆盖老的或者说是前面的栈帧结构,对我们来说它进行了二次覆盖的时候那么你曾经的Hello word的字符串便不会存在了。经过这个讲述你是不是更加清楚了原来没想到就短短的十行代码却有这么多的知识点(●'◡'●)
🍎注⇢这个临时数组的意思就是在我们所创建的函数当中或自定义函数当中被称之为临时数组。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> char* fun(char *str) { return str; } int main(void) { char str[] = "Hello word"; printf("s = %s\n", fun(str)); return 0; }
运行结果🖊
s = Hello word
📙上述代码解析⇢指针指向了地址,并不会销毁所在的空间。
📚拓展知识点如下↓
⒈在C语言当中是没有真正意义上的字符串类型的,但是在C语言是有字符串的。它不想其它的一些高级语言类型当是拥有字符串类型的. 例如:Java、Python、C++这些高级语言当中它们是有字符串类型(str)的。
⒉在C语言当中字符串的结束标志是'\0'的,它并不作为字符串内容的一部分。仅仅知识作为字符串当中的结束标志位。但是它依旧是字符,要占空间的。长度不包括'\0',容量包括'\0'。
🍊拓展知识点⇢如何理解栈帧[在调用函数的时候是先一次性给你开辟好栈帧空间然后再调用函数的函数里面内容的叫做为栈]销毁,计算机当中所谓的删除数据究竟是在做什么?
在计算机当中释放空间释放真的要将我们的数据全部清0/1,所谓的删除数据它绝对不会把数据删除和清0/1。因为清0和清1本质上就是目标空间进行写入。假设你今天有1个G的空间,最后清空全部变成0了,那么你也会写入1个G的数据那么这样删除数据就相当于和把一份1个G的文件拷贝到你的电脑当中是需要同等的时间的。这个显然是不可能的因为只会把拷贝到你的电脑当中写入进数据是需要一定的时间的,而我们删除数据的时候是很快的不然它们两个就会造成一个同等的时间这个是显然不可能的。
一句话:拷贝1个G的时间花了1min 钟,删除1个G的时间也需要花上1min钟的时间。这个显然是不可能的,因为我们在实际当中明显不会的。我们都非常地清楚拷贝的时间绝对比删除的时间是要花的时间是要多的。
📑计算机在清空数据本质只要设置该数据无效的时候即可。这句话可能比较难以理解那么我们可以来举个例子:比如说我们需要3G的C语言资料就会有对应的三十个对应的数据块,对我们计算来说我们写入的时候正儿八经的会把3G的空间写入进去。但是我们在清空的时候其实只是需要设置成3个比特位或者30个比特位,那这可以说是相差非常大的了。换言之你有若干个数据块那么要清空一个对应的0.5G的空间本质上只需要把若干个比特位给它进行清空就可以了。后续再来数据的时候就可以直接可以再该数据块进行写入,那么这样的话就可以不用再一个是删除的时候只删除很小量的数据。第二个就是不用高频的对磁盘这个存储介质或者其它的任何存储介质进行充电放电或者进行对应的读写同时也可以达到增强硬件的使用寿命。所以一般我们在删除数据的时候并不是把数据进行清空,而是只需要设置该数据的无效即可。具体如何"无效"是需要了解一些文件系统相关的内容的。
🍋文件系统⇢文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。
📚栈的介绍⇢在执行函数的时候,函数内部局部变量的存储单元都是可以在栈上进行创建的。函数执行结束的时候这些存储单元会被自动的进行释放,栈区主要存放运行函数所分配的局部变量,函数的参数,返回数据,返回地址等。
🌴return — 终止函数的执行
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main(void) { printf("C语言"); return; printf("下面的语句"); }
运行结果🖊
C语言
🍰上述代码解析→在上述代码当中我们可以得知返回值后面的语句是不会执行的也证明了return 后面的语句是不会再被执行的。相当于执行完上面的语句不执行下面的语句退出了。
🍋创建函数实现两个变量相加并且返回到实参当中去。
#include<stdio.h> int max() { int a = 10; int b = 10; return a + b; } int main(void) { int ret = max(); printf("ret = %d\n", ret); return 0; }
运行结果🖊
ret = 20
🧇上述代码解析⇢函数的返回值是指函数被调用之后,执行函数体中的代码所得到的结果。 这个结果通过 return 语句返回。
那么可能会有小伙伴会有疑问?这个在max()函数当中的变量a、变量b难道就不是临时变量吗。那为什么这里调用完之后返回的时候会把结果返回到整形变量ret当中去呢。
那么根据上面的我们说过:此时的a,b是一个临时变量,最终在return返回的时候外部的ret是怎么能够拿到return返回的临时变量的对应的值。它难道不会被释放掉吗?那么为什么ret会读取到返回的内容的。
其实这个不难理解→当你max()函数调用完成之后它才能够拥有返回值,调用完之后函数不是应该会被释放掉吗。那么这个max()当中的临时变脸为什么可以拿回来呢。这个就和我们这里要讲述的 return 语句是有关系的。
对于我们来说这个a,b叫做是临时变量,我们返回的时候拿的并不是真的a,b,而是a,b当中对应的内容。
这个max()形成栈帧会被释放掉了,但是并不会真正的把数据进行清空。
②① short ⇿ 短整形
⒈描述 ⇨ 短整型类型的使用关键字是
short
int,它在内存中占用的是 2 个字节,数值范围是:-32768~32767。⒉作用 ⇨ 定义一个短整型变量,然后其赋值整形型数字,最后通过输出语句将其显示在控制台上。
⒊注意 ⇨ 在32位平台下如windows32位中 short 一般为16位,格式符整形符号表示: %hd
🍅拓展知识点如下↓
任何数据在计算机当中,都必须要被转换成为二进制的,因为计算机只认二进制。根本原因是因为计算机当中硬件的内存单元是两Tai的,但是实际上我们人都是擅长于十进制的。
🍏注⇢long、short、int 它们都是属于整形类型的范畴当中的,只不过它们的区别是所占的字节大小,存储空间的大小,以及数值范围的大小是不同的。关系:long long > int > short.
②② signed ⇿ 有符号
⒈描述 ⇨ 有符号基本整形是指
signed
int 类型,其值是基本的整型常数。编写时,常将其关键字 signed 进行省略。有符号基本整形在内存中占 4 个字节。signed int 的取值范围是:-2147483648~2147483647
⒉作用 ⇨ 定义一个有符号整形变量,然后其赋值整形型数字,最后通过输出语句将其显示在控制台上。
⒊注意 ⇨ 通常说到的整形,都是只有符号基本整形 int 类型。
#include<stdio.h> int main(void) { signed int a = 1234; signed int b = -520; printf("a = %d b = %d\n", a, b); return 0; }
运行结果🖊
a = 1234 b = -520
🍏注:初学者要特别 signed 和 unsigned 的区别,很多初学者在刚学的时候会分不清。
🍎拓展⇢我们必须先把我们初始化的数值先转换成二进制,大白话说的就是先把数据存储在变量当中然后再把值转换成二进制。
②③ sizeof ⇿ 字节
⒈描述 ⇨
sizeof
是 C 语言中保留关键字。也可以认为是一种运算符,单目运算符。并非是"函数",也叫长度(求字节)运算符,sizeof是一种单目运算符,以字节为单位返回某操作数的大小,用来求某一类型变量的长度。其运算对象可以是任何数据类型或变量。⒉作用 ⇨ 求特定(一种)的一个类型对应的开辟空间数的大小,单位为字节。①字节=8bit位
⒊语法 ⇨
sizeof(
类型) - sizeof
表达式①返回 类型 的对象表示的字节大小。
②返回 表达式 类型 的 对象表示 的字节大小,不应用隐式转换到表达式。
🍅注意⇢sizeof 是C语言当中的操作数并不是函数所以它是可以不用带( )的
#include<stdio.h> int main(void) { printf("char 字节:%d\n",sizeof(char)); printf("short 字节:%d\n",sizeof(short)); printf("int 字节:%d\n",sizeof(int)); printf("long int 字节:%d\n",sizeof(long int)); printf("long long int字节:%d\n",sizeof(long long int)); printf("float 字节:%d\n",sizeof(float)); printf("double 字节:%d\n",sizeof(double)); return 0; }
🍏编译运行结果为👇
char 字节:1
short 字节:2
int 字节:4
long int 字节:8
long long int字节:8
float 字节:4
double 字节:8
🍅拓展⇨知识点如下↓
变量的本质→在内存中开辟一块空间,用来保存数据。 而定义一个变量时需要一个类型的,这个是基本语法决定的。那么类型决定了就会开辟空间的大小。
🍒那么当你懂了上述缩写的知识点的话,那么我们再举出一个例子供大家更了解 sizeof 的作用。示例代码如下↓
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main(void) { int a = 10; printf("%d\n", sizeof(a)); //1 printf("%d\n", sizeof(int));//2 printf("%d\n", sizeof a); //3 printf("%d\n", sizeof int); //4 return 0; }
选择题如下↓多选题选择正确的✔
A :1 2 3
B :1 4
C :1 3 4
D :2 3 4
正确✔的答案是:A
🍎解析→这里为什么④是错误的,因为 sizeof 是一个关键字,这个关键字它是不能够直接求另一个关键字的大小的,在这里的关键字指的是(int),像这种情况不带圆括号是不行的。
🍊计算机中的单位↓
⒈个比特位(bit)存放的是二进制当中的(0/1)
1 个字节(byte)存放的是⑧个比特位,我们在编程当中用的最多最常见的就是这两种单位。
再往上就是kb『1kb = 1024字节』后面的单位都是1024、再再往上就是mb、gb、tb、pb。
注→sizeof()既是操作数也是关键字,这点是初学者很容易不知道的。
🍏拓展知识点[强制类型转换]⇢值不变,类型发生变换。 所谓的强制类型转换指的是"并不改变该数据在内存当中的任何的二进制的组合,它仅仅改变的是解释该二进制就是所谓类型的概念"!
📙真实的强制类型转换和强制类型转换的区别⇢真实的转换是真的会改变其内存当中的值。而强制类型转换只是类型发生变化值并不会发生变化。
📑小细节⇢在很多面试的时候会出这样的一道题目。问你下述代码是几个字节...
int a; sizeof(a);
看到上述的题目很多人会不加思索的说这不就是④个字节吗?那么恭喜你当你回答这个的时候面试官会摇摇头小伙子你退下吧ヽ(✿゚▽゚)ノ在回答这个的时候我们应该说不同的编译器下所编译运行的结果是不同的比如在Linux系统当中的Gcc编译器和Windows系统下的Vs编译器它们是不同的,记住一句话→数据类型的大小都是由编译器来进行决定的。
②④ static ⇿ 修饰
⒈描述⇢
static
变量为静态变量,将函数的内部变量和外部变量声明成 static 变量的意义是不一样的。不过对于局部变量来说,static 变量是和 auto 变量相对而言的。尽管两者的作用域都仅限于声明变量的函数之中,但是在语句块执行期间,static 变量始终保持它的值,并且初始化操作只在第一次执行起作用。在随后的运行过程中,变量将保持语句块上一次执行时的值。⒉⇢static 作用如下↓
<1>修饰局部变量
📦保留上一次修饰变量的值,不被销毁,可以保留上一次的值,说明生命周期变长了!(本质上是改变了变量的存储类型)
<2>修饰全局变量
📦使得这个全局变量只能在自己所在的源文件内部使用,其它源文件不能使用。源文件实际上就是.c文件,注:你在.h文件当中进行了声明也是不可以的,也是只能在修饰全局变量当中的文件进行使用。如下图当中所示↓
<3>修饰函数
📦函数被静态 static 修饰函数也是在其他的源文件是不能被使用的,只能在源文件当中去进行使用也是和上面的修饰全局变量实际上是一个意思。
那么为什么要有修饰函数呢。实际上可以让我们的代码机制更加的安全,也就是我们可以用static修饰函数,然后用另一个函数调用我们封装后的函数 (套娃'doge') 。这其实就是static为什么需要有修饰函数和修饰全局变量的一个功能了。一句话:项目维护提供安全的保证!
⒊注意⇢其实修饰全局变量或者修饰函数,他们都是从外部链接属性变成内部链接属性的。
📢两个重要的知识点概述
㈠→全局变量是可以跨文件进行访问的。
㈡→函数也是可以跨文件进行访问的。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> void Num() { static int i = 1; i = i + 1; printf("%d\n", i); } int main(void) { printf("第一次调用:"); Num(); printf("第二次调用:"); Num(); return 0; }
🍏编译运行结果为↓
①第一次调用:2
②第二次调用:3
📙上述代码解析↓
📃在 Num 函数中定义了一个 static 型的整形变量 i,在其中对变量进行加 1 操作。之后在主函数 main 中通过显示提示语句,可以看到调用两次 AddOne 函数的输出,从结果上中可以发现 static 变量保持不变。说明了 static 静态变量修饰局部变量的时候是不会被销毁的,可以保留上一次的值,说明生命周期变长了,i变量并没有被释放。注⇢我们这里是用static修饰了变量i的。如果没有修饰的话,那么i一出形参作用域就会被释放。
💬注⇢static 修饰局部变量仅仅是改变了它的生命周期并没有改变修饰变量的作用域。
🍅最后⇢static关键字是我们必须要牢牢掌握的,它在有些程序功能当中是不可缺少的。
📃拓展⇢.h文件基本都是要被多个源文件所包含的,那么此时就会出现一个问题,就是被头文件包含的文件。这个问题其实很好被解决,只需要添加如下代码即可。
#pragma once
相信经过上面的讲述你已经大致明白了static这个关键字的作用了,那么我们再举出一个代码的例子通过代码你能够更明白它的作用。
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> void fun() { static int i = 1; printf("%d ",i++); } int main(void) { int i = 0; for (i = 0; i < 10; i++) { fun(); } return 0; }
运行结果🖊
1 2 3 4 5 6 7 8 9 10
解析代码如下↓📑
在上述的运行结果啊当中如果我们不加静态局部变量修饰i的话,此时的运行结果就是全部为1的数字。正是因为我们加入了这个static关键字才能保证fun()函数的作用域而不被销毁上一次的值而是保留了上一次的值从而可以说明生命周期延长了。
②⑤ struct ⇿ 结构体类型
⒈描述 ⇨ C 语言允许用户自己指定这样一种数据结构,它由不同类型的数据组合成一个整体,以便引用,这些组合在一个整体中的数据是互相联系的,这样的数据结构称为结构体
struct
,它相当于其它高级语言中记录。Ⅰ. 结构体 . 成员名,访问的内容。
Ⅱ. -> 结构体指针 -> 成员名,指向对象的内容。注→必须是指针类型。
⒉作用 ⇨ 结构体和其他类型基础数据类型一样,例如 int 类型,char 类型 只不过结构体可以做成你想要的数据类型。以方便日后的使用。
在实际项目中,结构体是大量存在的。研发人员常使用结构体来封装一些属性来组成新的类型。
⒊⇨ 结构体在函数中的作用不是简便,其最主要的作用就是封装。封装的好处就是可以再次利用。让使用者不必关心这个是什么,只要根据定义使用就可以了。
⒋拓展⇢在C语言当中为什么回存在结构体这个东西?
那么我们就来说说计算机是为了去解决人的问题,之所以有结构体这个类型肯定是可以为了更好的帮助人去解决问题的。如果它反而帮的是倒忙的话那以前的大牛们为什么要弄出一根结构体类型的这个东西呢。结构体实际上是一些值的集合,结构的每个成员是不同的变量。所以在这里结构体实际上也是复杂对象类型称之为构造类型,我觉得可以把这个构造类型看成是一个项目的总共。而基本类型就是小的项目。而在这里很多人可能会联想到数组,但是数组是一组相同类型的元素集合。而我们结构体可以是不同类型的元素的集合。在这里用玩具盒子来表示结构体名,用其它玩具表示每个不同の成员。
🍊注意⇢在声明结构体时候,要注意大括号最后面有一个分号,编程的时候不要忘记了。
#include<stdio.h> typedef struct student //(2) {//(3) char name[20];//学生名字 (4) char sex[5];//学生性别 (5) char id[20];//学生学号 (6) short int age;//学生年龄 (7) }student;(8) int main(void) {(11) student mation = { "小明", "男", "123456789", 18 };(12) printf("%s\n", mation.name);(13) printf("%s\n", mation.sex); (14) printf("%s\n", mation.id); (15) printf("%d\n", mation.age); (16) return 0; }(19)
struct — 结构体的关键字。
student— 结构体的标签,可以随意的替换看你指向的对象是谁了。
struct student—— 结构体类型。
大括号 { } 里面的内容被称作是成员变量。 注意大括号后面必须要有分号,可以加成员列表,分号前面内容,可以是全局的结构体变量(一般都不会去使用),在代码中我们要尽可能的去少的使用全局变量 。
在函数中 mation —— 结构体类型的变量,当然这里我直接 student mation;是可以的,因为我用了typedef关键字,当然这个是可以随意定义结构体标签新的名字,不过我喜欢直接去定义结构体标签 (12)
结构体类型的变量 (mation) 需要存放结构体类型的成员变量,所以我们可以要给上一个大括号直接去接收这些结构体变量,到了这一步,我们的结构体类型的初始化已经🆗了。注意:结构体类型的成员变量的数据类型使用不要弄错了以及主函数当中的打印的占位符记得要和全局变量一一对应才行 (12)
最后,就是打印了。这里说下xxx.xxx在这个解释下什么意思:xxx.这个是结构体标签,通过这个后面的xxx是结构体当中类型的成员变量,也就是找到那个成员 (13~16)
🍈在我们使用结构体传参还是传址的时候,通常都会采用传址。如下代码所示↓
#include<stdio.h> //描述一个学生 typedef struct student //typedef —— 可以使用它来为类型取一个新的名字 { char name[20]; //学生名字 char sex[5]; //学生性别 char id[20]; //学生学号 short int age; //学生年龄 }student; //注意:对结构体类型进行重命名 void print2(student* tmp) { printf("%s\n", tmp->name); printf("%s\n", tmp->sex); printf("%s\n", tmp->id); printf("%d\n", tmp->age); } int main(void) { // struct student mation;// mation —— 结构体类型的变量 student mation = { "法外狂徒张三", "man", "10086", 18 };//结构体成员变量进行初始化 //实现传址 print2(&mation); return 0; }
🍋原因⇢如果要选择一种,我们尽可能的去选择传址,因为效率更高些,而且还可以省出一定内存空间,尤其是对大型项目的时候,两种我们就可以去选择传址。
🍑说明⇢定义结构体本质上是制作结构体。
🍅拓展知识点如下↓
⒈结构体只能够被整体的初始化,不能被整体进行赋值操作。
student mation = { "法外狂徒张三", "man", "10086", 18 };//√ student mation; mation = { "法外狂徒张三", "man", "10086", 18 }; //×
⒉如果要进行赋值只能单个结构体类型当中的成员变量进行赋值以及访问结构体类型成员访问。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> struct stu { int id; char c; char aaa[10]; }age; int main(void) { age.id; printf("%d\n", age.id = 18); printf("%c\n", age.c = 'c'); printf("%s\n", age.aaa = "ssss");//×(错误) }
🍰注⇢这里的age代表的是变量,而 (.) 代表的是访问后面的代表的是结构体类型变量成员。当然在这里如果你是用数组的话编译器是会报错的(代码倒数第二行)不过你可以用字符串拷贝函数把字符拷贝到变量访问的结构体类型数组的成员,也就是我们可以把报错给禁止🙅但是你需要在你的头文件加入:#pragma warning(disable:xxx)编译所处error:后的数字。
⒊在VS编译器当中如果我们想用sizeof关键字查看空结构体的大小是不行的,在gcc编译器统下空结构体大小是0字节。在不同平台下的编译情况说的都是不一样的。
②⑥ switch ⇿ 分支
⒈描述⇢if 语句只有两个分支可供选择,而实际问题中常需要用到分支的选择。当然,使用嵌套的 if 语句也可以实现多分支的选择,但是如果分支较多的话,就会使得嵌套的 if 语句层数较多,程序繁杂,并且可读性不好。C 语言中可以使用
switch
语句直接处理多分支选择的情况,将程序的代码的可读性提高。⒉作用⇢switch 后面括号中的表达式就是要进行判断表达式就是要进行判断的条件。在 switch 的语句块中,使用 case 关键字表示检验条件符合的各种情况,其后面的语句就是相应的操作。其中还有一个 default 关键字,作用是如果没有符合条件的情况,那么执行 default 后的默认情况语句。
⒊注意⇢switch 语句检验的条件必须是一个 整型表达式,这意味着其中也可以包含运算符和函数调用,而 case 语句检验的值必须是 整型常量,即常量表达式或者常量运算,注意:不能是变量必须是常量切记。
🍏拓展→在 swtich case 语句当中不得使用 return 语句⇵defaule语句可以出现在switch语句当中的任意一个位置。
[组合▞]switch case
switch case 的基本语法结构如下所示↓
switch(整形变量/(const #define)/整形表达式/枚举类型) { case var1:语句;break; ...... default:语句;break; }
示例代码如下👇
#include<stdio.h> int main(void) { char ch; /* 字符型&&取值范围-128~+127 */ printf("请输入本次开学成绩 :"); scanf("%c",&ch);/* 格式输入函数 */ switch(ch)//switch(常量表达式/整形类型包括字符/枚举类型) { case 'A':printf("你的成绩在90分以上!\n");break; case 'B':printf("你的成绩在80~90分之间!\n");break;//******************* case 'C':printf("你的成绩在70~80分之间!\n");break;// 终止所在程序的循环 case 'D':printf("你的成绩在60~70分之间!\n");break;//******************* case 'E':printf("你的成绩在60分以下!\n");break; default:printf("请输入有效成绩!\n");/* 输出不在范围数字! */ } return 0; }
🥂说明⇢在上述程序当中 假设输入字符A的话就打印字符A当中的打印语句,然后遇到 break 语句退出循环,那么其它在的字符也是一样的。如果输入字符范围不再A~E的话,就到 default,打印"输入有效成绩",switch 语句通常可以很好配合 break 以及 default 进行使用。
如何在什么情况下使用if()语句和switch()语句?
答: 在switch()多条分支的情况下解决不了的话那么我们就可以使用if()语句来进行条件判断。
在分支语句较多的情况之下我们就口语使用switch(),反之在判定的条件比较少的情况下我们就可以使用if()语句来进行条件的判断。
⒈switch() 语法结构当中 switch()语句本身没有啥子功能。
⒉case 语句完成了判定的功能。注→const变量和普通的变量不能放。
⒊break 完成的是分支语句的功能。
⒋default 处理的是异常情况的功能,从语法上default可以防止任意的位置当中。通常来说我们都是把defalut放在最后的,推荐把正确的功能写在case语句的前面而异常的写在后面。
⒌要有好的布局方式。
此时,我们假设想要用switch case语句来做输入数字1~5打印周内,输入数字6~7打印周末。
#include<stdio.h> int main(void) { int num = 0; while (1) { printf("Please Enter Your day "); scanf("%d", &num); switch (num)//switch(常量表达式/整形类型包括字符/枚举类型) { case 1: case 2: case 3: case 4: case 5: printf("周内!\n"); break; case 6: case 7: printf("周末!\n"); break; default:printf("Error!\n"); break; } } return 0; }
运行结果🖊
在上述代码当中如果多个不同的case语句匹配,要执行同一个语句的话推荐上述代码做法。
注⇢在这里 case 语句本身就是用来判定的。
🍑拓展知识点⇢在 case 语句当中后面是不可以用 const 修饰的只读变量的。
②⑦ typedef ⇿ 重命名
⒈描述⇢C语言允许为一个数据类型起一个新的别名,就像给人起"绰号"一样。就像是有一个人的名字叫:张三,它的小名叫:小三,类似于这样。
⒉作用⇢起别名的目的不是为了提高程序运行效率,而是为了编码方便,可以配合结构体关键字一起使用哟。
⒊注意⇢需要强调的是,typedef 是赋予现有类型一个新的名字,而不是创建新的类型。为了"见名知意",请尽量使用含义明确的标识符,并且尽量大写。
⒋本质⇢
typedef
的本质就是类型重命名(typerename)🍏typedef 的常用作用用处如下所示⇲
1.对类型进行重命名
当我们想要给定一个无符号整形类型的时候比如:unsigned int 的时候,这时就会导致一个问题我们写代码的人每次写这个无符号整形类型就太多了。此时我们便可以使用typedef这个关键字来对这个类型(无符号类型)重命名如下代码所示↓
typedef unsigned int u_int; //重命名u_int 相当于 unsigned int
此时我们定义变量只需要这样:u_int x 即可(●'◡'●)
2.对结构体类型进行重命名
示例代码如下👇
struct stu{ char name[20]; char sex[5]; char id[20]; short int age; };
在上述代码当中我们定义了一组结构体类型。那么每次当我们定义结构体类型变量都需要带上:struct stu mattion(变量)这样就会每次都比较的麻烦,所以推荐使用结构体类型进行重命名的方式。
所以此时当我们写结构体的话可以这样写如下↓非常推荐大家使用结构体的时候用typedef。
#include<stdio.h> typedef struct student { char name[20]; }student,mation; int main(void) { student mation = {"法外狂徒张三"}; student* p = &mation; printf("%s", p->name); return 0; }
运行结果🖊
法外狂徒张三
🌸结论⇢typedef 的本质就是对类型进行重命名的一种解决方案。在C语言当中这个关键字的存在就是对可能会面对冗长的类型命名方面的可以使用 typedef 对于比较长或者是不太理解的数据类型从而进行简化。
拓展知识点⇢typedef 还可以给数组、指针、结构体等类型定义别名。
📑注意→在一个项目当中对类型进行过度重命名的话是会增加我们写代码的阅读成本的。所以,当我们去使用typedef的时候是一定要适度的去使用的。
有一种情况 typedef 是不能这样定义的就是在对函数指针类型重命名的时候。
代码示例演示:如下所示↓
typedef void(*)(int) pfun_t;
上述定义是错误的,在函数指针变量当中定义因该是在其函数指针变量名当中。
代码示例演示:如下所示↓
typedef void(*pfun_t)(int);
这样才是对函数指针类型 void(*)(int) 进行了重命名定义为 pfun_t。
②⑧ union ⇿ 共用体
⒈描述⇢union 也被称之为是 "共用体",共用体 是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
⒉作用⇢共用体看起来很像是结构体,只不过关键字由 struct 变成了 union。共用体和结构体的区别在于,结构体定义了一个由多个数据成员组成的特殊类型,而公用体定义了一块为数据成员共享的内存,也就是当我们想让联合体成员共同享有其中的一块空间。
注意⇢对于共用体初始化的时候,只是需要一个初始值就够了,其中的类型必须是由和共用体的第一个成员的类型是一致的。
⒊重点→联合类型实际上也是一种特殊的自定义类型,联合体本质上也是你自己新构建的一种类型也可以用(->)操作符或者(.)操作符来访问结构体类型当中成员变量。
#include<stdio.h> union Date { int i; char c; };//注意分号 int main(void) { union Date Union = { 97 }; printf("i = %d c = %c\n", Union.i, Union.c); return 0; }
运行结果🖊
i = 97 c = a
a 的ASCll码为 '97',在这里打印的都是97,说明他们是公用的联合体。
说明:如果共用体的第一个成员是结构体类型,则初始化值中可以包含多个用于初始化结构的表达式。
那么假设我们放不同的值,因该怎么办呢。如下代码所示↓
#include<stdio.h> union Date { int i; char c; };//注意分号 int main(void) { union Date Union; Union.i = 97; Union.c = 98; printf("i = %d c = %c\n", Union.i, Union.c); return 0; }
运行结果🖊
i = 97 c = 98
那这个时候有的人可能会有疑问了,为什么不能直接这样。
union Date Union = {97,98};
这样是不行的,让我们看看运行的错误结果。 从这里说明了如果对其进行初始化的话只能够初始化①个值。但是如果是右值赋值给结构体变量访问结构体成员变量编译器可以运行。
🍋联合体的特点
联合体的特点都是联合体成员变量当中都是共用同一块空间的。
下面用代码来表述↓
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> union S { char i; int j; }; int main(void) { union S S1; printf("%p\n", &(S1)); printf("%p\n", &(S1.i)); printf("%p\n", &(S1.j)); return 0; }
运行结果🖊
0x7ffdd676519c
0x7ffdd676519c
0x7ffdd676519c
从上述的运行结果当中可以看出来→联合体的特点并不是给每一块内存开辟空间的,而是它的空间完全是有可能当作同一块空间进行使用的。在申请空间的时候都是从较低的地址处进行开辟的然后向上进行分配的,上述代码当中c永远在i的低地址处。
②⑨ unsigned ⇿ 有符号
⒈描述⇢无符号基本整形是指
unsigned
int 类型,其值是基本的整型常数。编写时,常将其关键字 unsigned 进行省略。无符号基本整形在内存中占 4 个字节,取值范围 0~4294967295。是没有负数的,也就是只有正数,%u是用来输出无符号类型的。⒉作用⇢定义一个无符号整形变量,然后其赋值整形型数字,最后通过输出语句将其显示在控制台上。
⒊注意⇢通常说到的整形,都是只有符号基本整形 int 类型。
#include<stdio.h> int main(void) { unsigned int a = 1234; unsigned int b = -520; printf("a = %u b = %u\n", a, b); return 0; }
运行结果🖊
a = 1234
b = 4294966776
从运行结果可以得知 unsigned 是无符号的,证实了数字范围:0~4294967295
🍊拓展→数据的存储在C语言当中必须要有类型的才行。
变量存的过程⇢字面数据必须先转换成补码,在放入空间当中,所以所谓的符号为,完全看数据本身是否携带了+-号,和变量的有无符号位是无关的『正数:原码反码补码都是一样的』『负数:以补码为标准』『正数的最高位为0表示,负数的最高位为1表示』
变量取得过程⇢取数据一定要先看变量本身得类型,然后再决定要不要看最高位得符号位。
③⑩ void ⇿ 无类型
⒈描述 ⇨
void
的字面意思是 "无类型",void *则为"无类型指针"。void *可以指向任何类型的数据无论是整形类型、字符类型、浮点数类型还是结构体类型都是可以的。⒉作用 ⇨ 对函数返回的限定、对函数参数的限定。常用在程序中对定义函数的参数类型、返回值、函数中指针类型进行声明。
⒊注意 ⇨ 如果函数当中没有返回值的时候,我们应该使用 void 类型。
代码示例演示 如下所示↓
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> void swap(int *pa, int *pc) { int tep = 0; tep = *pa; *pa = *pc; *pc = tep; } int main(void) { int a = 10; int b = 20; printf("交换之前:%d - %d\n", a, b); swap(&a, &b); printf("交换之后:%d - %d\n", a, b); return 0; }
运行结果🖊
交换之前:10 - 20
交换之后:20 - 10
🍊注→在上述代码它的返回值就是void类型所以是无需任何的返回值。
🍅拓展知识点→void无传参当你在定义程序的时候加入无传参的时候,虽说程序也会运行起来。但是会有warning提示就是不需要的参数。void几乎只有"注释"和限制程序的作用,因为从来没有人会定义一个void变量。让我们试着来定义,因为它实际上没有任何的意义。但是 void*则不同 任何类型的指针都可以直接赋值给它无需进行强制类型转换。
void x; // void 类型是不能定义变量强制的不允许定义变量
🍏[void]类型是不能用作数据类型当作无变量来使用的这样属于非法使用"void"类型的。
🧊void类型不能定义变量
⒈定义变量的本质是:开辟空间。
⒉void作为空类型理论上是不应该开辟空间的即使开辟了空间也仅仅只是作为占位符看待。
占位符→就是先占住一个固定的位置,等着你再往里面添加内容的符号,广泛用于计算机中各类文档的编辑。
⒊无法开辟空间那么也就无法作为正常变量来进行使用既然无法使用编译器就直接不让他定义变量了。
🍯void类型从当函数的形参列表-告知用户的编译器该函数是不需要传参的。
无类型指针 - 可以指向任何类型的数据。
void*
📑注意一点⇥当我们需要使用(void*)类型的的指针变量去指向某一类型的变量的时候必须要对其进行类型转换。
隐私类型转换
隐私类型转换 - 正确形式如下↓
要先进行运算首先我们就要保证它的类型是一样的。所以这里就是我们为什么要进行隐私类型转换的原因。
int *p1 = NULL; void *p2 = NULL; p1 = (int *)p2;
📃代码解析→这里我们必须要把无类型指针进行强制类型转换成相对应的类型才行的。
🔥⒉个知识点
⒈*void 被任何指针类型所接收的。
⒉*void 也可以接收任意的指针类型。
拓展⇢相当于通用接口可以接收各种类型当中的参数,这样的话可以简化对接口当中设计。
🔖那么接下来我们就来用代码演示下第二个知识点。
int *p1 = NULL; void *p2 = NULL; p2 = p1;
📋注→在我们未使用类型指针的时候可以把值赋值给空指针 [NULL]
*void 定义的指针变量可以进行运算操作
答案⇢void *定义的指针变量不🙅可以进行运算操作。代码示例如下↓
void *p = NULL; p++; p--;
编译运行结果会产生(error)错误。
究其原因是因为占位符以及void*本身就不能确定自身的字节大小(0)等...
注⒈→在上述代码当中在Linux平台上是可以编译通过的 因为其大小为⒈的。
注⒉→在void*类型当中是不能对其进行解引用操作的。
③① volatile ⇿ 🥶
⒈描述⇢
volatile
的本意是 "易变的、不稳定" 因为访问寄存器要比访问内存单元快的多,所以编译器一般都会作减少存取内存的优化,但有可能会读脏数据。⒉作用⇢volatile关键字和const关键字一样都是对其类型进行修饰。volatile关键字是可以直接影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错。说的通俗简单便于大家好理解的话就是volatile这个关键字是不希望被编译器进行优化,达到便于稳定访问内存的目的。
⒊注意⇢volatile 这个关键字可以说是C语言当中最不常用的一个关键字,甚至很多时候大部分人都还是不清楚这个关键字知道可能知道这个关键字但也很少知道它的一个作用,你可能在你所打过的代码当中都没有使用过这个关键字。在不了解这个关键字的时候实际上我也一样😂其原因就是我们在学习C语言的时候基本上是没有任何场景下需要我们使用volatile这个关键字的。
拓展⇢volatile实际上别的应用场景下还是会用到例如:提供程的安全、状态标记变量以及嵌入式(在stm32单片机当中屏幕jcq的io口进行操作)等都会出现使用volatile这个关键字。当然,实际上volatile在面试当中也是会经常被问到的一个考点。
volatile int n = 1; // volatile 限定类型 int* p = (int*)&n; //取地址并且强制转换整形指针类型赋值给指针变量p int val = *p; // 未定义行为
任何通过volatile左值结果对其拥有volatile限定类型的对象尝试读或写会导致未定义行为。
struct s { int i; const int ci; } s; // s.i 类型是 int,s.ci 的类型是 const int volatile struct s vs; // vs.i 和 vs.ci 的类型各是 volatile int 和 const volatile int
volatile限定的是结构体或联合体类型,其成员会获取其所属类型的限定。当通过(.结构体变量的访问成员,是通过操作符( . )访问的)或(-> [xxx]指针箭头指向对象的内容)运算符时。
🍊拓展知识点⇢volatile其它知识点如下↓
const要求你不要进行写入就可以了,volatile的意思是你读取的时候,每次都要从内存读。
volatile就叫做易变关键字,但这里仅仅是描述它修饰的变量可能会变化,要编译器注意,并不是它要求对应变量必须变化,这点我们需要特别注意。
③② while ⇿ 循环
⒈描述⇢
while
关键字是循环语句结构,其一般形式如下 while(表达式) 语句。⒉作用⇢while 语句首先检验一个条件,也就是括号中间的表达式。当条件表达式的值为真时候,就执行后面的语句块。每执行一遍循环,程序都将回到 while 语句块,重新检验条件是不是满足。
⒊注意⇢while 循环的关键点是可以一次都进行不循环,这跟 do while 语句不同。
拓展知识点如下↓『三种循环结构,分别是 do...while、while、for』
对于任何一种循环的时候一定是要有循环当中的判定条件的[注→死循环除外]
一般正常的循环都要有④部分组成。
⒈循环条件初始化整形变量。
⒉while括号里面的表达式也就是循环判定。
⒊代码块当中执行的语句 例:输出printf()打印语句。
⒋循环条件更新 因为总有一次我们要退出循环[注→死循环除外]
代码示例如下↓ 题目 - while 循环计算1~100的值。
#include<stdio.h> int main(void) { int sum = 0; int i = 0; while(i<=100) { sum = sum + i; i++; } printf("sum = %d\n",sum); }
运行结果🖊
5050
代码示例分析如下↓
[while] 语句首先去检验一个条件,也就是括号当中的表达式。while循环关键字实际上和if语句的判定方式是一样的,都是先对(执行)表达式当中的值进行判定的。得到对应的值判定真还是假,判定条件成功执行代码块条件判定失败退出循环条件。
当表达式的值为"真"(非'0'即为真),就执行紧跟其后面语句的语句块。每执行一次循环,程序都会回到 [while] 语句处,重新检验条件是否满足
如果一开始条件就并不满足,则跳过循环体当中的语句,直接执行后面的程序代码
如果第一次检验时候条件就满足的话,那么在第一次或其后的循环过程中,必须要有使条件为"假"的操作,不然程序会无法终止,陷入到死循环当中
我们通常将"表达式" 称为 循环条件,把"语句块"称为循环体,整个循环的过程就是不停判断循环条件、并执行循环体代码的过程
当然 whilie 循环也是可以进行嵌套使用的,这里就不再讲解了自己尝试下,上述介绍了for的这里就不再多说了。
while(表达式) { //循环体内容 while(表达式) { //循环体内容 } //循环体内容 }
表达式值为真执行第一层,第二层表达式为真执行。再执行第一层表达式为假则不执行。
如果第一层为假,直接跳出,第二层不会执行。
第一层为真,执行第二层为假,那么再回到第一层判断。
当然while循环也是可以用break和continue的。
📚 最后🖍
本篇文章总共有3.6w+的字数,是博主写的字数最多的一篇文章。之所以写这篇文章其目的是我看很多博客讲C语言关键字都是很不详细的,最多也就几千字给带过去了。所以博主就想着写一篇非常详细的C语言关键字,这样对于那些初学C语言的小伙伴们来说是非常友好的,尽管博主也学了挺久的C了,看了看这篇文章也是觉得非常不错的毕竟还算得上详细ヾ(^▽^*)))
👌总结-学会了✔
★最后★ ⇢点赞👍 +关注👋 +收藏📑 ==学会✔