文章目录
本节重点
- 常见关键字
- define定义常量和宏
- 指针
- 结构体
常见关键字
C语言提供的关键字
特点:1.是C语言提供的,不能自己随意创造关键字
2.关键字不能做变量名
eg: int char; //error,char是关键字,不能做变量名
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:是自动的意思,每个局部变量都是auto修饰的
int main()
{
int a = 10;//局部变量是自动创建,自动销毁的,所以也叫自动变量,这里的auto省略掉啦
return 0;
}
关键字register
register寄存器关键字,用它创建的变量,建议放到寄存器里
#include <stdio.h>
int main()
{
register int num = 10;//前面有个register表示建议num的值存放到寄存器中,
//但也只是建议,它不做最决定,是否要放要由编译器最决定
return 0;
}
代码注解:
- register int num = 10;:前面有个register表示建议num的值存放到寄存器中,但也只是建议,它不做最决定,是否要放要由编译器最决定
- 疑问:为什么要放到寄存器里呢?
答:大量/频繁被使用的数据,想放在寄存器中提升效率,就会建议放在寄存器里面去。(随着编译器的发展,编译器已经会自动判断,就算不建议编译器也会做出最佳的提升效率的方法,因此,这个关键字的存在意义也不是特别大了。) - 拓展:计算机中数据可以存储到哪里呢?(如下图)
其他关键字
- signed:表示有符号的,即描述的数字或定义的某个变量是有正负的
- unsigned :无符号的,就是在它看来你定义的某个变量是没有符号位这个概念的。
- static:静态的
- union:联合体(共用体)
- void :无(空)
- volatile :C语言暂时不讲,它是能够体现你C语言段位的关键字,也就是说,如果你能将这个关键字理解的不错,说明你的C语言学的也很好,段位很高,如果不懂,说明你的C语言有待进一步提高。
- 疑问define:是不是关键字呢? 不是,它是预处理指令(预编译期间处理的指令叫预处理指令)
- 疑问include:那include呢? 不是,它是预处理指令
关键字typedef
typedef:叫类型重定义,这里可以理解为类型重命名。
typedef unsigned int u_int;//这里就是将unsigned int 重命名为u_int(别名),所以u_int也是一个类型名。它可以把一个复杂类型简单化,便于书写
int main()
{
unsigned int num = 100;
u_int num = 100;//类型重命名后就使这一句和上一句所表达的意思是完全一样了。
return 0;
}
关键字static
static:静态的,它有三种用法
- 1.static修饰局部变量
- 2.static修饰全局变量
- 3.static 修饰函数
1.static修饰局部变量
void test()
{
static int a = 1;//static修饰局部变量,它修饰a会使a在出它的运行范围之后不销毁,再次进来的时候不需要重新创建(延长了生命周期)
a++;
printf("%d ", a);//运行结果:2 3 4 5 6 7 8 9 10 11
//分析:如下图
//static修饰局部变量,改变了局部变量的生命周期(实际上是上改变了变量的存储类型,致使它改变了局部变量的生命周期.
int a = 1;//a是局部变量,它在出它的运行范围之后会销毁,再次进来之后需要重新创建(生命周期到期)
a++;
printf("%d ", a);//运行结果:2 2 2 2 2 2 2 2 2 2
//分析:如下图
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
全局变量
#include <stdio.h>
//全局变量在整个工程中都可以使用
//要想让test.c知道我在add.c中创建的变量并打印结果需要声明一下,
extern int g_val;//声明的时候只需要声明类型和名字,不需要声明值,通常还要加上extern。
//extern :用来声明外部符号的
int main()
{
printf("%d\n", g_val);//打印add.c中定义的全局变量,结果为2021
return 0;
}
2.static修饰全局变量
extern int g_val;
int main()
{
printf("%d\n", g_val);//打印add.c中定义的全局变量,结果报错:无法解析的外部符号
return 0;
}
报错原因:
- add.c文件中,当被static修饰后,即使声明了,也无法被识别
- 报错总结:static修饰全局变量,使得这个全局变量只能在自己所在的源文件(.c文件)内部可以使用,其他源文件不能使用。
- 疑问:static修饰的全局变量,是否改变了全局变量的作用域(作用域变小)?
答:不是,全局变量在其他源文件内部可以使用,是因为全局变量具有外部链接属性,但是被static修饰之后,就变成了内部链接属性,其他源文件就不能链接到这个静态的全局变量了,因此其他源文件不能使用。
函数
//要想让test.c知道我在add.c中创建的变量并打印结果需要声明一下,
extern int Add(int x, int y);//声明的时候只需要声明函数类型和函数名字,参数类型是什么,不需要声明值,通常还要加上extern。
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int sum = Add(a, b);
printf("sum=%d\n", sum);// 打印add.c中定义的函数,结果为sum=30;
return 0;
}
3.static修饰函数
#include <stdio.h>
extern int Add(int x, int y);
int main()
{
int a = 10;
int b = 20;
int sum = Add(a, b);
printf("sum=%d\n", sum);//打印add.c中定义的函数,结果报错:无法解析的外部符号
return 0;
}
报错原因:
- add.c文件中,函数被static修饰后,即使声明了,也无法被识别
- 报错总结:static修饰函数,使得这个函数只能在自己所在的源文件(.c文件)内部可以使用,其他源文件不能使用。本质上static是将函数的外部链接属性变成了内部连接属性(与static修饰全局变量一样)
define定义常量和宏
define是一个预处理指令
1.define定义符号(常量)
#include <stdio.h>
#define MAX 1000 //MAX:标识符常量
int main()
{
printf("%d\n", MAX);//打印结果:1000
return 0;
}
2.define定义宏
#include <stdio.h>
#define ADD(X,Y) X+Y //定义一个符号ADD设置两个参数,并使两个数相加
int main()
{
//printf("%d\n", ADD(2,3));//打印结果:5
printf("%d\n", 4*ADD(2, 3));//打印结果是否为20呢?错,结果为11
//想要得到20 ,需要将X+Y写成((X)+(Y))才可,因为,宏是有参数的,宏的参数是替换的。
return 0;
}
指针
指针引入
思考:
1.内存是怎么编号的?
答:1.地址线通电产生的电信号转化成数字信号对应的这个二进制序列就是我们的地址,编号就被称作地址。每一个内存单元都给一个地址,就产生了地址的编号,即根据一个字节为一个内存单元进行地址分配并进行编号(一个字节给一个编号)的。
2.一个这样的内存单元是多大空间?
答:从1知我们有2的32次方个地址,那我们一个内存单元给(管理)多少空间呢?
我们可以管理,2的32次方个内存单元,一个内存单元有多大呢?
假如一个内存单元是1bit,2的32次方个内存单元是2的32次方bit,二进制位(1,0000,0000,0000,0000,0000,0000,0000,0000),转换成十进制为4294,967,296 bit
我总共管理多大内存空间呢? 4294,967,296bit/8=536,870,912byte/1024=524,288KB/1024=512MB/1024=0.5GB
分析:
32位机器有32根地址线,地址线是物理的线,物理的线可以通电,正电为1,负电为0,如若32根地址线上全部通上电,则有的电是正的,有的电是负的,这就有了正负电之分,也就产生了1和0这样的信号。
因此我们把电信号转换成数字信号,这时就变成了0和1 组成的二进制序列。
此时32根地址线产生的二进制序列的信号的可能性就为:32个全0,二进制序列每次涨1,
00000000000000000000000000000000//
…
01111111111111111111111111111111//每次涨1之后变成这样
10000000000000000000000000000000//再涨变成这样
…
11111111111111111111111111111111//再涨变成这样
最终产生2的32次方个序列,这些序列都可以作为内存的编号,我们就把这个编号就称为内存单元的地址。
综上:一个内存单元是一个字节,内存是有编号和地址的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cMZDxbeo-1624359386155)(en-resource://database/1265:1)
指针是什么?
指针就是地址:因为指针变量里面存的是地址,所以使用指针变量的时候就是使用的它里面的地址。
#include <stdio.h>
int main()
{
int a = 10;//a在内存中是要分配空间的,要分配4个字节的空间
printf("%p\n", &a);
int * pa = &a;
char ch = 'w';
char * pc = &ch;//
printf("%p\n", &ch);
return 0;
}
代码注解:
- &a:当一个变量所占地址空间为4个字节时,虽然它每个空间都会分配地址,但是&a取的是它的第一个字节的地址,而不是4个都拿。
- %p:专门用来打印地址
- 指针书写格式及指针变量如何创建
- pa:是用来存放地址的,在C语言中,pa叫指针变量;
- int:表示pa指向的对象是int类型的,即pa指向的是a这样的一个整型变量。
- *:说明pa是指针变量
- pc的类型是char *, *表示pc是指针,前面的char表示pc指向的变量的类型是char类型
#include<stdio.h>
int main()
{
int a = 10;
int * pa = &a;
*pa = 20;
printf("%d\n", a);//打印结果:20,
return 0;
}
代码注解:
- *:解引用操作符,它是一个单目操作符
- 因为pa里面存的是a的地址,所以pa就是通过pa里面的地址找到a,这个就是解引用操作,*pa就是通过pa里面的地址找到a
- 由此可知:我们改a可以不通过a改变,只需把a的地址交给pa,pa通过解引用(*),就可以找到a,即借助pa间接改变a
指针变量的大小
int main()
{
printf("%d\n", sizeof(char*));//4
printf("%d\n", sizeof(short*));//4
printf("%d\n", sizeof(int*));//4
printf("%d\n", sizeof(long*));//4
printf("%d\n", sizeof(long long*));//4
printf("%d\n", sizeof(double*));//4
printf("%d\n", sizeof(float*));//4
return 0;
} //指针的大小是相同的
为什么大小相同呢?
指针是用来存放地址的,指针需要多大空间取决于地址的存储需要多大空间.
32位的机器,一个地址是32个bit位=4个字节,因为我的电脑是32位平台的,所以指针大小是4个字节
64位的机器,一个地址是64个bit位=8个字节,如果电脑是64位平台的,指针大小就是8个字节
结构体
结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。
结构体可以让C语言创建新的类型出来。
结合结构体讲解. 和 ->操作符
//如:创建一个学生
#include<stdio.h>.
struct Stu
{
char name[20];//成员变量
int age;//成员变量
double score;//成员变量
};
//创建一个书类型
struct Book
{
char name[20];
float price;
char id[30];
};
//结构体的基本访问
int main()
{
//类型创建出来了,需要创建结构体变量并访问
struct Stu s = { "张三",20, 85.5 };//结构体的创建和初始化
printf("第一次打印:%s %d %lf\n", s.name, s.age, s.score);//结构体变量(s).成员变量,其中.是一个操作符
//打印结果:第一次打印:张三 20 85.500000
struct Stu * ps = &s;//struct Stu *:叫结构体指针,ps是指针变量
printf("第二次打印:%s %d %lf\n", (*ps).name, (*ps).age, (*ps).score);//ps指向s,
//*ps解引用之后就是s, (*ps).name就找到成员内部变量了
//打印结果:第二次打印:张三 20 85.500000
//由此可见,当我们把一个结构体的地址交给一个指针的时候,它也能够打印出相应的内容
printf("第三次打印:%s %d %lf\n",ps->name, ps->age, ps->score);//通过ps先找到它指向的成员对象,然后再找到它里面的成员
//打印结果:第三次打印:张三 20 85.500000
//这种打印方法,左边必须是结构体指针->(指向)成员变量
return 0;
}
上述代码总结:
- 1.如果我们拿到的是结构体变量本身,直接.成员变量就可以打印
- 2.如果我们拿到的是结构体指针的时候,我们要先解引用,然后再.成员变量打印
- 3.也可以结构体指针->成员变量,结构体指针先找到它指向的成员对象,然后再找到它指向对象的成员打印