写在前面
这里是我们学习所有语言的基础,我们在这里看到为何存在多文件?什么是变量…这些都是我们开始的基础,没有他们,很难做到深度理解C语言。这个博客实在是太重要了,它的内容倒是不太多,本来想和其他的内容合并到一起,最后由于太过重要就单独列出来了,大家好好看看。
多文件
现在的你可能还属于使用一个test.c文件的情况,由于现在我们写的代码还很少,用不到这么多的文件,想想一个场景,你写了一个函数,效果很好,我们在main.c,add.c,sub.c…等等文件里面都需要使用是个函数,你该怎么办? 我们有两种方法,一种是一个一个在文件中使用extern来声明这个函数,但是由于你的函数的功能是在是太好了,很多文件都要使用,我们每一个文件都声明是不是有些麻烦?这里就有人提出了另一个方法,我把所有函数的声明都放到一个.h文件中,使用某个函数,我就把这个头文件包含一下据可以了.这样就简单多了.
看到这个test.h的文件你是不是感到很熟悉?这不就是和stdio.h文件类似吗?是的,他们的作用是一样的,只不过一个是我们定义的,一个是标准库提供的.
如何包含头文件
这个很简单,使用include就可以了,下面是两种情况.
- 包含标准库的文件 使用<>,类似#include <stdio.h>,这样编译器直接就去库里面寻找
- 包含自定文件 使用“”, 类似#include “test.h”,这样编译器先回相同文件目录下寻找,找不到的话再去库里面
防止头文件被重复包含
这个一直是我们担忧的问题,由于头文件里面的代码有可能非常多,就单独论stdio.h就有大概800多行代码,我们要是多次包含了相同的头文件,编译器不会报错,但是会对代码的性能造成一定的影响,所以我们要尽可能的防止头文件被重复包含.下面提供了两种方法,都可以完成.大家不太懂的话也没有关系,按照我们这样做就可以了,后面随着知识的增加,会逐渐了解的.
在文件最上面加上 #pragma once ,这个比较简单.
#pragma once
#include <stdio.h>
#include <stdio.h>
#include <stdio.h>
使用条件编译,这个要注意一点
#ifndef __TEST_H__
#define __TEST_H__
#include <stdio.h>
#include <assert.h>
#endif
左值&右值z
关于左值和右值我们需要记住就可以了,不是记住定义,而是他们的区别,我们只要可以区分它们就行了.
- 左值 可以理解是一块空间,可以修改空间里面的值
- 右值 可以理解为不可以修改的值
int main()
{
int a = 1;
a = 10;
return 0;
}
int main()
{
1 = 10;
return 0;
}
常量
我先谈谈常量,后面再说说变量。所谓的常量就是不可再次修改的数据.它有下面的几种分类.
- 数值常量 1,2,3…
- 字符串常量 “abcd”,“defcbg”
这里面我重点谈谈字符串常量,在C语言中,存在一个字符串常量池,例如"abcd"就是其中的一部分,要是我们想要修改"abcd"变为"bbcd",这是做不到的,有的人可能会感到疑惑,这不是很简单吗,我们只需要把a修改成b不就完成了吗?但是这只是表象,"bbcd"也是字符串常量池的一部分,我们所谓的修改,就是更改指针所指向的地址.
#include <stdio.h>
int main()
{
char* pa = "abcd";
printf("%p\n", pa);
pa = "bbcd";
printf("%p\n", pa);
return 0;
}
我们从这里就可以得出一个结论,相同字符串常量所在的地址是一样的.
int main()
{
char* pa = "abcd";
char* pb = "abcd";
if (pa == pb)
{
printf("pa == pb\n");
}
return 0;
}
变量
在C语言中,我们会学习到变量,常量,作用域,生命周期…等概念,对与数据在内存中的存储可能会有一些疑问.我们不由得发出疑问,什么是变量,为什么要有变量?这些都是我们需要处理得疑惑.
变量是什么
简单得来说,变量就是可以修改得量.变量不同于常量,它可以被修改,int x = 10,x可以修改为20,但是我们不能把常量1修改成常量2.这就是变量定.
在计算机中,变量是一个在内存中开辟特定大小的空间,用来保存数据.
怎么定义变量
这倒是很简单,就是类型 变量名 = 初始化的值
int a = 10;
double b = 10.2;
为何要有变量
计算机是为了解决人计算能力不足的问题而诞生的。即,计算机是为了进行计算的。 而计算,就需要数据。而要计算,任何一个时刻,不是所有的数据都要立马被计算。 如同:要吃饭,不是所有的饭菜都要立马被你吃掉。饭要一口一口吃,那么你还没有吃到的饭菜,就需要暂时放在盘子里。 这里的盘子,就如同变量,饭菜如同变量里面的数据。 换句话说,为何需要变量?因为有数据需要暂时被保存起来,等待后续处理。 那么,为什么吃饭要盘子?我想吃一口菜了,直接去锅里找不行吗?当然行,但是效率低。 因为我们吃饭的地方,和做饭的地方,是比较"远"的。
总而言之,变量就是为了辅助计算机更加高效率的工作.
变量的分类
变量的分类主要分为两大类.
局部变量:包含在代码块中的变量叫做局部变量。局部变量具有临时性。进入代码块,自动形成局部变量,退出代码块自动释放。(网上很多说函数中的变量是局部变量,不能说错,但说法是不准确的) 全局变量:在所有函数外定义的变量,叫做全局变量。全局变量具有全局性
作用域&生命周期
这个一直是一个比较容易模糊的点,有很多人对他们的区别可能分不太清楚.
- 作用域 变量可以被使用的地方,就是作用域
- 生命周期 变量开始开辟空间 到 空间销毁
这里我先给出点结论,适合大部分情况.局部变量的作用域和生命周期是在局部变量所在的代码块中,除了代码块就不可以了.全局变量的作用域和生命周期是整个程序.
C程序地址空间
C程序地址空间是一块虚拟的地址空间,我们先不用理解这个图,记住就可以了.
这里只写一些代码验证一下,具体的要学习操作系统的知识了,只需了解并认识变量如何存储的的就行。
结果是:
全局变量
这里我想和大家谈谈全局变量,这个有一点重要.
所谓的全局变量,我们可以认为是在main函数外边所定义的变量,一般按照g_变量名的格式来走,当然,也可以不按.
int g_key = 20;
double g_val = 10.00;
int main()
{
int a = 10;
double b = 10.2;
return 0;
}
全局变量的存储
在程序设计中,全局变量是在所有作用域都可访问的变量,与之对应的是局部变量。(来源:维基百科)
全局变量存储,全局变量存储在堆上,依次向上存储.
这里我有一点需要提醒大家的,由于全局变量具有外部链接属性(大家先看看就行),不要在自定义的头文件中定义全局变量,会造成变量的重定义.最好声明也不要,这里我会在extern那里重点谈的.
局部变量
在计算机科学中,**局部变量(英语:local variables)**是拥有局部作用域的变量。这样的变量只能由声明它的函数或块中访问。在仅有两层可见性的程序设计语言中,局部变量对应全局变量;另一方面,许多类ALGOL语言允许任意多层的嵌套函数,各自拥有私有变量、函数、常量和类型。(来源:维基百科)
局部变量的存储
我先和大家来看看这个局部变量的在内存的存储,局部变量存储在栈区.
局部变量存储在栈上,你会发现a、b、c的地址依次是降低的,这是因为局部变量在栈上开辟空间时是依次开辟的,每开辟一个空间,都是在从下往上压栈
总结
- 常量是不可以修改的
- 变量的本质是在内存中开辟一块空间
- 地址空间好像是一个内存,但实际上不是。
- 局部变量是“先进后出”。
- 局部变量的全局变量是“相对而行的”。
|