指针的初始化:适用于在不确定指针的指向时。相当于int b=0
int a = 10; int* pa = &a; int* p = NULL;//NULL——初始化指针,给指针赋初值
当我们不想使用某个指针但又不想其变成野指针时,就可以将其指向NULL
int a = 10; int* pa = &a; *pa = 20; pa = NULL;//此刻pa不指向a,指向空间已经被释放,将该指针置成空指针。
如果我们对指针pa进行解引用操作,会发生什么情况呢?
在调试过程中,编译器报错pa是nullptr,此时的指针pa是指向Null的,而这个空间是不能被访问的,因为它是一个空指针。
指针的运算:指针和整数的运算,指针和指针的运算,指针的关系运算
通过指针打印数组元素:
#include<stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, * pa,i,sz; pa = arr;//指针pa指向数组arr sz = sizeof(arr) / sizeof(arr[0]); for (i = 0; i < sz; i++) { printf("%d ", *pa);//对指针pa进行解引用 pa++;//指针指向下一个依次进行 } return 0; }
1 2 3 4 5 6 7 8 9 10
同样的,指针的步长不仅可以是1,也可以是2,3,,,,但需要注意的点是,跳转的次数不要超过数组的范围,下面举例说明:
#include<stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, * pa,i,sz; pa = arr; for (i = 0; i < 5; i++)//指针的步长为2,数组中的元素个数是10个,因此i最大值为4, //如果i的值还是等于数组元素的个数此时就会发生报错,原因是越界了 { printf("%d ", *pa); pa+=2; } return 0; }
另外,指针的指向不仅可以是++,也可以是–,实现方法就是让指针初始指向发生改变,下面举例说明:
#include<stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, * pa,i; pa = &arr[9];//&不要忘记加 for (i = 0; i <5; i++) { printf("%d ", *pa); pa-=2; } return 0; }
10 8 6 4 2
指针-指针:大地址—小地址得到元素个数,小地址—大地址的绝对值是元素个数
举例:
#include<stdio.h> int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, * pa,i; pa = arr; printf("%d\n", &arr[9] - &arr[2]);//大地址-小地址 printf("%d\n", &arr[5] - &arr[1]); printf("%d\n", &arr[2] - &arr[9]);//小地址-大地址 printf("%d\n", &arr[1] - &arr[7]); return 0; }
7 4 -7 -6
注意:指针-指针适用于指向同一个数组的指针,而不能用于字符型和整形指针之间,由于字符型指针的步长为1,而整形为4,两者类型不兼容,无法进行相减。
使用指针计算字符串长度:
#include<stdio.h> int my_strlen(char* str) { char* start = str; char* end = str; while (*end != '\0')//使指针end指向字符串末位置 { end++; } return end - start;//使用大地址减小地址,返回字符串长度 } int main() { char arr[] = "good"; int len = my_strlen(arr); printf("%d", len); return 0; }
4
指针的关系运算:比较指针的大小
方法一:
#define N_VALUES 5 float values[N_VALUES]; float* vp; for (vp = &values[N_VALUES]; vp > &values[0];)//如果满足后一个指针大于初始值指针,则将其赋值为0,再指向上一个,依次进行 { *--vp = 0; }
实现过程:指针的初始位置是下标为5的元素,判断该地址大于元素首地址,进入循环,先进行vp–,使它指向上一个元素,再将其赋值为0,直到指向首元素的地址,结束循环。
方法二:
#define N_VALUES 5 float values[N_VALUES]; float* vp; for (vp = &values[N_VALUES-1]; vp >= &values[0];vp--) { *vp = 0; }
实现过程:指针初始指向下标为4的元素的地址,判断该地址大于首元素地址,进入循环,将该地址中值赋值为0,再指向上一个元素,直到不满足vp>=&values[0],结束循环。
第二种方法在绝大部分的编译器上是可以顺利完成,但是我们应该还是避免使用第二种方法,而推荐第一种写法,因为标准并不保证它可行。
标准规定: 允许指向数组的指针与指向数组最后一个元素的那个内存位置的指针比较,但是不允许,与指向第一个元素之前的那个内存位置的指针进行比较。
指针和数组:
通常情况下,数组名表示数组首元素地址,但是在两种特殊的情况下,数组名不是首元素地址。
1:&数组名,此时数组名不是首元素的地址,数组名表示整个数组,&数组名取出的是整个数组的地址。
2:sizeof(数组名),此时,数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小
举例:
#include<stdio.h> int main() { int arr[10] = { 0 }; printf("%p\n", arr); printf("%p\n", &arr[0]); printf("%p\n", &arr); }
通过上面的输出结果,我们发现这三种形式输出的结果是数值完全一样的,那么&数组名和其他两种表现形式的差别在哪里呢?
我们可以通过给他们地址加一,再来看输出结果。
#include<stdio.h> int main() { int arr[10] = { 0 }; printf("%p\n", arr); printf("%p\n", arr+1);//在arr地址的基础上增加了4个字节的大小,也就是arr[1]的地址 printf("%p\n", &arr[0]); printf("%p\n", &arr[0]+1);//在arr[0]地址的基础上增加了4个字节的大小,也就是arr[1]的地址 printf("%p\n", &arr); printf("%p\n", &arr+1);//在&arr地址的基础上增加了40个字节,也就是直接跳到了下一个数组的首地址 }
可见&数组名和数组首元素地址以及数组名,只是在数值上都与数组的首地址相等,但含义是不一样的。
通过指针访问数组元素:
#include<stdio.h> int main() { int arr[10] = { 0 },i; int* p = arr; for (i = 0; i < 10; i++) { printf("%p======%p\n", p + i, &arr[i]);//p+i是通过指针打印数组元素地址,&arr[i]是通过下标打印数组元素地址 } return 0; }
通过输出结果我们不难发现,无论使用指针还是下标打印数组元素地址,其结果是相同的。
在数组中使用指针访问数组元素:
#include<stdio.h> int main() { int arr[10] = { 0 },i; int* p = arr; for (i = 0; i < 10; i++) { *(p + i) = i;//对指针进行解引用 printf("%d ", arr[i]);//printf("%d ",*(p+i))这两种输出方式都可以 } return 0;
}
0 1 2 3 4 5 6 7 8 9
二级指针,三级指针的定义和使用:n级指针解引用n次。
#include<stdio.h> int main() { int arr[10] = { 0 }; int* p = arr;//p为一级指针(*代表p是指针,int代表p指向的类型) int* * pa = &p;//pa为二级指针(*代表pa是指针,int*代表pa指向的类型) int** * ppa = &pa;//ppa为三级指针(*代表ppa是指针,int**代表ppa指向的类型) printf("%d\n", *p); printf("%d\n", **pa); printf("%d\n", ***ppa); return 0; }
10 10 10
指针数组和数组指针:指针数组本质是数组(存放指针的数组),数组指针本质是指针
指针数组的定义和使用:
#include<stdio.h> int main() { int arr[10];//整型数组,数组中的元素都是整型 int a = 10, b = 20, c = 30,i; int* arr1[3] = { &a,& b,&c };//指针数组,数组中的元素都是指针 for (i = 0; i < 3; i++) { printf("%d ", *(arr1[i]));//对数组中的指针依次进行解引用 } return 0; }
10 20 30