一、指针是什么
1.指针的定义
由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。但平常我们所说的指针指的是指针变量。
int val=10; int *p; p=&val; *p=20;2.指针的意义
分析上述代码:
定义一个常量val
int*p:表示声明一个指针变量p用于存储变量地址。
p=&val:表示p指向变量val,存储val的地址。
*p=20:解引用,修改该指针指向的值。
二、指针与const的结合
const int a=10; int b=20; //定义P指向a const int *p=&a;//*p具有常性 //p指向的值具有常性 *p=30//err int *const q=&b;//权限缩小 //p指向具有常性 q=&a;//err *q=50;//right指针指向变量时,变量不被修改
void(const char*arr,conse*char brr) { assert(arr&&brr) if(arr==brr) return 0; while(*arr==*brr) { if(*arr=='\0') return 0; *arr++; *brr++; } return *arr>*brr?1:-1; }修饰变量—>常变量—>变量居有常性特征(不可改变的量)
eg. const double PI=3.14 与double const PI=3.14完全相同(PI具有常性,不可被改变)
若PI=3.24//err
三、野指针与空指针
1.野指针与空指针定义
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的),指向一个非法的或已销毁的内存的指针,对系统造成不可预知的危险错误。也叫悬挂指针、空悬指针。
每一个指针类型都有一个特殊的值-----“空指针”。空指针与同类型的其他指针值都不同,它“保证与任何对象或函数的指针值都不相等”,也就是说空指针不会指向任何地方,它不是任何对象或函数的地址。简单点说,一个指针不指向任何数据,我们就称之为空指针,空指针用NULL表示。
2.造成野指针与空指针的原因
1.)指针未初始化
int main() { char*p; return 0; }没有对指针p进行初始化
2.)指针越界访问
int main() { int arr[10] = {0}; int i = 0; int* p = arr; for (i = 0; i <= 10; i++) { *p = i; p++; } return 0; }因为这里非法访问内存的同时试图改变空间的值,所以程序崩溃.
3.)指针指向的空间已经释放
char* fun() { char arr[] = "abc"; return arr; } int main() { char* p = fun(); printf("%s\n",p); return 0; } AI运行代码 c运行上述代码,有以下警告:
虽然程序没有崩溃,但是这种写法是非法的,arr是局部变量,函数调用结束,栈帧销毁,局部变量空间归还给操作系统,我们没有使用权限,此时p就是野指针,*p属于非法访问内存。
//空指针 NULL
#define NULL 0; int *p=NULL;//0号地址空间不具有访问权限,(内存前128M禁止访问) *p=1; //崩四、指针运算
1.指针+整型
int val = 10; int* p = &val; //*p解析4B空间 *p 解析的内存大小 取决于*p的类型 char num = 'a'; char* pn = # *pn -- 解析 1字节内存空间 *pn类型 *pn = 'b';2.数据在内存中的存储方式
int val = 0x12345678 存储模式: 大端模式(大端字节序): 高位数据存储低地址位置(高低/低高) 低地址 高地址 0x12 0x34 0x56 0x78 --- > 0x12345678 小端模式(小端字节系): 低地址位置存储低位数据(低低/高高) 低地址 高地址 0x78 0x56 0x34 0x12 // ---> 0x12345678大端模式,指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。
小端模式,指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
验证大小端模式:
int val = 0x12345678; char* p = (char*)&val; // *p --> 0x12345678 if(*p == 0x78) 小 else 大端3.指针+1
指针加1表示将指针的值增加1个单位。
int arr[] = {1,2,3,4,5}; int* p = arr; //数组名数组首元素地址 &arr[0] --- int* [1] [2] [3] [4] [5] | | p | | p+1 p+2 p+3 p+4 ---- 所指向的地址位置 int* 通过p访问每个元素值 | *p *(p+1) *(p+2) *(p+3) *(p+4) | *arr *(arr+1) *(arr+2) *(arr+3) *(arr+4) | arr[0] arr[1] arr[2] arr[3] arr[4] // arr+n 地址 等价于 &arr[n] // *(arr+n) arr[n] //arr[0] = 6; // *p = 6; // arr[0] p[0] *(p+0) p+1 能力 指针偏移量 从p所指地址开始 +1 指向1*sizeof(*p)位置 类型不变 p+1 类型 int*eg:
int mystrlen(const char* str) { int count=0; for(int i=0;str[i]!='\0';i++) { count++; return 0; } }利用指针更简便实现求字符串长度。
4.指针++,指针--
指针++是指针指向的内存地址向后移动一个所指数据类型的大小,指针--则是向前移动一个所指数据类型的大小,并非简单增减1.
int arr[]={1,2,3,4,5,6,7,8,9,10}; int *p=arr+2; *p++ ---->3 //p后置++先与其他运算符结合再自增1,值为3,指向4; *++p ---->5 //p前置++先自增1,再与其他运算符结合 指向5,值为5; ++(*p) ----> 6 //等价于 ++arr[4] (*p)++ ---->arr[4]++ 6五、指针与数组
1.指针数组
元素为指针的数组 核心格式: 数据类型 数组名[数组长度]
int a,b,c; int *pa=&a,*pb=&b,*pc=&c; int *arr[]={&a,&b,&c}; const char* srr[]={"hello","abc","def"};1.指针数组,是一维数组,每个元素都是int *;
2.指针数组经常结合二维数组使用,存储每行首元素的地址;
3.指针数组的本质是数组,这个数组中存储内容是指针;
4.定义 int *p[n];,[ ] 优先级高,先与 p 结合成为一个数组,再由 int*说明这是一个整型指针数组,它有 n 个指针类型的数组元素。
typedef long long(*PLLINT_2)[2]; PLLINT_2 P=&arr; PLLINT_2 q=&arr;//此时数组名为类型名2.数组指针
指向整个数组的指针
核心格式:*数据类型(指针名)[数组长度];()优先级高强制让*先结合指针名,明确其为指针;
六、void*
x86下占四个字节
无类型指针,可以指向任何数据类型的内存地址,但无法直接解引用或进行指针运算。
//brr拷贝到arr void Copy(void* arr,const void* brr,size_t size) { char* pa=(char *)arr;//强转为char* const char* pb=(const char *)brr; for(int i=0;i<size;i++) { pa[i]=pb[i]; } }int *q=&val; *q=1;q+1 ------>偏移量4B
void*p; 存储地址 p+1 ---->无法知道偏移量,void所占字节数不知;
void*变量名=任意类型的地址;
函数指针 不能解引用,不能加等
七、str系列函数
1.strlen求字符串长度,返回‘\0’前的字符个数(不含‘\0’);
2. strcpy(char*arr, const char *brr):将src指向的字符串复制到dest,需保证dest空间足够,会复制'\0'。
3. strcat(char *arr, const char *brr):将src指向的字符串追加到dest末尾,dest需有足够空间,会合并'\0'。
4. strcmp(const char *s1, const char *s2):比较两字符串,相等返回0;s1>s2返回1;s1<s2返回-1(按ASCII值比较)。
八、mem系列函数
1.memcpy 不考虑内存重叠问题 前提条件:另个内存独立
void mymemcpy(void* aim,void* src,size_t size) { char* paim=(char*)aim; const char*psrc=(const char*) src; while(size--) { *paim++=*src++; } }2.memmove 考虑内存重叠问题 指针判断是否有重叠
void mymemove(void*aim,const void*src,size_t size) { if(src+size-1<aim) { mymemcpy(aim,src,size); } char* pendaim=(char*)aim+size-1; const char* pendsrc=(const char*)src+size-1; while(size--) { *pendaim--=*pendsrc--; } }九、指针与函数
1.形参是指针
指针作为形参设计 ,形参修改影响实参,必须传递实参的地址,解引用
void changsize(int *psize) { (*psize)++; } int main() { int size=10; changsize(&size); return 0; }2.返回值是指针(指针函数)
局部变量地址禁止返回,将arr设计为全局变量
int *operate(int a,int b) { int arr[]={a+b,a*b};//普通局部变量生存周期函数调用结束销毁 return arr;// 局部变量地址禁止返回 }局部变量地址禁止返回,将arr设计为静态变量
//将arr设计为全局变量 int arr[2]; void operate(int a,int b) { arr[0]=a+b; arr[1]=a*b; return a+b; }//arr设计为静态变量 int *operate(int a,int b) { static int arr[]={a+b,a*b};//延长局部变量的生存周期 该局部变量前方+static return arr; }3.函数指针
是个指针,指向一个函数
long* funn1(){} //定义指针 指向funn1并完成函数调用 long*(*p1)()=funn1; p1();调用funn1函数void (*signal(int sig, void (*func)(int)))(int); typedef void (*PFun)(int); PFun signal(int sig,PFun pfun); 调用: void func1(int a){ 如何复制操作 } void func2(int b){ 如何粘贴操作 } signal(CTRLC, func1); signal(CTRLV,func2);不能解引用,不能加等