在这里我们接着上一次的内容来介绍一些关键字以及#define定义、指针、结构体
目录
一、关键字static
1.修饰局部变量
2.修饰全局变量
3.修饰函数
二、#define定义常量和宏
#define定义标识符常量
#define定义宏
三、指针
内存
指针
指针变量的大小
结构体
一、关键字static
在c语言中:
static是用来修饰变量和函数的
- 修饰局部变量---称为静态局部变量
- 修饰全局变量---称为静态全局变量
- 修饰函数---称为静态函数
我们先用两个代码来比较一下!
#includevoid text() { int a = 3; a++; printf("%d ", a); } int main() { int i = 0; while (i < 10) { text(); i++; } return 0; }
运行结果如下:
在这里a是局部变量,在进入作用域范围,生命周期开始,出了作用域生命周期结束,销毁,所以在这里编译结果都是4。
下面再看使用static修饰a这个局部变量编译的结果:
#includevoid text() { static int a = 3; a++; printf("%d ", a); } int main() { int i = 0; while (i < 10) { text(); i++; } return 0; }
此时我们发现运行结果改变了,具体是什么原因呢,接下来我们详细的介绍;
首先我们了解一下栈区、堆区以及静态区的内容:
对比两个代码结果,我们可以知道,局部变量a用static修饰之后增长了生命周期,在出了局部范围后,没有被销毁。
由此我们来解释一下:
普通的局部变量是放在内存的栈区上的,进入局部范围,变量创建,出了局部范围,变量销毁。
当static修饰局部变量时,局部变量是在静态区开辟的,这时的局部变量出了作用域变量不销毁,下次进入作用域使用的是上一次遗留的数据。(改变了存储位置,由栈区→静态区,使得变量的生命周期发生了变化)
- static修饰局部变量改变了变量的生命周期。
- 让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
首先我们要知道,一个全局变量是具有外部链接属性的,在一个工程的其他文件定义,在本文件依然能够使用,如果全局变量被static修饰,外部链接属性就变为了内部链接属性,其他的源文件就无法再通过连接找到这个变量,static修饰的全局变量只能在自己所在的.c文件内部使用。
下面我们使用代码来说明:
我们可以发现当使用static修饰全局变量g_时,程序无法运行。
结论:
- 一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在本工程中其他源文件内使用。
static修饰函数与修饰全局变量相似,都是将其外部链接属性变为内部链接属性。
函数是具有外部链接属性的,其他源文件想使用某函数,只需要正确的声明就可以,但是函数被static修饰时,外部链接属性就变为了内部链接属性,此时函数只能在自己所在的.c文件内部使用,不能在外部使用。下面我们来通过代码展现:
我们可以发现当使用static修饰函数时,程序运行不通过,由此证实。
总结:
- 一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
二、#define定义常量和宏
#define定义标识符常量#define是一个预处理指令
例如:
#define MAX 1000
#define STR “abcdef”
此处的MAX
#define定义宏宏是有参数的,我们可以通过定义宏来实现自己想要的功能。
注意:宏的名字一般是大写字母组成。
例如:
#define MAX(x,y) ((x)>(y)?(x):(y)) //此处是条件操作符
//宏的目的是求x,y中的最大值
我们来简单分析一下
#include//define定义宏 #define ADD(x,y) ((x)+(y)) //定义一个宏,宏的功能是完成两个数的相加 //此处x,y加括号原因我们可以通过举例说明 //如果在主函数中是ADD(a, b + r); //那么通过宏来实现功能的时候如果不加括号会造成混淆 int main() { int sum = ADD(2, 3); printf("sum=%dn", sum); sum = 10 * ADD(2, 3); printf("sum=%dn", sum); return 0; }
我们发现函数与宏有大同小异之处。
上述宏实现的功能可以通过以下函数实现:
#includeint ADD(int x, int y) { return x + y; } int main() { int sum = ADD(2, 3); printf("sum=%dn", sum); sum = 10 * ADD(2, 3); printf("sum=%dn", sum); return 0; }
注意:函数的参数是传参的,
宏的参数是替换进去的。
三、指针 内存内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。
为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址(我们把地址也叫做指针)。
编号 = 地址 = 指针
指针变量创建是在内存中的(在内存中分配空间的),每个内存单元都有地址,所以变量也是有地址的。
在这里我们需要知道&取地址符号
&num; //指的就是取出num的地址
#includeint main() { int num = 10; # //取出num的地址 //注意:这里num的4个字节,每个字节都有地址(编号),取出的是第一个字节的地址(较小的地址) printf("%pn", &num); //打印地址,%p是以地址的形式打印 return 0; }
在上面有了地址这一概念,那么这么想也应该有一个存储地址的变量,叫做指针变量。
指针变量的类型是什么?我们来用一段代码来解释
#includeint main() { int a = 10; //4个字节 int* pa = &a; //pa是创建出来存放地址 return 0; }
在这里int *pa = &a;
int * 整体是一个类型,整型指针类型;
* 说明pa是指针变量;
int 说明pa指向的对象是int类型的;
(pa(地址)中存放的对象,即此题中pa指向的对象为10(int类型))
*pa; //*是解引用操作符,*pa就是通过pa中存放的地址找到pa指向的空间;
总结:
- 指针其实就是地址,地址就是内存单元的编号
- 把地址进行存储的时候,就可以放到一个变量中,这个变量就是指针变量
- 口头语中说的指针,一般指的是指针变量
指针变量的大小是多少呢?
指针变量是用来存储地址的!所以指针变量的大小取决于存储一个地址需要多大的空间!
在这里,我们需要知道电脑是硬件,硬件就要通电,其实在电脑上有一组地址线(32 / 64根)
32位环境下有32根地址线;
64位环境下有64根地址线。
电线一旦通电,电信号就会转换为数字信号0/1(高低频),32根地址线时,如果32根地址线全部通电,那么会存在 000000000000000000000000000000000
000000000000000000000000000000001
000000000000000000000000000000010
......
01111111111111111111111111111111111111
.......
11111111111111111111111111111111111111
我们把一次产生的当做一个地址。32个0/1组成的地址序列,作为一个地址存储需要32bit的空间来存储。(4byte)32根地址线会产生2^32种地址所以在32位环境下指针变量的大小需要4个字节。
在64位环境,地址序列就是64个0/1组成的二进制序列,需要64bit---->8byte ,指针变量的大小需要8个字节的空间来存储。
补充:
sizeof计算的结果是无符号整数的,对应的打印格式应该是%zu(%d也可以但最好用%zu)
vs编译器下 x86对应32位环境
x64对应64位环境
结构体
结构体使得c语言有能力描述复杂类型。
比如描述,学生是复杂对象,包括名字+年龄+性别+学号,所以学生不能简单的使用int,float类型来描述因此c语言中就引入了结构体的概念,自己定义一个结构体类型来表达某一事物.
struct是结构体关键字。
结构体的语法如下:
在上例中的学生使用结构体类型定义如下:
struct stu
{
char name[20]; //名字
int age; //年龄
char sex[5]; //性别
char id[15]; //学号
};
在定义好结构体之后我们就可以使用上述自己定义的结构体类型创建变量并且进行初始化
struct stu s;//用上述自己定义的结构体类型创建变量
struct stu s1 = {"zhangsan",30,95.5f}
下面我们来介绍如何访问结构体的成员
- 结构体指针变量->结构体成员
- 结构体变量.结构体成员
使用代码进行简单解释:
#includestruct stu { char name[20]; //名字 int age; //年龄 char sex[5]; //性别 char id[15]; //学号 }; void print(struct stu* ps) { //此时传过来的是s2的地址在这种情况下使用结构体指针->结构体成员的形式 printf("%sn", ps->name); //也可以使用*(结构体指针).结构体成员 printf("%sn", (*ps).name); } int main() { struct stu s1 = { "zhangsan",20,"men",12 }; struct stu s2 = { "zhangsa",21,"men",13 }; //结构体变量.结构体成员 printf("%sn", s1.name); print(&s2); return 0; }



