指针
指针:数据类型,地址
概念:为了方便访问内存内容,系统给每一个内存单元(字节)编号该编号称为地址。也就是指针
指针定义: 类型 * 指针变量名 (类型:指针变量,保存的地址,所对应的变量的类型)
* 没有实际意义,就是说明定义的是一个指针
内存大小:4个字节
int *p p 类型是int* (int型指针),p指向的类型是int型
int**p p 的类型是int** (int 二级指针),p指向的类型是int * 型
指针变量有自己独立的内存空间,其保存别人的内存地址
指针运算符:& 取地址符 &变量名 *取内容(解析引用符),取地址中的内容
* 指针变量名 等价于指针指向的变量,读取数据时,读取几个字节,看指针指向的类型
指针变量赋值方式:1.相同类型变量的地址2.相同类型的指针变量3.字符串4.数组名
p = &a
&p :p自己的地址
p:a的地址
*p 等价于a
直接访问:通过变量名访问, 间接访问:通过地址访问
int a = 10;
cout <<&a <<endl;// 获取a的地址
int * p = &a ;
cout<< &p <<endl; // 获取指针变量自己的地址
cout<< *p <<endl;//获取指针变量保存的地址 xp = x&a = 10
指针偏移
指针 +/ - 整数
p +1 , p - 1 (1表示1个单位),1个单位占几个字节,看指针类型
int n = 0x01 a1 02 a2;
short *pshort = (short *)&n;
pshort = pshort + 1;// pshort = 偏移2个字节
cout<<*pshort<<endl;//01a1
char c[10] = "abcdef" ;
int *pint = (int *) &c[0];
pint ++; // 指针偏移 (int型偏移4个字节,指到第五个元素地址)
cout<<(char*) pint <<endl;//ef
// 通过指针的偏移,遍历数组
int n[5] = {1,2,3,4,5};
int *p = n;//等价于&n[0];
for (int i =0;i<5 ; i++)
{
cout << *(p+i) <<endl;
}
for (int i =0;i<5 ; i++)
{
cout << *p++ <<endl; //++ 优先级大于*
*n++ // 错误,n为数组名常量
}
//指针变量通过下标形式防访问数组元素·
for (int i =0;i<5 ; i++)
{
cout << p[i] <<endl; //等价于 *(p+i)
}
void *p void型指针,可以指向任何类型 (*p ,.p +1 不允许)
p = nullptr
int *py = null;
=》 py = 0 =》 py = null; //0地址
0地址中不能存储数据 ,*py 不能使用
指针数组 , 数组指针 ,二级指针
指针数组:数组中每一个元素都是指针类型
数组指针:指向数组的指针
二级指针:指向指针的指针
指针数组的定义:定义一个int型的指针数组,有五个元素,每个元素都是int *
int *pn[5]
数组指针的定义:指向一个类型是int 大小为5的一维数组
int n[5]
int (*pn)[5] =&n
题例子:
//数组指针:数组类型(*p) 数组大小
short *(*pshort)[3][4][5];
cout << sizeof(pshort) <<endl;//4
cout << pshort + 1 <<endl;//4 *3 * 4 * 5 = 240
cout << sizeof(*pshort) <<endl;//240 三维数组的大小
cout << sizeof(**pshort) <<endl;//4*4*5 =80 二维数组的大小
cout << sizeof(***pshort) <<endl;//4*5 =20 一维数组
cout << sizeof(****pshort) <<endl;//4 元祖大小(short * )
cout << sizeof(*****pshort) <<endl;//2 (short)
double **(**pp[3][4])[5][6][7];
cout << sizeof(pp) <<endl;//4*3*4 = 48 3行4列二维数组,每一个元素是二级指针
cout << sizeof(*pp) <<endl;//4*4 = 16 大小为一的一维数组,每一个元素是二级指针
cout << sizeof(**pp) <<endl;//4 二级指针
cout << sizeof(***pp) <<endl;//4 一级指针(数组指针)
cout << sizeof(****pp) <<endl;//4*5*6*7 =840 指向数组的大小(三维数组,每个元素2级指针))
cout << sizeof(*****pp) <<endl;//4*6*7 =168 二维数组
cout << sizeof(******pp) <<endl;//4*7 =28 一维数组
cout << sizeof(*******pp) <<endl;//4 double **
cout << sizeof(********pp) <<endl;//4 double *
cout << sizeof(*********pp) <<endl;//8 double
指针 与 const
const 修饰的变量必须初始化
指针常量:Const 修饰指针本身const 在* 后
1.必须初始化
2.指针不能改变指向
3.可以通过指针修改指向内存中的内容
常量指针:const 修饰指针指向的类型 const在* 前
1.可以不初始化
2.可以改变指向,并且可以指向普通类型(没有const 修饰)的变量
3.不能通过指针修改指针指向的内存中的内容
只读类型指针常量
1.必须初始化
2.指针不能改变指向
3.不能通过指针修改指针指向的内存中的内容
例子:
//只读类型的变量n
const int n = 100;
int const m = 10;
//const 修饰的指针指向的内容 *p ,常量指针(只读类型的指针)
const int *p;
int const *p1;
//const 修饰的指针变量本身 p2指针常量
int x =100 ;
int * const p2 = &x;
p = &m;
p = &x;//常量指针可以改变指向,并且可以指向普通变量
// *p = 100; 错误,不能通过常量指针修改指向内存中的内容
x = 200;
// p2 = &x; 错误,指针常量,不能改变指针指向
*p2 = 100;
//只读类型的指针(说白了就是把常量指针,和指针常量结合在一起使用)
const int * const p3 = &x;
指针与内存
作用域:当前语块, 文件作用域,项目作用域
内存区域的划分:1.静态全局区,2.栈区(stack)3.堆区(head)4.常量区
1.静态全局区:静态变量和全局变量 。编译时由系统自动分配内存(并且默认的初始值,只初始化一次),程序结束后由系统自动回收
2.栈区:普通局部变量。函数调用时,系统临时分配内存,函数调用结束后,系统自动回收内存
3.堆区:有程序员自己手动申请(c语言:malloc ,c++:new) 由程序自动释放 (c:free,c++ delete)
4.常量区:字符串,不能修改
静态变量:定义时static 修饰变量
全局变量:定义在函数外变量
局部变量:定义函数内的变量
-----------------------------------------------------------------------------------------------------
静态局部变量 和 普通局部变量的异同
1.作用域范围相同,当前语块
2.生存周期不相同,静态局部变量在 静态全局区,普通局部变量在栈区
静态全局变量 和 普通全局变量的异同
1.生存周期相同,都在静态全局区
2.作用域范围不同,普通全局变量是项目作用域(在其他文件使用时,必须先声明 extarn),静态全局变量是文件作用域
堆区:内存泄漏:申请的内存,没有释放。(结果死机,蓝屏)
野指针:内存释放后,指针没有置空,再去操作该内存时,该指针为野指针,(结果:程序崩溃)
c++ 引用 :数据地址
引用:数据类型 ,给变量取别名
类型& 引用名 = 变量名 必须初始化
指针与引用的区别:
1.引用必须初始化。指针可以不初始化
2.指针可以为空,引用不能为空
3.指针有自己独立的内存空间,引用和引用变量内存相同
4.指针可以改变指向,引用不能改变被引用变量
typedef 类型名 , 新名字 给类型取别名
typedef int INT;
int main (INT a;)// 定义一个int型变量a
指针注意事项:
1.定义指针时最好初始化,如果不能明确指针指向,指向0地址
2.使用指针时,先判断是否为空
int*p = nullptr
if(p != nullptr) {*p = 100}
3.使用数组时,注意数组越界
4.堆区申请内存,必须先释放,再置空。
函数
函数 : 定义,声明,调用
void test (int n) {return n;}//定义
int main(){test} // 调用
函数参数
参数缺省:声明或定义函数可以给形参一个缺省值
缺省顺序:从右往左,传参顺序是从左往右
c++ 函数重载:函数名相同,参数不同(个数,类型,顺序)《c语言函数名字必须不一样》
二义性:1.参数类型不匹配:解决:明确实参类型
2.参数缺省,解决:参数不缺省
3.继承时,产生二义性:通过类名加以限定
参数传参的方式:1.值传递,2址传递,3.引用传递
assert(),断言,用于错误检测
数组作为函数参数
1.数组名(形实)2.数组的元素(实参)
当数组名作为形参时,弱化成指针类型,必须添加一个形参表表示数组大小
void printArray (int n[4][5])
宏定义
宏定义:宏替换
#define 宏名
#define PI 3.14;// 预编译处理
int main{printf(pI*2)}
带参宏 #define M(x) x*x;
c语言:函数代码少,使用比较频繁,使用宏定义
c++ inline 内联函数(不能使用条件:函数代码过长,函数代码中有循环或递归)
递归
递归,函数直接或间接调用自己
1.规律2.找出跳出条件
1.1到n递增
int sum(int n)
{
int s =0 ;
if (1==n)
{
s = 1;
}else
{
s = sum(n-1)+n
}
return s;
}
未完》》》》