【C语言】关键字、指针、结构体

193 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情

C语言提供的关键字:1.C语言提供的,不能自己创建关键字      2.关键字不能做变量名

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16​编辑

常见关键字

auto   break  case  char  const  continue  default  do  double  else  enum   extern  float  for  goto  if  int   long  register  return  short   signed  sizeof  static  struct  switch  typedef  union  unsigned  void  volatile  while  

简单了解一下这些关键字。

auto是自动的,每个局部变量都是auto修饰的。

创建局部变量a,看到在第六行a进到大括号开始创建,出第八行销毁。所以对a来说是自动创建,自动销毁的。所以它是自动变量,在局部变量的前面本身都要带auto。因为局部变量都是带auto的,所以把auto省略掉了。auto在新的C语言语法中有其他的用法,暂时不考虑。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16

关键字do在do......while语句中讲过,enum在枚举常量中讲到过,char,double,float,int,long,short在讲数据类型中提到过,这里不做详细介绍。

extern是用来申明外部符号的。

for循环,goto语句,if语句

register叫寄存器关键字,这个关键字创建的变量建议存放在寄存器中。

那么为什么要把数据存放在寄存器中?我们来了解一下。

计算机中数据可以存储到哪里呢?

数据可以存放在网盘、硬盘、内存、高速缓存、寄存器中。 

网盘因为依赖于网络,所以传输速度慢,而硬盘的传输速度比网盘要快,内存读写数据比硬盘更快所以它的造价也就更高,技术含量也更高。那再往上,高速缓存比内存的传输速度更快,寄存器又要比高速缓存快。那这个时候在计算机中就形成了一种存储的类似金字塔的结构。越往上,造价越高,速度越快但是空间越小。像大量的,频繁的被使用的数据,想放在寄存器中,就可以提高效率。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16

关键字signed是有符号的,像10,-20都是有符号的数字,与之相对应的是关键字unsigned无符号的,它定义的某些关键字是没有符号的概念的。

sizeof求大小,static静态的,后面详细讲。struct结构器,switch语句,typedef类型定义,union联合体或共用体,void无或者空的意思,while循环。我们要对哪些是关键字哪些不是有印象。

那像define,include是不是关键字呢?不是,它们是预处理器并不是关键字。

接下来详细介绍几个关键字。

关键字typedef

类型重定义,如下图,我们定义num变量的类型为unsigned int写起来不方便,能不能把这个类型简化一下,就要用到typedef关键字,我们重定义u_int为unsigned int,这样只要写u_int定义就和unsigned int的效果是一样的。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16

关键字static - 静态的

1.static修饰局部变量

2.static修饰全局变量

3.static修饰函数

static修饰局部变量

如下图一,我们用while循环,创建一个变量i,让它循环10次,每次循环都调用一个test函数,在函数里面创建局部变量a,打印变量a的值。那我们的打印结果会是什么呢?我们打印一下,看到结果如下图二,打印结果为十个二。我们来分析一下,首先代码从主函数第一行先走,到while语句中,然后到调用函数test中,进到函数里面,a是局部变量,局部变量的特点是进入到其范围里面a创建,出了范围,a自动销毁,所以这里a++就是2。然后a出来自动销毁,下一次主函数重新走一遍,然后代码走到test函数中再次新建一个a,a++为2,一直这样循环十次,打印出来的就是十个2。那我们再用static修饰局部变量a。我们可以看到如下图四的打印结果是211,这次为什么得到的是211。代码重新走起来,先从主函数进入到test函数中,static修饰局部变量a,使得a出去之后不会销毁。那么这一句static int a=1;再下一次就没有作用了,就是a++,然后下一次进入循环的就是a++之后的值,以此类推,就得到打印结果为2~10。可以看出static修饰之后局部变量出范围之后不销毁,使得局部变量的生命周期变长。我们就可以总结出static修饰局部变量,改变了局部变量的生命周期(本质上是改变了变量的存储类型)。

内存在学习计算机语言中,会把内存分为几个区域,分别为栈区,堆区,静态区。栈区里面存的是局部变量,函数的参数,只要是局部的,临时的变量都存放在栈区中。堆区是用来动态内存分配的。静态区存两种值,一种是全局变量,另一种是static修饰的静态变量。

那我们局部变量a一开始是放在栈区的,当被static修饰时就放在了静态区。所以a本质上就是从栈区变成了静态区,改变了存储类型。因为存在不同区域内,数据就有不同特点,静态区我们可以看到全局变量的生命周期非常长,只要程序不结束,变量就不会销毁。静态变量和全局变量的生命周期都是一样的。

图一

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16​  图二

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16​  图三

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16​ 图四

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16

static修饰全局变量

再创建一个源文件,再一个源文件中创建一个全局变量,如下图一,在另一个源文件中打印全局变量,用extern声明就可以打印。extern是用来声明外部符号的,只要声明名字和类型。那我们加上static修饰全局变量,如图二打印结果会报错。无法解析外部符号,说明static修饰全局变量,使得这个全局变量只能在自己所在的源文件(.c文件)中使用,其他文件不能使用。

全局变量,在其他源文件内部可以使用,是因为全局变量具有外部链接属性,但是被static修饰之后,就变成了内部链接属性,其他源文件就不能链接到这个静态的全局变量了。

图一

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16​图二

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16static修饰函数

static修饰函数和static修饰全局变量是类似的,如图一创建一个Add函数在一个源文件中,另一个源文件引用该函数,用extern声明,发现是可以打印的。当Add函数被static修饰,就会发现无法解析,如下图二。

static修饰函数,使得函数只能在自己所在的文件内部使用,不能在其他源文件内部使用。

本质上:static是将函数的外部链接属性变成了内部链接属性。(和static修饰全局变量一样)。

图一

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16​编辑图二

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16​编辑 #define定义常量和宏

define是一个预处理指令。

1.define定义标识符常量

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16

2.define定义宏

用define定义一个宏Add,如图一,用2和3分别对应x和y,那加起来一打印可以看到打印结果为5。那这个时候我们在Add(2,3)前面乘以4,打印结果是20吗?如下图二我们可以看到打印结果为11,因为宏是可以替换的,在这里替换成了42+3就是11,而不是45。那我们要怎么得到20这个结果呢?宏在书写的时候不应该把x和y想成普通的变量,而应该当成表达式来写。所以我们要把它们都括起来。如下图三的写法。这里宏就不再继续详细讲了。

图一

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16

图二

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16

图三

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSb6Jav,size_20,color_FFFFFF,t_70,g_se,x_16

 下面我们来了解一下C语言的一个学习重点——指针

要讲指针就必须先了解内存的概念。

内存

内存是电脑上特别重要的储存器,计算机中所有程序的运行都是在内存中进行的。就相当于一个储存空间,和现实生活中对于空间的使用,两者是十分相似的。

现实生活中当我们要访问一个空间的时候,给每一个地方都编了一个有效的地址,而地址又跟一个个房间是关联的,我们给每一个房间都是编号的。那么对于内存也是一样的,内存就相当于一块大的空间。那这里就会出现两个问题,1.内存是怎么编号的?2.一个内存单元是多大空间?

在生活中,我们讨论一个计算机是32位,64位机器,那这些32位,64位是什么意思。32位机器有32根地址线,地址线是物理线,它可以通电,通电的时候它的正电就是1,负电就为0。有正负电之分就产生了1/0这样的信号。我们把电信号转换为数字信号,这个时候就组成了由1和0组成的二进制序列。那32根地址线产生的二进制序列就有32个全0到32个全1,2^32个二进制序列。当一个这样的二进制序列成为一个内存编号的时候,我们就把这个编号称为这个内存的单元的地址。

为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为内存单元的地址。

所以为了有效的使用内存,就把内存划分成一个个的内存单元,每个内存单元的大小是一个字节(byte)。

如下图,创建一个变量a,a在内存中要分配空间,就是4个字节,怎么看内存的情况呢?我们按f10调试以下,调试->窗口->内存,在地址栏里输入&a(取地址a),我们再打开监视窗口。输入&a,可以看到a取的是它的起始地址。

 打印&a,%p是专门用来打印地址的。这个&a拿出来也是一个值,那这个值要存起来。如下图二,pa就是用来存放&a的,在C语言中把pa叫做指针变量。pa是指针类型,它就应该写成int*。对于int*是怎么理解的呢?*说明pa是指针变量,而前面的int说明pa指向的对象是int类型的。由此,我们也可以得到图三创建char变量指针变量类型。

图一

图二

 图三

当我们能够拿到a的地址时,不仅仅是因为要把它存起来,而是想通过这个地址找到a。所以怎么找呢?如下图,*是解引用操作符,是个单目操作符。可以看到我们在找pa的地址同时也改变了a的地址。其实指针就是地址。pa是指针变量,它里面存的是地址,所以在使用这个指针变量的时候,就是使用存在它里面的地址。

 指针变量的大小

如图所示,指针的大小是相同的。为什么呢?指针是用来存放地址的,那么指针需要多大空间,取决于地址的存储需要多大空间。32位机器就是32个bit位,也就是4个byte。64位机器是64bit就是8个byte。那我们这里看到的4,是因为我们是32位平台。我们改成x64看看打印结果,如下图二所示。结论就是:指针大小在32位平台是4个字节,64位平台是8个字节。

图一

 图二

结构体

结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂对象

比如描述学生,学生包含:名字+年龄+性别+学号这几项信息。这里只能使用结构体来描述了。

结构体可以让C语言创建出新的类型出来。如下图一,创建一个学生类型的变量。创建一个结构体变量s并赋值,这样就是结构体的初始化。打印结果可以用结构体变量.成员变量。也可以如下图二所示,创建一个指针变量ps对s取地址。*ps解引用操作就等于变量s,就可以用第二种方法。也可以用->操作符,结构体指针->成员变量名的方式。

图一

 图二

写在最后🍎:码字不易,如果对你有帮助的话,给个三连或者关注一下吧🍰,感谢支持!📣