目录
1、关键字
1.1C语言宏中的#和##
1.2关键词volatile的使用场景
1.3关键词static的作用
1.4extern关键字(声明)
1.5 const 作用
1.6 const使用场景
1.7new/delete和malloc/free的区别
1.8 strlen(" ")=?0 sizeof(" ")=?2
1.9 struct 与 union 的区别是什么:结构体和联合体区别
1.10左值和右值
1.11 短路求值
1.12 ++a和a++运算
2.内存
2.1C语言内存分配的方式
2.2堆和栈区别
2.3栈的用处
2.4 C++内存管理
2.5内存泄漏
3.指针
3.1数组指针和指针数组
3.2函数指针和指针函数
3.3数组名与指针
3.4指针常量 常量指针,指向常量的常量指针
3.5指针和引用区别
3.6野指针
3.7C++中智能指针
4 预处理
4.1 预处理标识符#error
4.2define和const
4.3typedef 和define区别
4.4#include和#include “filename.h”
4.5头文件作用
4.6 头文件中是否可以定义静态变量
5、变量
5.1全局变量和局部变量区别
5.2 全局变量是否可以被定义在多个.c文件包含的头文件中,为什么?
5.3局部变量是否可以和全局变量重名
6、函数
6.1怎么可以让函数在main函数之前运行
6.2为什么析构函数必须是虚函数
1、关键字
1.1C语言宏中的#和##
#可以将宏定义中的传入参数名转换为双引号括起来的参数名字符串,必须置于宏定义的参数名前
##是将宏定义的多个形参转换成一个实际的参数名
#define STRCPY(a, b) strcpy(a ## _p, #b) int main() { char var1_p[20]; char var2_p[30]; strcpy(var1_p, "aaaa"); strcpy(var2_p, "bbbb"); STRCPY(var1, var2);//转换为strcpy(var1_p,"var2") STRCPY(var2, var1); printf("var1 = %sn", var1_p);//var1=var2 printf("var2 = %sn", var2_p);//var2=var1 return 0; }
1.2关键词volatile的使用场景
volatile(直接存取原始内存地址)(避免对应的变量值在程序不知道的情况下发生改变)
使用场景:
1.并行设备的硬件寄存器(状态寄存器)
2.中断服务子程序会访问的变量
3.多线程应用中被几个任务共享的变量
1.3关键词static的作用
作用:更改函数与变量的作用域,只在内存中分配一次;
1、在函数体内,一个被声明的静态局部变量在函数反复调用时保持值不变
2、模块内,函数体外,一个被声明的全局静态变量只能被模块内函数访问
3、模块内的静态函数可以被模块内的其他函数使用
1.4extern关键字(声明)
1、变量声明情况分类
int i ;声明+定位 需要存储空间 extern int i; 声明 + 定义 不需要内存空间
2、在C语言中,修饰符extern用在变量和函数的声明之前,用来说明此变量/函数是在别处定位的,需要在此处引用。 extern 的应用方式比包含头文件方便很多,想用哪个函数就extern哪个函数,加速编译过程。
3、extern “C” 指示编译器这部分代码按C语言规则编译。
#ifndef __INCvxWorksh #define __INCvxWorksh #ifdef __cplusplus//告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的 extern "C"{ #endif #ifdef __cplusplus } #endif #endif
1.5 const 作用
1、定义变量为常量,必须初始化
2、修饰函数的参数,表示在函数体内不能修改这个参数的值
3、修饰函数的返回值(只用于修饰指针变量,一般都是修饰内容不变)
4、相比与宏,const可以避免不必要的内存分配,宏在编译时每次替换都会分配内存空间,const只分配一次。
1.6 const使用场景
2、修饰常数组、常指针、常对象(对象常量、不能被更新)
3、修饰形参
4、修饰函数返回值
1.7new/delete和malloc/free的区别
1.new/delete是c++中的操作符,malloc/free是库函数
2.new和delete对应于构造函数和析构函数,,malloc和free是内存分配
3.new返回指定类型的指针,并且可以自动计算申请内存的大小。而 malloc需要我们计算申请内存的大小,并且在返回时强行转换为实际类型的指针
1.8 strlen(" ")=?0 sizeof(" ")=?2
1、strlen是库函数,用于计算字符串长度(),遇到 结束。sizeof是关键字,以字节形式给出操作数存储大小。
2、strlen在运行期计算,sizeof在编译期计算。
不用sizeof如何求 int 占用的字节数
#include#define Mysizeof(Value) (char*)(&Value+1)-(char*)&value int main() { int i; double f; double *q; printf("%drn",Mysizeof(i)); printf("%drn",Mysizeof(f)); printf("%drn",Mysizeof(a)); printf("%drn",Mysizeof(q)); return 0 ; } 4 8 32 4 (char*)&Value 返回Value的地址的第一个字节,(char*)(&Value+1)返回value 的地址的下一个地址的第一个字节。
不用sizeof求电脑的位数(只需要求两个char指针之间的宽度)
#includeusing namespace std; int main() { void* a; void* b; int scope = (char*)&a - (char*)&b; cout << "&a:" << &a << endl; cout << "&b:" << &b << endl; cout << "scope:" << scope << endl; if(scope==8) { cout << "64bits" << endl; } else { cout << "32bits" << endl; } }
1.9 struct 与 union 的区别是什么:结构体和联合体区别
struct 和union是两个不同的复合结构,区别在于:
1、联合体共用一块地址空间,一个联合体变量的长度等于最长的成员长度;结构体不同成员放在不同的地址中,占用空间是累加的(考虑空间对齐);
2、对联合体不同成员赋值将会对他的其他成员重写,原来的成员的值就不存在了。对结构体的不同成员赋值是互不影响的。
1.10左值和右值
左值可写,右值可读;
1.11 短路求值
提前判断(逻辑与/逻辑或)
#includeint main() { int i = 6; int j = 1; if(i>0||(j++)>0);//提前判断 printf("%Drn",j); return 0; }
1.12 ++a和a++运算
int temp = a;
a = a+1;
return temp;
++a的过程
a = a + 1;
return a ;
2.内存
2.1C语言内存分配的方式
2.1C语言内存分配的方式
栈:函数调用和使用,局部变量;
堆:内存的动态申请和归还 malloc/free
静态数据区:保存局部变量和静态变量
2.2堆和栈区别
1、申请方式(栈上OS自动分配/释放 堆上手动)
2、申请大小的限制(栈小,堆大)
3、地址的生长方向(栈溢出可能会导致栈顶的数据发生覆盖)
4、申请效率(栈快 , 堆慢)
2.3栈的用处
1、存储临时变量和函数参数
2、多线程编程的基石,每个线程都有专属的栈,中断和异常处理也有栈;
2.4 压栈顺序
从右往左
int p; printf("%d %d %dn",p++,++p,p++) 2 2 0
2.4 C++内存管理
C++中的虚拟内存分为 代码段,数据段,BSS段,堆区,文件映射区,以及栈区;
代码段:包括只读存储区(存储字符串常量)和文本区(机器代码)
数据段:存储程序中已经初始化的全局变量和静态变量
BSS段:存储未初始化的全局变量和所有被初始化为0的全局变量和静态变量
堆区:调用new/malloc函数在堆区动态分配内存,调用delete和free释放内存
映射区:存储动态链接库
栈:存储函数的返回地址,参数,局部变量,返回值
2.5内存泄漏
内存泄漏就是指申请了一块内存,但是使用完以后没有释放掉
3.指针
3.1数组指针和指针数组
数组指针是指向数组的指针
//声明了一个指针,该指针指向了一个有9个int型元素的数组 int (*pa)[9]
指针数组是指一个数组,数组中的元素是指针(其实就是相当于数组元素是地址,解引用之后可以得到地址中的值)
3.2函数指针和指针函数
函数指针是一个指针,该指针指向一个函数
int(*p)(int, int);
# includeint Max(int, int); //函数声明 int main(void) { int(*p)(int, int); //定义一个函数指针 int a, b, c; p = Max; //把函数Max赋给指针变量p, 使p指向Max函数 printf("please enter a and b:"); scanf("%d%d", &a, &b); c = (*p)(a, b); //通过函数指针调用Max函数 printf("a = %dnb = %dnmax = %dn", a, b, c); return 0; } int Max(int x, int y) //定义Max函数 { int z; if (x > y) { z = x; } else { z = y; } return z; }
指针函数是指返回值为指针类型(地址值)的函数
int *pfun(int, int);
#includefloat *find(float(*pionter)[4],int n);//函数声明 int main(void) { static float score[][4]={{60,70,80,90},{56,89,34,45},{34,23,56,45}}; float *p; int i,m; printf("Enter the number to be found:"); scanf("%d",&m); printf("the score of NO.%d are:n",m); p=find(score,m-1); for(i=0;i<4;i++) printf("%5.2ft",*(p+i)); return 0; } float *find(float(*pionter)[4],int n) { float *pt; pt=*(pionter+n); return(pt); }
3.3数组名与指针
1.数据保存方面
指针保存的是地址,内存访问偏移量为四个字节,无论其中保存的是何种数据,都是以地址类型进行解析
数组保存的数据,数组名表示的是第一个元素的地址,内存偏移量是保存数据类型的内存偏移量,只有对数组名取地址时,数组名才表示整个数组, 内存访问偏移量为整个数组的大小(sizeof(数组名))
2.数据访问方面
指针是间接访问,使用解引用,*
数组对数据访问是直接访问,通过下标或者数组名加偏移量
3.使用环境
指针多用于动态数据结构,(链表等)和动态内存开辟
数组用于存储固定个数且类型统一的数据结构和隐式分配
3.4指针常量 常量指针,指向常量的常量指针
常量指针:
int const *p = &a; const int *p = &a;
指针指向的是个常量,指针指向的值就不能被改变
理解如下:
指针常量:指针本身是常量,指向一个确定存在的地方。
int * const p; int a = 0, b = 0; int *const p = &a; *p = 1;//正确,可以改初值 *p = &b;//错误,不可改地址
指向常量的指针常量(地址不可变,值不可变)
int a = 0, b = 0; const int *const p = &a; *p = 1;//不可以修改初值 *p = &b;//不可以改地址
3.5指针和引用区别
同:都是地址的概念,从内存分配上来看,两者都是占用内存的
不同:
1.指针是实体,引用是别名
2.引用只能初始化一次,不能为空,之后不可变,指针可变可空
3.sizeof得到的引用是指向的变量(对象)的大小,指针是指针本身的大小
转换
指针转引用:把指针用*就可以转换成对象,可以用在引用参数中
引用转指针:把引用类型的对象用&取地址就获得指针
int a = 5; int *p = &a; void fun (int &x){}
3.6野指针
1.野指针指向不可用内存,创建时没有初始化会随机指向
2.free或delete时,没有将指针指向null,因为只释放了内存
3.指针超过了变量的作用范围
避免:
初始化,使用完进行null赋值,
malloc函数分配完内存后需注意:
a. 检查是否分配成功(若分配成功,返回内存的首地址;分配不成功,返回NULL。可以通过if语句来判断)
b. 清空内存中的数据(malloc分配的空间里可能存在垃圾值,用memset或bzero 函数清空内存)
3.7C++中智能指针
智能指针是指向动态对象的指针类,主要有,
unique_ptr C++11引入用来替代auto_ptr,解决不安全问题(auto有拷贝语义,再次访问原对象,会程序崩溃,而unique提供了移动语义)不共享所管理的对象,
shared_ptr 是共享指针,允许多个指针指向同一个对象,除了包括指向对象的指针,还必须包括一个引用计数代理对象的指针
weak_ptr 是配合shared_ptr使用的,观测资源的引用计数,可以用于打破循环引用(比如两个类互相引用),解决内存泄漏的问题
weak_ptr不会增加引用计数,是不能直接访问对象的,访问可以通过lock()函数来创建shared_ptr来引用,shared_ptr能够保证在 shared_ptr 没有被释放之前,其所管理的资源是不会被释放的
4 预处理
4.1 预处理标识符#error
#error预处理指令作用是在编译程序时,只要遇到#error就会生成一个编译错误提示信息,并且停止编译。语法格式为#error error-message。
4.2define和const
define是做文本替换,
1.内存区域不同:define常量的生命周期在编译期,不分配内存,存在于代码段,const常量存在于数据段,并在堆栈中分配空间,可以被调用传递
2.数据类型不同,define常量没有数据类型,const实际存在,并且可以编译器检查
3.define替换方便,但容易出错,const可读性强,便于维护和调试。
4.3typedef 和define区别
两者都是替一个对象取别名,区别在于
1、原理不同;#define是预处理指令,在预处理阶段做简单的字符串替换,没有正确性检查
typedef是关键字,在编译时处理。有正确性检查
#define Pi 3.1415926 typedef int INTEGER INTEGER a,b;
2、功能不同 typedef用于定义类型的别名
#define不只是可以为对象取别名,还可以定义常量/变量,编译开关等
3、作用域不同 #define没有作用域的限制,之前定义过的之后都可以使用
typedef有自己的定义域
4、对指针的操作不同 #define是没有任何意义的替换,仅为字符串替换
typedef有一定含义
#define INTPTR1 int* typedef int* INTPTR2; INTPTR1 p1,p2; -> int* P1,p2; INTPTR2 p3,p4; -> int* P3,*p4;
4.4#include和#include “filename.h”
#include是从标准库路径开始搜索
#include“filename.h”是先从工作路径开始搜索,再从标准库路径开始搜索
4.5头文件作用
1、调用库功能
2、检查类型安全(报错)
4.6 头文件中是否可以定义静态变量
头文件中尽量不要定义变量,会造成空间浪费和程序错误
宏输入两个参数并返回较小的一个
#define MIN(A,B) ((A)<=(B)?(A):(B))
5、变量
5.1全局变量和局部变量区别
1、作用域不同
2、存储方式不同:全局变量(静态全局变量,静态局部变量)分配在全局数据区(静态存储区),后者分配在栈区
3、生命周期不同
4、使用方式不同(全局变量extern就行)
5.2 全局变量是否可以被定义在多个.c文件包含的头文件中,为什么?
可以,在不同的C文件中以static形式来声明同名全局变量
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件对此变量赋初值
5.3局部变量是否可以和全局变量重名
能,局部会屏蔽全局
在函数内部引用这个变量时,会用局部变量
6、函数
6.1怎么可以让函数在main函数之前运行
attribute可以设置函数属性(FUnction Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)
#include
void before() _attribute_((constructor));
void after() _attribute_((destructor));
void before(){
printf("this is function %sn",_func_);
return;
}
void after(){
printf("this is function %sn",_func_);
return;
}
int main(){
printf("this is function %sn",_func_);
return 0;
}
//输出结果
//this is function before
//this is function main
//this is function after
6.2为什么析构函数必须是虚函数
#include
#include“filename.h”是先从工作路径开始搜索,再从标准库路径开始搜索
4.5头文件作用
1、调用库功能
2、检查类型安全(报错)
4.6 头文件中是否可以定义静态变量
头文件中尽量不要定义变量,会造成空间浪费和程序错误
宏输入两个参数并返回较小的一个 #define MIN(A,B) ((A)<=(B)?(A):(B))
5、变量
5.1全局变量和局部变量区别
1、作用域不同
2、存储方式不同:全局变量(静态全局变量,静态局部变量)分配在全局数据区(静态存储区),后者分配在栈区
3、生命周期不同
4、使用方式不同(全局变量extern就行)
5.2 全局变量是否可以被定义在多个.c文件包含的头文件中,为什么?
可以,在不同的C文件中以static形式来声明同名全局变量
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件对此变量赋初值
5.3局部变量是否可以和全局变量重名
能,局部会屏蔽全局
在函数内部引用这个变量时,会用局部变量
6、函数
6.1怎么可以让函数在main函数之前运行
attribute可以设置函数属性(FUnction Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)
#includevoid before() _attribute_((constructor)); void after() _attribute_((destructor)); void before(){ printf("this is function %sn",_func_); return; } void after(){ printf("this is function %sn",_func_); return; } int main(){ printf("this is function %sn",_func_); return 0; } //输出结果 //this is function before //this is function main //this is function after