指针初阶
指针是什么
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思hi是通过它能找到以它为内存的内存单元。
1 2 3 4 5 6 7
| int main() { int a=10; int *pa = &a; *pa = 20; return 0; }
|
存放地址的变量被叫做指针变量,地址就是指针。
指针和指针类型
1 2 3 4 5 6 7 8 9 10
| int main() { int* pa; char* pc; float* pf; printf("%d\n",sizeof(pa)); printf("%d\n",sizeof(pc)); printf("%d\n",sizeof(pf)); }
|
结果是一样的,虽然所占空间是一致的,但是没有万用指针类型,因此指针类型还是有实际意义的。
指针类型的意义
1 2 3 4 5 6 7 8 9 10
| int main() { int a = 0x11223344; int*pa =&a; *pa=0; char*pc =&a; *pc=0; return 0; }
|
1 2 3 4 5 6 7 8 9 10 11
| int main() { int arr[10]={0}; int *p=arr; char*pc=arr; printf("%p\n",p); printf("%p\n",p+1); printf("%p\n",pc); printf("%p\n",pc+1); return 0; }
|
结果发现,字符指针+1相当于跳过一个字符,因此地址增加一个字节,而整形指针+1相当于跳过一个整型,因此地址增加4个字节
总结
1.指针类型决定了:指针解引用有的权限有多大
2.指针类型决定了:指针走一步,能走多远(步长)
1 2 3 4 5 6 7 8 9 10 11 12
| int main() { int arr[10]={0}; int *p=arr; int i=0; for(i=0;i<10;i++) { *(p+i)=1; } return 0; }
|
野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针的成因
指针未初始化
1 2 3 4 5 6
| int main() { int *p; *p =20; return 0; }
|
指针越界访问
1 2 3 4 5 6 7 8 9 10 11 12
| int main() { int a[10]={0}; int *p=a; int i=0; for(i=0;i<=10;i++) { *p=i; p++; } return 0; }
|
指针指向的空间释放
1 2 3 4 5 6 7 8 9 10 11
| int *test() { int a=10; return &a } int main() { int *p=test(); 8p=20; return 0; }
|
如何规避野指针
1.指针初始化
1 2 3 4 5 6 7 8 9 10 11
| #include <stdio.h> int main() { int *p = NULL; int a=10; int *ptr =&a; return 0; }
|
2.避免指针越界(C语言本身是不会检查数数据的越界行为的)
3.指针指向的空间释放之后及时指向为空指针(NULL)
4.使用指针之前,要检查其有效性
1 2 3 4 5
| int *p =NULL; if(p!=NUll) { *p=10; }
|
一个指针变量当你不知道会指向什么地方的时候,初始化为空指针,当它指向的空间被释放掉之后,也指向到空指针,当它指向一个有效空间时,就给它一个有效的地址。因此指针就要么是一个空指针,要么就是一个有效的指针。
指针运算
指针+-整数
1 2 3 4 5 6 7 8 9 10 11 12
| #include <stdio.h> #define N_VALUES 5 int main() { float values [N_VALUES]; float*p; for(vp=&values[0];vp<&values[N_VALUES];) { *vp++; } return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12
| int main() { int a[10]={1,2,3,4,5,6,7,8,9,10}; int *p =a; int *pend =a+9; while(p<=pend) { printf("%d\n",*p); p++; } return 0; }
|
指针-指针
1 2 3 4 5
| Int main() { int arr[10]={1,2,3,4,5,6,7,8,9,10}; printf("%d\n",arr[9]-arr[10]) }
|
答案是9。
因此我们发现指针-指针得到的是两个指针中间相差的元素个数
但是指针相减有个前提是两个指针指向同一块空间
strlen()求字符串
1 2 3 4 5 6 7
| #include <stdio.h> int main() { int len = strlen("abc"); printf("%d",len); return 0; }
|
手搓strlen()的三种形式
指针-指针
1 2 3 4 5 6 7 8 9
| int my_strlen(char*str) { int *start = str; while(str!=0) { str++; } return start-str; }
|
建立临时变量
1 2 3 4 5 6 7 8 9 10
| int my_strlrn(char*str) { int cont=0; while(*str!-'\0') { cont++; str++; } return cont; }
|
函数递归
1 2 3 4 5 6 7 8 9 10 11
| int my_strlen(char* str) { if(*str != '\0') { return 1+(my_strlen{str+1}) } else { return 0; } }
|
指针的关系运算
1 2 3 4 5
| #define N_VALUES 5 for(vp = &values[N_VALUES];vp> &values[0];) { *--vp=0; }
|
1 2 3 4
| for(vp=&values[N_VALUES-1];vp>&values;vp--) { *vp=0; }
|
这两种关系运算,虽然都可以达到目的,但还是要尽可能的避免使用后一种写法,因为标准并不保证其的可行性。
标准规定
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
指针和数组
数组名是什么?
1 2 3 4 5
| int main() { int arr[10]={0} printf("%p",arr); }
|
1 2 3 4 5 6 7
| int main() { int arr[10]={0}; int* p = arr; return 0; }
|
能写成2[arr]是因为[]是一个操作符,而2和arr是两个操作数,而当计算机识别时
1 2
| arr[2] --> *(arr+2) 2[arr] --> *(2+arr)
|
二级指针
1 2 3 4 5 6 7 8 9 10
| int main() { int a=10; int* pa =&a; pa; int* *ppa =&pa; return 0; }
|
指针数组
存放指针的数组
1 2 3 4 5 6
| int main() { int arr[10]; char ch[10]; int* parr[10]; }
|