初识C语言----完结篇

作者:学写代码的恐龙
博客主页:学写代码的恐龙博客主页
专栏:【初级c语言】
语录:❀未来的你,一定会感谢现在努力奋斗的自己❀
初识C语言----完结篇_第1张图片

初识C语言--完结篇

  • 十一:关键字
    • 11.1:register--寄存器
    • 11.2:typedef
    • 11.3:static--静态的
      • 11.3.1:static修饰局部变量
      • 11.3.2:static修饰的全局变量
      • 11.3.3:static修饰函数
  • 十二:#define定义常量和宏
    • 12.1:#define定义的标识符常量
    • 12.2:#define定义的宏
  • 十三:指针
    • 13.1:内存
    • 13.2:指针
    • 13.3:指针变量的大小
  • 十四:结构体
    • 14.1:如何定义一个结构体类型?
    • 14.2:如何创建一个结构体类型的变量?
    • 14.3:结构体列类型的初始化
    • 14.4:如何取到结构体的成员?

十一:关键字

11.1:register–寄存器

在计算机中我们可以把数据存放在网盘硬盘内存高速缓存寄存器里。
初识C语言----完结篇_第2张图片
他们之间结构成了上图的金字塔结构。越往金字塔的顶端,存储空间越小,访问速度也越来越快,这也导致他们的价格越来越贵。

在计算机中有一个叫CPU的东西,他也被叫做中央处理器,是用来处理数据的,所有的运算都是通过CPU来进行的。最早的时候CPU的运算速度不算快,内存的访问速度也还可以,CPU就会从内存里面拿取数据来进行运算。但随着硬件的发展,CPU的运行速度越来越快,而内存中数据的读取速度没怎么提升,这时尽管CPU的处理速度特别快,但读取数据的速度却显得很慢,这时就诞生了寄存器和高速缓存区,此时CPU直接从寄存器中读取数据,在CPU处理数据的同时,寄存器也会从高速缓存区读取即将要用到的数据,高速缓存区则从内存中读取即将要要用到的数据,这样他们同时工作,大大提升了计算机的工作效率。其中寄存器的单位是字节,我们一般都说有几个寄存器,它集成在CPU上。高速缓存又分为一级缓存,二级缓存,三级缓存等等。

register的作用就是建议编译器把创建的变量值放到寄存器里面,未来对变量的访问就是访问寄存器,从寄存器里面取值,当大量使用到此变量时,从寄存器里面拿效率就会高一些。但register的仅仅起到建议作用,最终是否把变量的值放到寄存器里完全取决于编译器。但现在的编译器都比较聪明,即使不加register他也会对程序进行分析,把一些值放到寄存器里面。

int  main()
{
   register  a  =  10;//仅仅是建议
   return 0;
}

11.2:typedef

type翻译为:类型,def是英文define的缩写翻译为:定义。顾名思义:typedef叫做类型定义。这里应该理解为类型重命名

当我们想定义一个无符号的整型变量是需要写unsigned int ,这显得非常的长,不方便,此时我们就可以通过typedef给unsigned int重新取一个名字来代替它。

typedef unsigned int uint;

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

此时我们把unsigned int重新命名为uint,这里的ab具有相同的数据类型

11.3:static–静态的

主要有三种用法:

  • 修饰局部变量
  • 修饰全局变量
  • 修饰函数

11.3.1:static修饰局部变量

void text()
{
	int a = 0;
	a++;
	printf("%d\n", a);
}
int main()
{
	int i = 0;
	while (i < 10)
	{
		text();
		i++;
	}
	return 0;
}

这里我么们写了一个循环,当i<10的时候调用text函数,可见一共调用了10次。每调用一次text函数就会打印一个值。
初识C语言----完结篇_第3张图片
从结果看出这里一共打印了10个1,那为什么呢?

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

text是一个函数,这里的大括号是一个局部范围,每次进入这个函数都会创建一个局部变量a,a的生命周期是进入这个局部范围时创建,出这个局部范围时销毁当 i=0 循环第一次调用text函数时,进入函数创建a,然后让a++,打印出a的值为1,出这个函数时a就销毁,然后让i++,i变成1,继续执行循环,调用text函数,由于上一次调用结束时,a已经被销毁,所以此时会重新创建一个a=0,然后a++,a变成1,打印出a的值,出text函数,销毁掉a。就这样重复循环十次,所以就会打印出10个1。

接下来我们在局部变量a的前面加上static进行修饰。


```c
void text()
{
	static int a = 0;
	a++;
	printf("%d\n", a);
}
int main()
{
	int i = 0;
	while (i < 10)
	{
		text();
		i++;
	}
	return 0;
}

此时打印出来的结果是1-10.
初识C语言----完结篇_第4张图片

大家可以这样来想原因,当我们第一次调用text函数的时候,起初定义a=0,a++后a变成1,打印出第一个数字1,当第二次调用text函数的时候,如果上一次的a销毁了,那此时重新创建一个a=0,然后a++,打印出来a的值还是1但实际却打印的是2,那说明分析的有问题,再看第二次调用text函数的时候执行a++了,结果是2,那说明a原来的值一定是1,那这个1是从哪里来的呢?如果第二次循环重新创建a了,那a就是0,所以这里a=1,说明第一次循环调用text函数结束的时候并没有把a销毁,而是继续保存a=1。因此,这里的a从第一次循环调用text函数时被创建,在每次函数调用结束时没有被销毁,这样a就会产生一个累加效果

这里本质的原因是,被static修饰后的局部变量会被存储在内存中的静态区。这里就需要扩展一点内存方面的知识。内存中被分了3块区域,分别是栈区堆区静态区栈区中一般是用来存储局部变量和函数的参数这类临时变量堆区里面是用来动态内存分配的,malloc、calloc、realloc、这些函数都是在堆区中申请的空间静态区存放的是静态变量和全局变量栈区的特点是,进入作用域创建,出了作用域就释放。里面存放的变量都是临时的。静态区里面的数据创建好后直到程序结束才销毁。

注意:被static修饰是不会影响作用域的。但是生命周期发生了变化,变长了。
初识C语言----完结篇_第5张图片
总结一下,当我们希望保留变量上一次的值,这时我们就可以用static来修饰这个变量

11.3.2:static修饰的全局变量

在之前介绍全局变量作用域的时候给大家举了个例子,就是我们可以用extern来声明一个外部变量,这样我们就可以在A这个源文件中用同一个工程下B这个源文件中定义的全局变量了。
初识C语言----完结篇_第6张图片

如上图,我们在add.c中定义了一个全局变量a=1,此时我们要是想在text.c中用a的值,这需要在text.c中用extern声明一下这个外部变量a就可以。

如果我们在add.c中的“int a = 1;”前面加上一个static会发生什么结果呢?

初识C语言----完结篇_第7张图片
可以看出,此时程序报错了“无法解析的外部符号_a”,也就是程序不认识这个符号。为什么会出现这个原因呢?

原因是:全局变量本身是具有外部链接属性的,也就是在同一个工程下的a源文件中定义的全局变量在b源文件中可以通过链接使用。但是如果全局变量被ststic修饰,这个外部连接属性就变成了内部连接属性,这个全局变量只能在自己所在的源文件内使用。static的修饰会把全局变量的外部链接属性变成内部连接属性。最终使得全局变量的作用域变小。(一个工程最终只能编出一个可执行程序,一个text.c可以通过 编译 + 链接 生成一个可执行程序----- .exe。链接这个阶段回去其他的文件找我们想要的符号。(注意:局部变量没有链接属性)

11.3.3:static修饰函数

我们在B源文件中定义了一个函数,在A中直接调用这个函数程序会发出警告。如下图,在add.c源文件中定义了一个函数add在text.c中调用这个函数,程序报警说:add未定义。此时我们可以像声明一个外部变量那样,用extern来声明一个外部函数。(每一个编译器都是对.c文件进行单独编译的)
初识C语言----完结篇_第8张图片

修改后的代码为:
初识C语言----完结篇_第9张图片

此时编译器没有报警告。

当我们在add函数的前面加上一个ststic后会发生什么呢?
初识C语言----完结篇_第10张图片
是不是很熟悉,这结果和上面static修饰全局变量的结果是一样的。

函数本身是具有外部链接属性的,被static修饰后,外部链接属性变成了内部链接属性,使得这个函数只能在自己的这个源文件里使用,其他源文件无法使用。

十二:#define定义常量和宏

12.1:#define定义的标识符常量

初识C语言----完结篇_第11张图片

#define 定义的一个标识符常量,这里的M是一个符号不是变量,但是是全局的。
(注意**:箭头指向的语句后边千万不能加分号**。我在这里就老出错)

12.2:#define定义的宏

#define Max(x,y)  (x>y?x:y)

int main()
{
	int a = 10;
	int b = 20;
	int c = Max(a, b);
	printf("%d\n", c);
	return 0;
}

代码第一行就是定义了一个宏,其中Max叫做宏的名字(x,y)叫做宏的参数(x>y?x:y)叫做宏体。它的用法和函数一样。

宏是如何工作的呢?

当编译器检查到main里面的Max的时候,发现Max是一个宏,此**时Max(a,b)会被宏体替换。**如下:

int c = Max(a, b);
int c = (a > b ? a : b);//上面的语句被替换成这个

宏和函数的区别:

函数参数有类型参数没有类型,宏用来处理逻辑简单的结构,而函数用来处理逻辑复杂的结构。

十三:指针

13.1:内存

内存是计算机上的一种存储空间,像我们平时使用的电脑,有8G的和16G的,程序运行的时候会载入内存,程序中如果有数据需要存储也会申请内存空间。

初识C语言----完结篇_第12张图片
内存是如何使用的呢?

为了有效的使用内存,就把内存划分成一个个小的内存单元每个内存单元的大小是1个字节。为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址
初识C语言----完结篇_第13张图片

进制:(只是数值的表现形式)
初识C语言----完结篇_第14张图片

16进制中最大的数字15可以由2进制的1111来表示,所以2进制转16进制时,就是4个2进制位转化成1个16进制位

13.2:指针

如何取地址:

&就是取地址符,%p是专门打印地址的。

初识C语言----完结篇_第15张图片

在定义a的时候,就向系统申请了连续的4个字节用来存储a,但取a的地址打印的时候只取了a所占4个字节中的第一个字节的地址

初识C语言----完结篇_第16张图片

左边框起来的是内存的地址,因为地址是由16进制数字表示出来的,所以是0x开头的(0开头的数字是8进制)。右边框起来的是内存的数据,红色圆圈圈起来的是一个字节,1个字节等于8位bit,也就是一个字节由8个2进制位,4个2进制位表示一个16进制位,所以一个字节由两个16进制位组成。

地址的用处:

可以先把地址存起来,有朝一日通过这个地址找到你,地址只不过是一个16进制的数字

int main()
{
	int a = 1;
	int* pa = &a;
	printf("%p\n", &a);
	printf("%p\n", pa);
	return 0;
}

其中pa是一个变量这个变量用来存放地址,而地址又叫指针所以C语言中把pa叫指针变量(指针变量是存放指针的变量),pa变量的类型是int*型,取出a变量申请的4个字节中的第一个字节的地址,把他给pa。&a和pa的值一样。

初识C语言----完结篇_第17张图片

初识C语言----完结篇_第18张图片

解引用操作:

*pa//通过pa中的地址找到a,*pa就是a
int main()
{
	int a = 1;
	int* pa = &a;
	*pa = 20//*pa就是a,就相当于---a = 20;
	printf("%d\n", a);//此时打印出来的a的值是20
	printf("%p\n", &a);
	printf("%p\n", pa);
	return 0;
}

指针就是地址
口语中指针一般指的是指针变量

13.3:指针变量的大小

初识C语言----完结篇_第19张图片

x86是32位机,此时两个指针变量的大小都是4个字节

为什么32位机上指针变量的大小都是4个字节?

原因:指针变量是用来存放地址的,32位机器地址由32个bit位组成,64位机器上地址是由64个bit位组成的。所以32位机器上指针变量存放的是32bit的地址,64位机器上指针变量存放的是64bit的地址,而32个bit位就是4个字节(1个字节8个bit位)。所以32位机器上,指针变量的大小是4个字节。同理,64位机器上,指针变量的大小是8个字节。

初识C语言----完结篇_第20张图片

x64为64位机器,此时两个指针变量的大小都是8个字节。(其中%zd打印的是无符号整型,这里也可以用%zu。sizeof返回的是一个无符号数字,%u打印32位机下的无符号整型)

十四:结构体

  • char
  • short
  • int
  • long
  • long long
  • float
  • double

以上这些都是C语言的内置类型

当我们想描述一本书的时候,书有书名、作者、出版社、定价等信息,描述一个人的时候有姓名、性别、身高、体重、年龄等信息。可以发现书和人都是一个复杂对象,我们很难只用一个上面提到的C语言内置类型来描述清楚,因此我们希望高出一种复合类型,可以把书的各种信息都包含进去,这时C语言就提供了一些自定义的类型,用户可以自己定义了类型。

自定义类型:

  • 结构体
  • 枚举
  • 联合体

结构体的关键字是struct,他就表示结构体

14.1:如何定义一个结构体类型?

假设我们想描述一个学生,学生包含:姓名、性别、年龄。三个信息。下面代码就定义了一个学生类型:

struct Stu
	{
		char name[20];//用来存放姓名
		char sex[5];//用来存放性别
		int age;//用来存放年龄
	};

其中定义的namesexage叫做结构体的成员

14.2:如何创建一个结构体类型的变量?

和创建整型变量、字符型变量、浮点型变量等的方法一样,我们需要有它的类型变量名,如下面代码中,学生类型就用struct Stu 来表示,变量名是s

struct Stu
{
	char name[20];
	char sex[5];
	int age;
};


int main()
{
	
	struct Stu s;

	return 0;
}

14.3:结构体列类型的初始化

struct Stu s = {"zhangsan","男",20};

此时我们就初始化了一个学生变量s,它的名字叫zhangsan,性别是男,年龄为20岁。

14.4:如何取到结构体的成员?

s.name//取名字
s.sex//取性别
s.age//取年龄

具体实例:打印出s的姓名、性别、年龄

初识C语言----完结篇_第21张图片

可见如果要得到结构体里的成员用 . 这个操作符就可以。结构体变量. 成员名,就可以

假如我们有了结构体变量s的地址,我们还可以用下面这两种方法来得到结构体里的成员

struct Stu* ps = &s;//这里定义了一个结构体类型的指针变量ps,用来存放结构体类型变量s的地址
(*ps).name,(*ps).sex, (*ps).age//方法一:通过指针变量的解引用操作*ps就等于s,然后再用.操作符来取到结构体中的成员
ps->name,ps->sex,ps->age//方法二:用->来得到结构体成员

实例:
初识C语言----完结篇_第22张图片

结构体的指针->成员名

初识C语言的分享到这里就正式结束啦!

今天就分享到这里啦,喜欢的话可以点赞、评论和收藏哟!
初识C语言----完结篇_第23张图片

你可能感兴趣的:(初级C语言,c语言,开发语言)