【C语言】 指针知识
一、指针是什么1.指针的定义由于通过地址能找到所需的变量单元可以说地址指向该变量单元。因此将地址形象化的称为“指针”。但平常我们所说的指针指的是指针变量。int val10; int *p; pval; *p20;2.指针的意义分析上述代码定义一个常量valint*p:表示声明一个指针变量p用于存储变量地址。pval表示p指向变量val存储val的地址。*p20解引用修改该指针指向的值。二、指针与const的结合const int a10; int b20; //定义P指向a const int *pa;//*p具有常性 //p指向的值具有常性 *p30//err int *const qb;//权限缩小 //p指向具有常性 qa;//err *q50;//right指针指向变量时变量不被修改void(const char*arr,conse*char brr) { assert(arrbrr) if(arrbrr) return 0; while(*arr*brr) { if(*arr\0) return 0; *arr; *brr; } return *arr*brr?1:-1; }修饰变量—常变量—变量居有常性特征不可改变的量eg. const double PI3.14 与double const PI3.14完全相同(PI具有常性不可被改变若PI3.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 *pNULL;//0号地址空间不具有访问权限内存前128M禁止访问 *p1; //崩四、指针运算1.指针整型int val 10; int* p val; //*p解析4B空间 *p 解析的内存大小 取决于*p的类型 char num a; char* pn num; *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 | | p1 p2 p3 p4 ---- 所指向的地址位置 int* 通过p访问每个元素值 | *p *(p1) *(p2) *(p3) *(p4) | *arr *(arr1) *(arr2) *(arr3) *(arr4) | arr[0] arr[1] arr[2] arr[3] arr[4] // arrn 地址 等价于 arr[n] // *(arrn) arr[n] //arr[0] 6; // *p 6; // arr[0] p[0] *(p0) p1 能力 指针偏移量 从p所指地址开始 1 指向1*sizeof(*p)位置 类型不变 p1 类型 int*egint mystrlen(const char* str) { int count0; for(int i0;str[i]!\0;i) { count; return 0; } }利用指针更简便实现求字符串长度。4.指针指针--指针是指针指向的内存地址向后移动一个所指数据类型的大小指针--则是向前移动一个所指数据类型的大小并非简单增减1.int arr[]{1,2,3,4,5,6,7,8,9,10}; int *parr2; *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 *paa,*pbb,*pcc; 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 Parr; PLLINT_2 qarr;//此时数组名为类型名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 i0;isize;i) { pa[i]pb[i]; } }int *qval; *q1;q1 ------偏移量4Bvoid*p 存储地址 p1 ----无法知道偏移量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)比较两字符串相等返回0s1s2返回1s1s2返回-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(srcsize-1aim) { mymemcpy(aim,src,size); } char* pendaim(char*)aimsize-1; const char* pendsrc(const char*)srcsize-1; while(size--) { *pendaim--*pendsrc--; } }九、指针与函数1.形参是指针指针作为形参设计 形参修改影响实参必须传递实参的地址解引用void changsize(int *psize) { (*psize); } int main() { int size10; changsize(size); return 0; }2.返回值是指针指针函数局部变量地址禁止返回将arr设计为全局变量int *operate(int a,int b) { int arr[]{ab,a*b};//普通局部变量生存周期函数调用结束销毁 return arr;// 局部变量地址禁止返回 }局部变量地址禁止返回将arr设计为静态变量//将arr设计为全局变量 int arr[2]; void operate(int a,int b) { arr[0]ab; arr[1]a*b; return ab; }//arr设计为静态变量 int *operate(int a,int b) { static int arr[]{ab,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);不能解引用不能加等