C语言提供了丰富的关键字,这些关键字都是语言本身预先设定好的,用户自己是不能创造关键字的。
(1)break 在循环中用或者在switch语句中用
(2)continue 在循环中用
(3)do…while
(4)while
(5)for
(1)switch
(2)case
(3)default 是switch…case语句中的默认选项
(4)if…else 分支语句
(5)goto 语句
(1)char
(2)short
(3)int
(4)long
(5)float
(6)double
(7)sizeof 计算变量所占空间的大小(根据类型来去确定)
(8)const 修饰类型或者指针变量
(9)void 无具体类型
(10)signed 有符号的
(11)unsigned 无符号的
(12)union 联合体
(13)enum 枚举
(14)struct 结构体
(15)typedef
(1)auto
一般用法:
auto int a=9;
这里的auto一般省略不写,局部变量也叫自动变量,放在栈区,自动创建,自动销毁
(2)register
register 表示寄存器,用register修饰一个变量,这个变量会被放到寄存器中存储
计算机上的存储设备:硬盘、内存、高速缓存、寄存器(电脑上有几十个,每个4byte)
早期,CPU处理从内存中拿的数据,处理完再放到内存中去。内存里读多快,CPU就处理多块,内存里写多块,CPU就处理多块。随着技术的提升,CPU的处理速度越来越快,但是硬件的读写速度提升的不明显,此时出现CPU和内存工作不协同(内存读半天,CPU一下子处理完),所以找更快的存储设备,从更快的存储设备中拿。
因此,CPU从寄存器中拿数据,读写数据就提升上来了。
(3)static 静态的(影响变量的存储位置)
(1)return 函数中返回值
(2)volatile 展示不讲
(3)extern 做外部符号声明(当使用其他源文件中的符号时)
一般用法:
register有建议的作用,这里的意思是建议把100放寄存器中存储,是否真的放到寄存器中存储取决于编译器。编译器会评估num书不是被大量频繁使用,是的话就会放到寄存器中,否则就不放。
实际上如今的编译器不断发展,已经很聪明了,他会自动评估,将值放到寄存器中,所以加不加register就不需要我们操心了。
typedef 顾名思义是类型定义,这里应该理解为类型重命名。
在C语言中:
static是用来修饰变量和函数的
static的作用
(1)修饰局部变量:一个普通的局部变量是放在栈区的,而被static修饰的局部变量是放在内存的静态区的。这个普通的局部变量存储位置发生变化,使得局部变量除了作用域也不会销毁,影响了变量的生命周期,作用域不受影响。
//代码1
#include
void test()
{
int i = 0;
i++;
printf("%d ", i);
}
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
test();
}
return 0;
}
//代码2
#include
void test()
{
//static修饰局部变量
static int i = 0;
i++;
printf("%d ", i);
}
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
test();
}
return 0;
}
对比代码1和代码2的效果理解static修饰局部变量的意义。
结论:
static修饰局部变量改变了变量的生命周期
让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
(2)修饰全局变量:一个全局变量在整个工程中都可以使用。全局变量具有外部链接属性,在其他源文件的内部,只要适当的声明就可以使用(用extern)。static修饰全局变量,这时全局变量的外部链接属性变成了内部链接属性,只能在自己的.c文件内部使用,其他的.c文件无法使用,影响了变量的作用域。
//代码1
//add.c
int g_val = 2018;
//test.c
int main()
{
printf("%d\n", g_val);
return 0;
}
//代码2
//add.c
static int g_val = 2018;
//test.c
int main()
{
printf("%d\n", g_val);
return 0;
}
代码1正常,代码2在编译的时候会出现连接性错误。
结论:
一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使
用。
(3)修饰函数:函数是具有外部链接属性的。static修饰函数后,函数的外部链接属性就变成了内部链接属性。被static修饰的函数只能在自己所在的.c文件中使用,其他的.c文件无法使用。最终相当于影响了作用域。
//代码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在编译的时候会出现连接性错误.
结论:
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
(1)一个变量放在栈区就是临时的,进入它的范围额就会创建,出范围就会销毁。
(2)一个变量放在静态区,一旦创建好,直到程序退出,这个变量才销毁。
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。
变量是创建内存中的(在内存中分配空间的),每个内存单元(每个内存单元也就是上面的一小格)都有地址,所以变量也是有地址的。
(1)一个内存单元多大比较合适?
1byte比较合适。如果是1bit的话,char ch=0;这里就需要编8个地址,地址就划分的太细化了;如果是1KB的话,使用1字节的空间占用一个地址,有很多的空间就浪费了。所以1byte合适。
(2)内存单元的编号怎么产生?
地址的产生:
32位机器上,32位地址线(电线),电线是通电高电平、低电平,转换成数字就是1/0.
32根地址线给过来的电信号:
总共能产生2^ 32个二进制序列(2^32个地址)。
32根地址线产生的电信号转换成数字信号的二进制序列就是内存单元的编号。也有这样的二进制序列就可以在内存中找到一个内存单元和他匹配起来。
要点总结
一个内存单元是一个字节
一个内存单元有一个地址
2^ 32个地址能够管理2^ 32字节的空间。(2^ 32字节也就是4GB)
内存单元的编号=地址=指针
取出变量地址如下:
#include
int main()
{
int num = 10;
#//取出num的地址
//注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)
printf("%p\n", &num);//打印地址,%p是以地址的形式打印
return 0;
}
int num = 10;
int *p;//p为一个整形指针变量
p = #
指针的使用实例:
#include
int main()
{
int num = 10;
int *p = #
*p = 20;
return 0;
}
#include
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'q';
printf("%c\n", ch);
return 0;
}
#include
//指针变量的大小取决于地址的大小
//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;
}
结论:指针大小在32位平台是4个字节,64位平台是8个字节。
结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。
比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息。
这里只能使用结构体来描述了。
例如:
结构体的初始化:
//打印结构体信息
struct Stu s = {"张三", 20, "男", "20180101"};
//.为结构成员访问操作符
printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id);
//->操作符
struct Stu *ps = &s;
printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps-
>id);