这里只放一些面试中常常会问的基础知识点、static、enum、struct、const等后续还会将自己总结的陆续公开。
静态关键字static
static关键字修饰变量和函数:
1、局部变量
2、全局变量
3、函数
static关键字最基本的用法是:
1、被static修饰的变量属于类变量,可以通过类名.变量名直接引用,而不需要new出一个类来
2、被static修饰的方法属于类方法,可以通过类名.方法名直接引用,而不需要new出一个类来
一、修饰局部变量
1、用静态关键字static修饰的局部变量,在编译的过程中,会在数据区为该变量开辟空间,并对其进行初始化,如果代码中未对其进行初始化,则系统默认初始化为0
2、用static修饰的局部变量,会延长局部变量的寿命,超出函数的生存期
3、对静态关键字修饰的局部变量的初始化
在这里插入代码片
#include "stdio.h"
int sum(int a){
int c=0; //局部变量,每次循环结束后,c又会初始化为0;
static int b=3; //静态局部变量,出了作用域依然存在,到程序结束,生命周期才结束
c+=1;
b+=2;
return (a+b+c);
}
int main(){
int i=0;
int a=2;
for (int i = 0; i < 3; ++i) {
printf("%d\n",sum(a));
}
return 0;
}
二、修饰全局变量
全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化
普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了。
静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。
在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。
三、修饰函数
static修饰函数时和static修饰全局变量很相似,因为函数一般都是有外部链接的,被 static 修饰后,会使得函数失去外部链接属性,变成内部链接属性。所以 static 修饰的函数只能在自己所在的 .c 文件内部使用,不能在其他文件中使用
//text1.c
#include "stdio.h"
int fun(){
printf("hello ,from text1\n");
}
//main.c
#include "stdio.h"
extern int fun();
int fun1(){
printf("hello ,from fun1\n");
}
int main(){
fun();
fun1();
return 0;
}
//运行main.c的结果是:
//hello ,from text1
//hello ,from fun1
enum(枚举)
用#define给每个变量进行定义别名时,有些变量可以用更加简洁的方法定义
例如:
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN 7
为了更加的方便我们可以用枚举enum的方法去重新定义
enum DAY{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
枚举变量的三种方法
//方法一
enum DAY{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;
}
//方法二
enum DAY{
MON=1, TUE, WED, THU, FRI, SAT, SUN
}day;
//方法三
enum {
MON=1, TUE, WED, THU, FRI, SAT, SUN
}day;
用enum定义变量的实例
#include "stdio.h"
enum DAY{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
int main(){
enum DAY day;
day=SUN;
printf("枚举元素:%d\n",day);
return 0;
}
for循环实现遍历枚举元素的方法
enum DAY{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
int main(){
enum DAY day;
for (int day = MON; day < SUN; ++day) {
printf("枚举元素:%d\n",day);
}
return 0;
}
**
C语言中的struct
**
结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员。
结构体也是一种数据类型,它由自己定义,可以包含多个其他类型的数据。
#include "stdlib.h"
struct stu{
char *name;
int age;
char *sex;
int height;
};
/*定义两个结构体变量
方法一:struct stu stu1, stu2;
方法二,
#include "stdlib.h"
struct stu{
char *name;
int age;
char *sex;
int height;
}stu1,stu2;
*/
结构体成员的赋值
#include "stdlib.h"
struct stu{
char *name;
int age;
char *sex;
int height;
}stu1;
int main(){
stu1.name = "tom";
stu1.age=16;
stu1.sex="man";
stu1.height=175;
printf("学生的名字是:%s\n学生的年龄是:%d\n学生的性别是:%d\n学生的身高是:%d\n",stu1.name,stu1.age
,stu1.sex,stu1.height);
}
这里穿插一下指针的用法
地址和指针:在程序运行过程中,变量或者程序代码被存储在以字节为单位组织的存储器中,计算机为了对内存单元中的数据进行操作,一般是按“地址”存取的,也就是说对内存单元进行标识编号。
直接赋值:
int a=5;
这一句话完成了两个操作,计算机内部主要分为:(1)、int a; (2)、a=5; 先在栈中定义了一个变量a,并且在内存中开辟了一个int类型大小的空间, 即4个字节 ,然后让a指向这个空间,a有一个属于自己的空间;其次,在a的自己的那片空间,里面存放数值5 ,把5转换成二进制,存到a的4个字节的空间 ,这就是直接赋值。
前言
//指针
int *p;//整型数据的指针
//数组
int p[3];//先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,
//所以P 是一个由整型数据组成的数组
//指针数组
int *p[3];//从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,
// 然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数
//数组指针
int (*p)[3];//从P处开始,先与*结合,说明P是一个指针然后再与[]结合,说明指针所指向的内容是一个数组,然
// 后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针
实例
int ptr; 变量名叫number,类型为int *,可存放一个int数据的地址 。
#include "stdio.h"
int main(){
int *ptr;
int number=5;
ptr=&number;
printf("number的值是:%d\n",*ptr);
}
数组的使用和定义
什么是数组呢?顾名思义数组就是很多数的组合!数组中的每一个数据叫做数组元素Element,数组中的每个元素都有一个序号,这个序号从0开始的,称之为index,a[0]就是第一个元素,
#include "stdio.h"
int main(){
int a[4]={1,2,4,5};//也可以也为int a[]={1,2,4,5};
int b;
for (int b = 0; b < 4; ++b) {
printf("%d ",a[b]);
}
a[3]=100;
printf("\n");
for (int i = 0; i < 4; ++i) {
printf("%d ",a[i]);
}
}
关键字const用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变,用 const 定义常变量的方法很简单,就在通常定义变量时前面加 const 即可,如:
const int a=1;//const和int位置可以互换,因此也可以写作int const a=1;
const 和define的区别
1、define是预编译指令,而const是普通变量的定义。define定义的宏是在预处理阶段展开的,而const定义的只读变量是在编译运行阶段使用的。
2、const定义的是变量,而define定义的是常量。define定义的宏在编译后就不存在了,它不占用内存,因为它不是变量,系统只会给变量分配内存。但const定义的常变量本质上仍然是一个变量,具有变量的基本属性,有类型、占用存储单元。可以说,常变量是有名字的不变量,而常量是没有名字的。有名字就便于在程序中被引用,所以从使用的角度看,除了不能作为数组的长度,用const定义的常变量具有宏的优点,而且使用更方便。所以编程时在使用const和define都可以的情况下尽量使用常变量来取代宏。
3、const定义的是变量,而宏定义的是常量,所以const定义的对象有数据类型,而宏定义的对象没有数据类型。
修饰全局变量
全局变量的作用域是整个文件,且全局变量的生存周期为程序运行的整个过程,所以我们应该尽量避免使用全局变量,一旦某个函数改变了全局变量的值,会影响到其他引用这个变量的函数,是一个很隐蔽的操作。
如果一定要用全局变量,应该尽量的使用const进行修饰,防止不必要的人为修改,使用 const 修饰过的局部变量就有了静态特性,它的生存周期也是程序运行的整个过程,虽然有了静态特性,但并不是说它变成了静态变量。
修饰常量指针与指针常量
常量指针是指针指向的内容是常量,可以有一下两种定义方式
const int *p;
int const *p;
需要注意以下两点
1、常量指针:不能通过这个指针改变变量的值,但是还是可以通过其他的引用来改变变量的值的。
int cnt=5;
const int *num=&cnt;
cnt=6;
2、常量指针指向的值不能改变,但是这并不是意味着指针本身不能改变,常量指针可以指向其他的地址。
int cnt=5;
int tmp=6;
const int *num=&cnt;
num=&tmp;
指针常量
指针常量是指指针本身是个常量,不能在指向其他的地址,写法如下:
int *const n;
指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向该地址的指针来修改。
int a=5;
int *p=&a;
int* const n=&a;
*p=8;
内存四区
简述一下内存四区的个人理解
C语言在编译时候,内存占用主要可以分为四个区域,分别为:栈区、堆区、全局区(静态区)、代码区,每个程序都有唯一的内存四区,这里分别对四个区进行简单阐述。面试时候可能会问哪些变量存在哪个区。
栈区
首先说一下栈区中的存储内容吧,栈区主要存放的是函数的参数值、返回值以及局部变量值。栈是一种先进后出的内存结构,由编译器自动分配释放,在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。
堆区
用于动态分配内存,堆区也是RAM里面的一段,一般由程序员手动分配释放 (比如使用C语言内的malloc和free动态内存分配) ,若程序员不释放,程序结束时可能由操作系统回收,如果因为其他原因没有及时释放,程序结束后,没有指针指向这一块内存,则会导致内存泄漏。
全局区(静态区)
全局区的叫法很多全局区的存储内容主要有**:全局变量、静态变量、常量**,再进行细分的话,初始化的全局变量和静态变量是在一个区域、未初始化的全局变量和未初始化的静态变量或者初始化为0的全局\静态变量在相邻的另一块区域、常量,const声明后的变量存在变量区。
代码区
代码区是最简单的,你写的代码都会放入到代码区,它的特点是共享和只读。
补充
这里再补充一个简单的最基础最基础的小知识点,面试时让我现场定义,仅供参考。以变量a为例。
1)定义一个整型数;
2)定义一个指向整型数的指针;
3)定义一个指向指针的指针,它指向的指针是指向一个整型数;
4)定义一个有10个整型的数组;
5)定义一个有10个指针的数组,该指针是指向一个整型数;
6)定义一个指向有10个整型数数组的指针;
7)定义一个指向函数的指针,该函数有一个整型参数并返回一个整型数;
8)定义一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数;
int a;
int *a;
int **a;
int a[10];
int *a[10];
int (*a)[10];
int (*a)(int);
int (*a[10])(int);