计算机语言的发展:
一)机器语言:计算机直接可以进行识别的语言,硬件直接可以读懂的,就是0和1的二进制数据,因为计算机本身可以进行识别的就是0和1这样的二进制数据;
有人写代码就是使用二进制写的:0010,0001,1010,但是使用二进制写代码非常难,每使用一个功能的时候都需要进行查询,什么样的二进制序列代表什么样子的功能
二)汇编语言:用注记符来代替0和1的二进制序列
一)创建新项目:
xxx.c为后缀的文件是源文件,xxx.h为后缀的叫做头文件
选择空项目,点击下一步
二)创建源文件,写出C语言代码:
点击鼠标右键,添加,新建项,.c为后缀的才是源文件,并且要加入头文件
三)运行C语言代码,可以点击ctrl+Fn+F5来编译链接运行C语言代码
全局变量:再大括号外定义的
局部变量:在大括号内部定义的,当全局变量和局部变量冲突的时候,局部变量优先
printf函数是在屏幕上打印相关的数据的,是一个输出型库函数,是C语言本身提供的
#include<stdio.h>的意思是标准输入输出头文件,使用库函数就需要包含对应的头文件
打印出来的最中结果是以字节为单位的,C语言的标准规定,sizeof(long)>=sizeof(short)
#include<stdio.h> int main() { printf("%d\n", 100); //sizeof是一个操作符,是用来计算类型和变量大小的 printf("%d\n", sizeof(char)); printf("%d\n", sizeof(int)); printf("%d\n", sizeof(short)); printf("%d\n", sizeof(float)); printf("%d\n", sizeof(double)); printf("%d\n", sizeof(long)); printf("%d\n", sizeof(long long)); return 0; }
直接运行下面的代码会出现报错,这个函数或者变量可能会失效,考虑使用scanf_f来代替,如果让这个描述失效,使用#define _CRT_SECURE_NO_WARNINGS,此时代码就可以运行了
因为scanf_s是编译器vs提供的函数,并非C语言提供的库函数,当你的代码中使用了scanf_s函数在非vs中的编译器是不能够编译通过的,这样就降低了代码的可移植性
在C语言中ctrl+shift+\表示开始进行注释
为什么说scanf函数是不安全的呢?因为会有可能导致溢出,对应的数组就会溢出,会出现数组越界访问内存,也就是非法访问;
作用域和生命周期:
1)作用域是程序的设计概念,通常来说是指一段程序代码中的所使用的名字并不总是有效可用的,而限定这个变量的可用性的代码范围就是这个变量的作用域,局部变量的作用域就是变量所在的局部范围,而全局变量的作用域就是整个工程,这个变量在哪里使用,那里面
上面假设在add.c中创建了一个addTemp的变量,但是想要在test.c中进行访问,就要使用extern关键字来进行声明,还有一个项目里面只能有一个主函数;
生命周期:变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
局部变量的生命周期:是从进入到作用域的生命周期开始,出作用域的生命周期结束;
全局变量的生命周期:整个程序的生命周期
C语言中的常量:C语言的注释风格是不支持嵌套的
1)字面常量
2)const修饰的常变量
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> extern int addTemp; int test() { int temp = 10; printf("%d",temp); temp = 90; printf("%d", temp); } int main() { test(); //1.字面常量 int a = 100; char ch = "abcdef"; 3.14;//浮点型的字面常量 "cdefgh";//字符串常量 //2.const修饰的长变量,一旦const修饰了某一个变量,那么这个变量就具有了常属性,也就是说这个变量就具有了常属性,但是本质上还是一个变量,用到变量的时候仍然不能进行替换 const int tmp = 90; int array[10] = { 0 };//表示代码中有10个元素,初始化为全0,里面的10不可以替换成n return 0; }
3)#define修饰的标识符常量
4)枚举常量:枚举常量有着自己的值是不可以进行修改
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> //针对于枚举常量来说,枚举就是一一列举,枚举就是C语言中用来提供的一种自定义类型的方法 //性别,男,女,保密 //三原色:红黄蓝 enum Sex { //里面存放的是枚举类型的值,枚举大括号中的取值就被称之为是枚举常量 male, female, }; enum Color { red, green, blue }; int main() { printf("%d", red); printf("%d",green); printf("%d", blue); enum Sex sex1 = male; enum Sex sex2 = female; //最终打印的结果是0,1,2 }
在VS编译器中先要进行实现多行的复制,直接选中行进行ctrl+v,再按下回车键进行ctrl+v就可以实现多行的复制功能了;
进行调试观察值:
在C语言中,这种由双引号引起来的一串字符称之为是字符串字面值,或者简称为字符串,注意在C语言中字符串的结束标志是一个\0的转义字符,再进行计算字符串长度的时候\0是结束标志,不算做字符串的内容
![]()
转义字符:转义就是就是转变他的意思
常见的转义字符:
1)??)在C语言中如果想要通过printf进行打印是不可以的,三个字符会被整体的识别成?,所以为了防止这种情况地发生,只能在每一个问号前面加上一个\;
2)单独打印单引号是不可以的,所以要再单引号前面加还是那个一个斜杠,斜杠双引号也是可以的
3)extern表示是声明外部符号的
3)\\表示将后面的字符进行转义
4)\ddd表示三个表示八进制的数字,\xdd,表示两个16进制的数据
![]()
1)数组:一组相同类型的元素的集合
在C语言中,数据类型分为内置数据类型:char short int long long float double
2)在自定义类型中,struct,union,enum,数组
3)'0',字符0,ascill码值是48,'\0'也被称之为是转义字符,它的Ascill码值是0
4)EOF-end and file,本质上是-1,在C语言头文件里面,#define EOF -1
5)C语言 strlen 函数用来求字符串的长度(包含多少个字符)
strlen() 函数从字符串的开头位置依次向后计数,直到遇见\0
,然后返回计时器的值,最终统计的字符串长度不包括\0,下面的输出语句中\123算作是一个字符
6)在C语言中,如果一个全局变量不进行初始化的话,那么它的值默认就是0,如果一个局部变量不给他进行初始化的话,那么默认就是随机值
![]()
这种情况下进行数组的初始化,只有第一个元素被初始化,剩下的元素都是随机值
scanf函数表示的是最终成功读取了几个输入的值,如果没有读到任何的数字,那么直接返回-1,在遇到文件结束或者是遇到错误都会返回-1
双目操作符:有两个操作数,也是双目操作符,分为左操作数和右操作数
单目操作符:只有一个数字的操作符
在C语言中,0表示假,非0的数字表示是真
#include<string.h> int main() { int a = 0; int b = ++a;//这是前置++,主要意思就是先进行++再进行赋值 int c = a++;//这是后置++,主要意思就是先进行赋值,在进行++ printf("%d\n", a);//2 printf("%d\n", b);//1 printf("%d\n", c);//1 return 0; }
逗号表达式:逗号表达式是从左向右依次计算,整个表达式的结果是最后一个表达式的结果
typedef是类型重定义,下面的unsigned int和uint是等价的
register叫做寄存器关键字,寄存器是计算机中的一种存储器,是集成在CPU中,这个关键字是定义一个寄存器变量,这个定义的变量就被放到寄存器里面了,将来使用这个变量的时候,直接去寄存器中拿就可以了,就大大提高了这个变量的访问速度,否则如果你使用普通的变量进行访问,结果这个变量还是存放在内存里面的;
但是register只是起到一个建议的作用,建议将num的值存放到寄存器中,最终是由编译器决定的,寄存器变量也不能取地址,地址只是针对于内存来说的,而寄存器是集成于CPU中
static:静态的,static可以修饰局部变量,表示静态局部变量,改变了局部变量的存储类型,本来一个局部变量是存放在栈区的,被static修饰之后的局部变量是存储在静态区的,出了作用于之后,变量仍然没有销毁,存储在静态区的变量出了他的作用域之后变量也不会销毁,所以说生命周期比较长,进入到作用域也不重新创建,除了作用域之后也不会被销毁;
修饰全局变量表示静态全局变量,修饰函数表示静态函数
1)但是加上了static关键字之后,n在内存中就只有一份了,也就是这个test()函数中创建的变量是不会发生改变的,当函数结束的时候是不会被销毁的,当程序结束之后,static修饰的局部变量才会被销毁;
2)存在于栈上面的变量都是临时的,存在于栈区的变量也就是局部变量,局部变量进入到作用域创建,除了作用于之后就会被销毁,生命周期比较短;
3)当加上了static变量,这个变量就存在了静态区,它的生命周期是伴随于整个程序;
static修饰全局变量:
全局变量具有外部链接属性,局部变量没有外部链接属性
本来一个全局变量是具有外部链接属性的,但是当这个static全局变量变量被static修饰以后就变成了内部链接属性,这个时候static修饰的全局变量只能在本源文件.c中进行使用,在其他文件中无法被使用到,即使使用extern也无法使用;
如果不加static修饰的变量,如果所有的源文件都可以进行访问这个全局变量,那么这个全局变量的可控性是会发生改变的;
函数本身也是有动态连接属性的,但是加上static关键字修饰函数,那么这个函数只能在本源文件中进行使用,当static修饰函数和static修饰全局变量是类似的,一个函数本来也是具有外部链接属性的,当被static修饰的时候,外部链接也就变成了内部连接属性,这个函数也就只能在本源文件内部进行使用,其他文件就不可以进行使用了
static运用场景:当自己源文件修饰的函数或者是变量只希望在自己的源文件中使用,在其他源文件不能使用;
![]()
点击调试,窗口,内存,就可以看到对应的值在地址中的变化情况
void main() { int a = 10; int* p = &a; //1.这个p变量就被称之为是指针变量,这个变量里面存放的就是a的地址 //2.内存单元是有编号的(地址/指针/编号),&a是拿出a这个变量在内存中的地址 //3.然后把a的地址存放到p这个变量中 //4.int*表示这个变量存放的是整形类型的指针 printf("%d", *p);//结果是10 char ch = "w"; char* ch = &ch; }
&表示取地址符号
*又叫做解引用操作符,如果这个字符后面跟上一个指针变量,那么*pa表示的是
1)将pa指针变量中所存放的地址取出来
2)再根据取出来的pa变量的地址找到所指向的数据
指针变量是用来存放地址的一种变量
总结:
1)内存会被划分成小的内存单元,一个内存单元的大小是一个字节
2)每一个内存单元都会有编号,这个编号也被称之为地址或者指针
3)地址或者指针变量就被存放到一个变量中,这个变量也被称之为是指针变量
4)通过这个指针变量中所存储的地址,就可以找到指针指向的空间
指针变量是用来存放地址的,地址的存放需要多大空间,指针变量的存放就需要多大空间
在32位的机器上面,是支持32位虚拟地址空间,产生的地址就是32位,所以需要32位的空间存储,也就是4byte,此时指针变量的大小也是4byte
在64位的机器上面,是支持64位虚拟地址空间,产生的地址就是64位,所以需要64位的空间存储,也就是8byte,此时指针变量的大小也是8byte
struct Student { //创建了一个学生类型 int userID; char name[20]; int age; char sex[5]; double score; }; void main() { //1.创建一个结构体并进行赋值 struct Student student1 = {1,"张三",20,"男",90.3}; struct Student student2 = { 2,"李四",30,"女",100.9 }; //2.打印结构体相关的信息 printf("%d %s %d %s %lf\n", student1.userID, student1.name, student1.age, student1.sex, student1.score); //3.存放指针变量,访问结构体类型中的成员可以将指针变量进行解引用可以进行相应的访问, //或者还可以通过指针变量->的方式来进行访问 struct Student* ps = &student1;//存放的是struct Student类型的变量的地址 printf("%d %s\n", (*ps).age,(*ps).name); printf("%d %s", ps->age, ps->name); }
else和最近的if进行匹配,所以上面程序的运行结果是啥也不输出
swith中的case语句只是决定了程序的执行入口在哪里,从case所匹配的常量进行进入,而并不能决定程序具体要从哪里出去,所以要想出去,只能加上break语句,swith(整型常量表达式),case分支只有一条语句的时候是不需要加大括号的,但是如果有多条语句,就需要加上大括号了,当defalut语句放在最前面,如果不加break,那么defalut也会执行;
void main() { int day = 0; scanf("%d", &day); switch(day){ case 1: case 2: case 3: case 4: case 5: printf("工作日"); break; case 6: case 7: printf("周末"); break; default: printf("啥也不是"); } }
m=5,n=3
void main() { int ch = getchar();//这个函数是从标准输入中读取一个字符,最终返回值是这个字符对应的Ascill码值 //返回值是不能使用int来进行接收的,如果使用int进行接收,如果最终返回一个EOF也就是-1 //那么使用char类型是不可以进行接收的 putchar(ch); printf("%d", ch);//输入小写字母a,最终打印出来的整数值就是65 }
int ch = 0; while ((ch = getchar())!= EOF){ putchar(ch); }
从上图可知,代码直接运行失败了,当金星输入密码的时候,第一个getchar()的时候没有进行输入任何字符程序就直接运行失败了,因为:
1)scanf和getchar()函数是直接向缓冲区索要数据,当程序打印第一次输入密码的时候,程序员恰好输入了密码,此时scanf函数就会立即向缓冲区索要数据12345;
2)此时输入回车键,于是缓冲区中又有了一个字符叫做\n,此时getchar()函数直接就向缓冲区中索要数据,直接得到了字符"\n",程序继续向下执行,发现"\n"并不等同于"y",所以程序直接打印"密码确认失败",所以一个其中的解决方法就是提前消耗缓冲区中的一个字符,也就是getchar();
3)但是这样做又会出现新的问题,上面的方式只是适合处理缓冲区中只存在一个字符\n的情况,假设当输入123456 abc的时候,这个时候代码仍然是错误的
原因就是当你写出scanf("%s",&ch)的时候,这个scanf函数就会读取缓冲区里面的数据,读取到空格就结束了,所以当你输入123456 abc的时候,scanf函数只会读取到123456,但是abc\n仍然停留在缓冲区中,此时getchar()只能进行读取缓冲区中的第一个字符,也就是只能读取a,此时代码仍然会发生报错
所以说这个代码的最终的解决方案就是,在读取用户向缓冲区输入的字符之前要把用户缓冲区中的所有字符全部读取出来
void main() { char password[20] = { 0 }; printf("请输入密码:>"); scanf("%s", password); printf("请确认密码是否成功"); int temp = 0; while (temp = getchar() != '\n') { //循环读取缓冲区中\n以前的字符 } char ch = getchar(); if (ch == 'y') { printf("密码确认成功"); } else { printf("密码确认失败"); } }
如果将for循环的判断部分省略了,那么就意味着判断恒为真
上面代码实际上执行了多少次?
答:实际上执行了0次,因为k=0是一个结果为0的式子,如果变成k==0,那么代码只会执行一次