C语言的一大特点就是与内存强相关,C语言拥有三种不同的内存池。
1.静态区(static):全局变量,静态变量储存(生命周期是整个工程)
2.栈区(stack):局部变量存储(自动,连续的内存)
3.堆区(heap):动态存储(非常大的内存池,非连续分配)
因为static修饰的局部变量是存储在静态区的,static修饰全局变量时,实际改变的是变量的存储位置。 局部变量放在栈区的,被static修饰后放在了静态区。从而导致除了作用域依然存在,生命周期并没有结束。
比如说我再写一个代码:
void add()//我们不需要add函数返回,所以返回类型就是void { int a = 5;//每一次调用都要重新创建临时变量a,初始化的值是5 a++; printf("%d ", a);//这里打印add函数里面临时变量a的值 } #include <stdio.h> int main() { int i = 0; while (i < 10)//这里循环里面内容十次 { add();//这里是调用add函数 i++; } return 0; }
我们的输出结果是:
6 6 6 6 6 6 6 6 6 6
这次我们用static来修饰一下局部变量a:
void add()//我们不需要add函数返回,所以返回类型就是void { static int a = 5;//这次我们用static修饰了局部变量a a++; printf("%d ", a);//这里打印add函数里面临时变量a的值 } #include <stdio.h> int main() { int i = 0; while (i < 10)//这里循环里面内容十次 { add();//这里是调用add函数 i++; } return 0; }
输出结果如下:
6 7 8 9 10 11 12 13 14 15
这也能说明static修饰的局部变量a的储存位置被改变了,它的生命周期会一直到工程结束为止。
static最后的一个作用就是修饰函数
这里我就不用我的编译器份文件说了,直接注释标明
//代码1 //add.c int Add(int x, int y) { return c+y; } //test.c int main() { printf("%d\n", Add(2, 3)); return 0; } //代码2 //add.c static int Add(int x, int y) { return c+y; } //test.c int main() { printf("%d\n", Add(2, 3)); return 0; }
代码1正常,代码2在编译的时候会出现连接性错误。
其他关键字以后用到我会讲解。
define 定义常量和宏
这里先说一下,define是预处理指令,也就是说在编译的初阶段时,对于某些东西进行文本上的替换。
例如:
//define定义标识符常量 #define MAX 1000 //define定义宏 #define ADD(x, y) ((x)+(y)) #include <stdio.h> int main() { int sum = ADD(2, 3); printf("sum = %d\n", sum); sum = 10*ADD(2, 3); printf("sum = %d\n", sum); printf("%d\n",MAX); return 0; }
运行结果如下:
sum = 5
sum = 50
1000
对于ADD来说,x和y被替换成了2和3假如说没有那个括号的话,我们第二个结果就不一样了。对于MAX来说,它以后就代表1000这个常量了。
看这里!靓仔!标识符.
指针
它来了,它来了!
嗯,我们C语言最灵魂的地方——指针来了。
内存
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。
就相当于某人在广东省深圳市某区某街某小区某号楼某单元某层楼几号房一样!也就是说指向了你的这个位置。
所以说,地址就是指针。
变量是创建内存中的(在内存中分配空间的),每个内存单元都有地址,所以变量也是有地址的。
我们这里来说一下单位转换:
8bit=1byte(字节)
1kb=1024byte
1mb=1024kb
1gb=1024mb
1tb=1024gb
再说一下我们最常用的进制,二进制,八进制,十六进制,至于怎么互相转换的,小白看一下视频吧。
链接在这里进制转换
我们来看,一个字节等于8个bit位,一个bit位里面只能储存一个数字,因为我们计算机储存的本质正负电信号,也就是二进制0 1
至于后面为什么是0x什么什么的,这是因为0x表示十六进制
00000000000000000000000000000001
这个结果就等于1
二进制转换为十六进制
0x00000001
上面的视频最好一定要看哦。
二进制储存没有十六进制储存简洁明了,所以地址编号用十六进制表示!
我们了解了这么多,来取出一个地址打印看看!
#include <stdio.h> int main() { int num = 10; #//前面加一个&符号,这里是取出num的地址,不能是常量 //注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址) printf("%p\n", &num);//打印地址,%p是以地址的形式打印 return 0; }
打印结果(32位系统)
0012FF47
打印的结果每次都不一样,这是因为程序在运行的时候创建局部变量num当整个程序运行结束之后这个局部变量的生命周期也走到尽头了,每次创建变量都需要在栈区里随机的一个位置创建,所以每次的结果都不一样。
看到了吗,取出来的只是首个字节的地址,因为能通过首个地址找到后面三个字节的地址。
常量可以储存到变量里面,那地址如何存储?这需要定义指针变量。
int num = 10; int *p;//p为一个整形指针变量,如果int换成char就是字符类型,也就是说前面这个地方是决定指针类型的地方 p = #//将变量num储存到p这个指针里面
当 * 与一个变量结合时,说明这个变量是指针变量。
那么指针该如何使用呢?
#include <stdio.h> int main() { int num = 10; int *p = # *p = 20;//*这个操作符叫做解引用操作符,不仅仅是初始化可以用到,也是释放储存到指针变量的钥匙 return 0; }
解引用这个操作符你不要把他和初始化弄混, C语言定义指针的初始化就是这个样子,初始化前面是有类型定义的,而解引用并没有。
解引用是咋回事呢,指针的储存就相当于你把地址num这个东西放进了包裹p里,你想打开这个包裹,就需要解引用这个操作才能打开这个包裹。
然后通过num地址找到了里面的10这个元素。
至于为什么是指针变量,就拿p来说,它可以指向num的地址,也可以指向其他地址。
int a=30; p = &a;//这里不再储存变量num的地址而是变量a的地址
只要是数据储存在内存里就会有地址
就算是指针变量也一样。(这里先不套娃了,后期再说)
以整形指针举例,可以推广到其他类型,如:
#include <stdio.h> int main() { char ch = 'w'; char* pc = &ch; *pc = 'q'; printf("%c\n", ch); return 0; }
输出结果为
q
指针变量的大小
//指针变量的大小取决于地址的大小 //32位平台下地址是32个bit位(即4个字节) //64位平台下地址是64个bit位(即8个字节) int main() { printf("%d\n", sizeof(char *)); printf("%d\n", sizeof(short *)); printf("%d\n", sizeof(int *)); printf("%d\n", sizeof(double *)); return 0; }
输出结果为
4
4
4
4
为什么呢?不是说char类型是两个字节,short类型是两个字节吗?
其实这是指针的大小,我们上面说过了,所以不要在意指针变量前面的是什么类型,这个以后会说用处的,不要急。
结构体
结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。
比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息。
这里只能使用结构体来描述了。
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" }; //.为结构成员访问操作符,利用这个操作符访问s这个学生的个人信息 printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id); //->操作符,相当于指针变量ps解引用之后访问s这个学生的信息一样 struct Stu* ps = &s; printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps -> id); }
输出结果如下:
结构体以后会详细讲的,先了解这么多吧。
结论
操作符主要是介绍了一些简单的,注意平时积累,慢慢就会了,以后遇到没讲的我会详细讲解的。
用户自己是不能创造关键字
关键字static:
static修饰局部变量改变了变量的生命周期让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
预处理指令是在编译初阶更改文本!
注意自定义标识符的规则!
指针大小在32位平台是4个字节,64位平台是8个字节。
结构体是像数据类型的那种东西,我的理解是自定义的数据类型。
给家人们的留言!!!
家人们,抽丝剥茧C语言的初阶——初识C语言,到此就完结了, 目前我们已经了解了C语言大概是什么样子的,对于以后学习C语言更加方便。
我并不是C语言只讲了这么些,而是让大家熟悉下C语言,不过很重要!!!
请路过的同志们点点赞,互关一波!!!