C语言中的指针 初阶
1.指针是什么
初学者都有一个疑问,那就是指针是什么?简单的说,就是通过它能找到以它为地址的内存单元。
地址指向了一个确定的内存空间,所以地址形象的被称为指针。
int main() { int a = 10; int* pa = &a; return 0; } //pa是用来存放地址(指针),所以pa是指针变量。
总结:指针就是变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
地址是唯一标识一块空间的。
指针的大小在32位平台是4个字节,在64位平台是8个字节。
2.指针和指针类型
我们知道变量有不同的类型(整型、浮点型、字符型等),其实指针也是有不同类型的。
指针类型的意义1:
指针类型决定了指针解引用操作的时候,一次访问几个字节(访问内存的大小)
char*
指针解引用访问1个字节
int*
指针解引用访问四个字节
int main() { char* pc = &a; *pc = 0; return 0; }
指针类型的意义2:
指针类型决定了,指针±整数的时候的步长(指针±整数的时候,跳过几个字节)
int*
指针+1 跳过四个字节
char*
指针+1 跳过一个字节
int main() { int a = 10; int* pa = &a; char* pc = &a; printf("%p\n", pa); printf("%p\n", pc); printf("%p\n", pa+1); printf("%p\n", pc+1); return 0; }
3.野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
3.1野指针成因
指针未初始化
int main() { int* p;//局部变量指针未初始化,默认为随机值 *p = 20;//通过p中存的随机值作为地址,找到一个空间,这个空间不属于我们当前的程序 //就造成了非法访问,p就是野指针 return 0; }
指针越界访问
int main() { int arr[10] = 0; int i = 0; int* p = arr; for(i =0; i <= 10; i++) { *p = i; p++;//当指针指向的范围超出数组arr的范围时,p就是野指针 } return 0; }
指针指向的空间释放
int* test() { int a = 10; return &a; } int main() { int* p = test(); printf("%d\n",*p); return 0; }
3.2如何规避野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放即使置
NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
int main() { int a = 10; int* p = &a;//明确地初始化,确定指向 int* p2 = NULL;//不知道一个指针当前应该指向哪里时,可以初始化为NULL return 0; }
4.指针的运算
4.1指针±整数
#define N_VALUES 5 float values[N_VALUES]; float* vp; for(vp = &values[0]; vp < &values[N_VALUES];) { *vp++ = 0; } int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,0}; int* p = &arr[9]; printf("%p\n",p); printf("%p\n",p-2); return 0; }
4.2指针-指针
指针-指针 得到的数字的绝对值是指针和指针之间元素的个数
int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,0 }; printf("%d\n", &arr[9] - &arr[0]); printf("%d\n", &arr[0] - &arr[9]); return 0; }
指针-指针 的前提是两个指针指向同一块区域
int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,0}; char ch[5] = {0}; printf("%d\n",&arr[9] - &ch[0]);//err return 0; }
应用 求字符串长度
int my_strlen(char* s) { int count = 0; char* start = s; while(*s!='\0') { s++; } return s - start; } int main() { char arr[] = "abcdef"; int len = my_strlen(arr); printf("%d\n", len); return 0; }
4.3指针的关系运算
#define N_VALUES 5 float values[N_VALUES]; float *vp; for(vp = &values[N_VALUES]; vp > &values[0];) { *--vp = 0; }
上述程序也可以写成这样
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--) { *vp = 0; }
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这么写,因为标准并不保证它可行。
标准规定
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
5.指针和数组
数组 - 是一块连续的空间,放的是相同类型的元素
数组大小和元素类型,元素个数有关系
指针(变量) - 是一个变量,放地址
指针变量的大小 是4(32bit)/8(64bit)
个byte
数组名确实是首元素地址
但是有两个例外:
- sizeof(数组名) - 这里的数组名不是首元素的地址,是表示整个数组的,这里计算的是整个数组的大小,单位还是字节。
- &数组名 - 这里的数组名不是首元素的地址,是表示整个数组的,拿到的是整个数组的地址
int main() { int arr[10] = { 0 }; int sz = sizeof(arr); printf("%d\n", sz); return 0; }
int main() { int arr[10] = { 0 }; int* p = arr; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); for (i = 0;i < sz;i++) { *(p + i) = i; } for (i = 0;i < sz;i++) { printf("%d ", *(p + i)); } return 0; }
6.二级指针
我们都知道,指针变量是变量,是变量就有地址,那么指针变量的地址存放在哪里呢?
这就是我们要了解的二级指针。
int main() { int a = 10; int* p = &a; int** pp = &p;//pp就是二级指针 **pp = 20; printf("%d\n", a);//a = 20 return 0; }
7.指针数组
从名字上来看,大家觉得指针数组是指针还是数组?
答案是数组,是存放指针的数组。
整型数组 - 存放整型的数组就是整型数组
字符数组 - 存放字符的数组就是字符数组
指针数组 - 存放指针的数组就是指针数组
int*
整型指针的数组
char*
字符指针的数组
int main() { int arr[10]; char ch[5]; int* parr[5]; char* pc[6]; return 0; } int main() { int a = 10; int b = 20; int c = 30; int* parr[3] = { &a,&b,&c }; for (int i = 0;i < 3;i++) { printf("%d\n", *(parr[i])); } return 0; }