目录
前言:
1.关键字
1.1常见关键字:
1.1.1typedef类型重定义
1.2.static
1.2.1static修饰局部变量
1.2.2static修饰全局变量
1.2.3static修饰函数
1.3#define定义常量和宏
1.3.1#define定常量
1.3.2#define定义宏
2.指针
2.1.什么是内存
2.1.1地址是如何产生的
2.2指针
2.2.1指针变量的大小
3.结构体
3.1为什么会有结构体呢
3.2结构体的语法形式
3.3结构体成员.和->
3.3.1 结构体成员.使用
前言:
小伙伴们大家好!来和菜菜酱一起学习初识c语言最后一部分叭!ok,废话不多说直接开始叭
1.关键字
1.1常见关键字:
auto break case char const continue default sizeof static |
do double else enum extern float for goto union unsigned |
if int long register return short signed switch typedef |
c语言提供了丰富的关键字,这些关键字是语言本身预先设定好的,用户不能创建关键字也不能用关键字命名为变量名!!!
1.1.1typedef类型重定义
#include<stdio.h>
typedef unsigned int u_int;
int main()
{
//unsigned int n = 13;
u_int n = 13;
if (n)
{
printf("haha\n");
}
return 0;
}
让我们一起来分析一下代码叭!最开始n的类型为unsigned int类型,但是我们发现这个类型写起来好麻烦,有什么方法可以把这个类型名变得简单一点吖?这个时候我们的主角typedef就该上场了!typedef unsigned int u_int,typedef重新把unsigned int 命名为u_int。这种就好比我叫张三,然后家里人会喊小名三三。三三是我张三也是我!在这里u_int是unsigned int 的小名。在代码中直接使用u_int是可以的哦。来看看运行结果叭
总结一下:typedef可以将类型重命名!可以起到简便作用!
1.2.static
static的三个作用:
- static可以修饰局部变量---静态局部变量
- static可以修饰全局变量---静态全局变量
- static可以修饰函数---静态函数
1.2.1static修饰局部变量
酱酱们还记得关于变量的生命周期和作用域嘛?在那节中我们说过全局变量的生命周期和作用域是整个工程,局部变量的生命周期进入作用域生命周期开始,出作用域生命周期结束。局部变量的作用域指的是函数的有效区域
#include<stdio.h>
void test()
{
int n = 6;
n++;
printf("%d ", n);
}
int main()
{
int a = 0;
while (a <5)
{
test();
a++;
}
return 0;
}
以这个代码为例,在test函数中变量n是局部变量,它的作用域是test整个大括号,它的生命在周期是进入大括号开始,出大括号结束。所以每次调用test函数,n的值都是从6开始,n++之后n的值改为7并打印!此时代码的结果显而易见啦,我们一起crtl+f5运行一下代码叭
没错结果就是5个7!我们再来看看那要是调试结果叭
第一次进入test函数(进入函数内部调试要按f11)n最开始的值为6,n++之后值为7。我们再按f10会发现返回到了while里
然后再重复之前的动作,再次按f11进入test函数
此时我们发现刚进入n的值先是为随机值,再次按f10。此时n的值重新为6
当n++之后n的值为7,并且此时再次再屏幕上打印7。后面就一直再重复2至7号的动作!
我们在int n前面加上一个static修饰结果又会是什么样的呢?一起来看看吧!
#include<stdio.h>
void test()
{
static int n = 6;
n++;
printf("%d ", n);
}
int main()
{
int a = 0;
while (a <5)
{
test();
a++;
}
return 0;
}
直接ctrl+f5运行看看结果吧!
哦豁,可之前的结果不一样欸,这是为什么呢?我们来调试一下代码!
这个代码的执行程序和之前的一样,我们主要来看看第二次进入test函数n的值为多少吧!
此时我们可以观察到进入test函数之后直接跳过static int n =6,
直接来到n++并且此时n的值没有变成随机值也没有变成6,而一直是7.。再次f10,我们发现n++之后n的值为8。
我们可以得出一个结论:被static修饰的局部变量在编译期间就创建好了,出作用域没有被销毁,在程序彻底结束时才会销毁。而不加static修饰的局部变量出作用域就被销毁每次再进作用域会产生新的值!!!可是为什么会这样呢?此时我们就需要了解一下原理啦
在c/c++代码学习中把内存划分为三个区域:栈区,堆区,静态区
栈区的特点是:进入作用域创建,出作用域销毁(也就是把内存还给操作系统)
堆区的特点:进行动态分配(后期会讲噢)
静态区的特点:创建好之后直到程序结束才会销毁。
了解这些完毕我们再来解答,static修饰局部变量(生命周期边长,但作用域不变)本来局部变量在栈区,被static修饰之后存储到了静态区。即static修饰局部变量改变了变量的存储位置,使得局部变量生命周期变长了直到程序结束才结束,但依旧是局部变量。
1.2.2static修饰全局变量
直接上代码!!!
当我们写这样一段代码时,感觉好像好像没问题。然后crtl+f5运行一下
然后就发现代码运行出错了,发出警告Add未定义。为什么会这样呢?我们 不是在add.c源文件里定义了Add函数嘛,Add函数不是全局变量嘛为什么还会报错呢?这是因为在跨文件是使用全局变量之前要进行声明!使用关键字extern声明其类型函数名参数类型等!
此时我们会发现代码就可以成功运行啦!超棒哒!接下来我们试试加上static修饰叭
还是之前的代码!ctrl+f5运行叭
哦豁!为什么又失败了我们不是用extern声明了外部符号嘛为什么加上static修饰之后会报错呀!解释:全局变量之所以能够跨文件使用,是因为其具有外部连接属性!被static修饰之后外部连接属性变成了内部链接属性!被修饰的全局变量只能在内部源文件使用不可以跨文件是使用呐而且感觉被static修饰的全局变量作用域变小了,但是它的位置不变噢!
所以此时我们应当将代码改为
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
static int Add(int a, int b)
{
return a + b;
}
int main()
{
int m = 0;
int n = 0;
scanf("%d%d", &m, &n);
int k = Add(m, n);
printf("%d ", k);
return 0;
}
此时就是使用啦!
1.2.3static修饰函数
还是用上面的代码为例。函数和全局变量一样具有外部链接属性,可以进行跨文件使用!但是被static修饰之后,外部链接属性变为内部链接属性!只能在内部源文件内使用不可以在其他源文件里使用!这是不是和全局变量一样呐!
1.3#define定义常量和宏
1.3.1#define定常量
#define _CRT_SECURE_NO_WARNINGS
#define m 10
#include<stdio.h>
int main()
{
int arr[m] = {1,2,3,4,5,6,7,8,9,0};//此时m是一个常数
int i = 0;
for (i = 0; i < m; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
当我们运行代码时可以成功打印结
#define定义的常量和const修饰的常变量不一样!#define定义的标识符常量本质是一个常量,而const修饰的常变量只是具有常属性本质还是一个变量!
1.3.2#define定义宏
#define _CRT_SECURE_NO_WARNINGS
#define Add(x,y) ((x)+(y))
//Add是宏的名字,(x,y)是宏的参数 ((x)+(y))是宏的实现体
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c = Add(a, b);
printf("%d ", c);
return 0;
}
在编译期间int c=Add(a,b)就已经替换为int c =((a)+(b))。ctrl+f5代码运行成功没有保证错
2.指针
2.1.什么是内存
内存是电脑上特别重要的存储器,计算机中的程序的运行都是在内存中进行的。
所以为了有效使用内存,把内存划分为一个个内存单元,每个内存单元的大小是一字节。
为了有效访问到每个内存单元,就给内存进行编号,这些编号称之为内存单元的地址
2.1.1地址是如何产生的
在计算机中有地址线(物理线)通电后产生高低电频,转换为数字信号(1/0)。
在32位机器上有32根地址线,可以产生2的32次方个编号同时也说明可以管理2的32次方的空间(即2GB)
同理得,在64位机器上有64位地址线,可以产生2的64次方个编号。
int main()
{
int a = 10;
printf("%p ", &a);
return 0;
}
int的大小为4个字节
那么int a 在内存中的存储应该是这样,那么我们打印a的地址是打印出来的是哪个值呢?一起来调试代码叭
为什么是00 00 00 01呢?这是因为为了方便在内存中中采取十六进制的方式!四位二进制数字可以换取得到一位十六进制数字!再来看看打印出来的地址 此时我们惊奇的发现打印出来的地址是int a四个字节地址中的第一个地址!!
2.2指针
在c语言中,把编号称为地址,把地址又称之为指针!
当我们得到一个变量的地址该如何进行存储呢?上代码
#include<stdio.h>
int main()
{
int a = 1;
int* pa = &a;
*pa = 10;
printf("%d", a);
return 0;
}
利用&(取地址操作符)拿到a的地址,并把a的地址存放在变量pa中。在c语言中我们把存放地址的变量称之为指针变量!pa的类型为int*类型,*说明pa是一个指针,int说明被指向的对象的类型是int类型。pa中存放着a的地址我们可以通过*解引用操作符来通过地址找到a,此时的*pa就相当于a,可以改变a原本的值!让我们一起来运行代码看看结果叭
哦豁说明我们的分析正确欸!我们再来画图理解一下pa和a的关系叭
在这里再说一下,指针是地址也是编号,指针变量是用来存放地址的变量!口语中所说的指针一般都是☞指针变量!
2.2.1指针变量的大小
指针变量是用来存放地址的。在(x86)32位机器上,地址是32为二进制位,这个地址要存储的话需要4个字节。在(x64)64位机器山,地址是64位二进制位,这个地址存储的话需要8个字节(因为1个字节=8个比特位)
3.结构体
3.1为什么会有结构体呢
结构体的出现是为了帮我们描述复杂对象。例如对于一本书的描述我们需要有定价,作者,书名等,对一个学生的描述我们需要有性别,姓名,年龄,学号等
3.2结构体的语法形式
上代码!
#include<stdio.h>
struct stu
{
char name[20];
int age;
char sex[5];
};
int main()
{
struct stu s = { "张三",20,"男" };
printf("%s %d %s ", s.name, s.age, s.sex);
return 0;
}
从这个代码我们可以发现struct的语法形式为
struct tag
{
tag的相关属性;
};//这里要注意一定不能漏了分号
struct tag一起组成结构体类型!结构体的初始化要用{}
3.3结构体成员.和->
还是以上面的代码为例
3.3.1 结构体成员.使用
在使用结构成员之前先使用结构体类型创建一个变量,结构体访问对象用. 构成结构体变量.成员名
3.3.2结构体成员->
这个和上面的结构体变量又不相同!->使用指针中哒,上代码看看效果叭
#include<stdio.h>
struct stu
{
char name[20];
int age;
char sex[5];
};
void print(struct stu* ps)
{
printf("%s %d %s ", ps->name, ps->age, ps->sex);
}
int main()
{
struct stu s = { "张三",20,"男" };
print(&s);
return 0;
}
在这个代码里向print函数传送的是变量s的地址,因此在print函数中要用结构体指针来接收。在打印时直接指针变量->结构体成员即可!
总结:结构体成员的两种使用方法:第一种:结构体变量.结构体成员。第二种:结构体指针->结构体成员!!!
以上就是第一章的最后一部分的内容!