四、指针运算
4.1 指针+-整数
指针加+-运算用于将指针移动指定的偏移量,以便访问其他地址处的数据。例如,如果有一个指向整型数组的指针p,可以使用p+n或p-n将指针移动n个整型长度的位置。
#define N_VALUES 5 float values[N_VALUES]; float *vp; //指针+-整数;指针的关系运算 for (vp = &values[0]; vp < &values[N_VALUES];) { *vp++ = 0; }
4.2 指针-指针
可以对两个指针进行减法运算,得到它们之间的距离(以元素个数为单位)。这在处理数组或动态内存分配时非常有用。例如,假设有两个int类型的指针ptr1和ptr2,我们可以进行如下操作:
int distance = ptr2 - ptr1; // 计算ptr2和ptr1之间的距离(以int元素个数为单位)
需要注意的是,指针运算应该谨慎使用,确保指针指向的内存是有效的。否则,可能会导致未定义行为或内存错误。此外,不同类型的指针之间不应进行直接算术运算,因为它们可能引用不同大小的数据。只有在指向同一数组的元素或有效分配的内存区域时,指针之间的运算才是有效的。
4.3 指针的关系运算
在C、C++等编程语言中,指针的关系运算用于比较指针的地址或者判断指针是否为NULL。下面是指针的关系运算符及其含义:
📌相等(==):用于判断两个指针是否指向同一个内存地址。如果两个指针指向相同的地址,则关系表达式为真,否则为假。
1. int* ptr1; 2. int* ptr2; 3. 4. if (ptr1 == ptr2) { 5. // 指针ptr1和ptr2指向相同的地址 6. }
📌不相等(!=):用于判断两个指针是否指向不同的内存地址。如果两个指针指向不同的地址,则关系表达式为真,否则为假。
int* ptr1; int* ptr2; if (ptr1 != ptr2) { // 指针ptr1和ptr2指向不同的地址 }
📌大于(>)、小于(<)、大于等于(>=)、小于等于(<=):这些关系运算符用于比较两个指针所指向的地址之间的顺序关系。只有当指针指向同一个数组中的元素或者在有效的内存范围内时,这些关系运算符才有定义。
int* ptr1; int* ptr2; if (ptr1 > ptr2) { // ptr1指向的地址在ptr2指向的地址之后 } if (ptr1 < ptr2) { // ptr1指向的地址在ptr2指向的地址之前 } if (ptr1 >= ptr2) { // ptr1指向的地址在ptr2指向的地址之后或相同 } if (ptr1 <= ptr2) { // ptr1指向的地址在ptr2指向的地址之前或相同 }
📌空指针检查:指针的关系运算还可以用于检查指针是否为空(指向NULL)。在C和C++中,NULL是一个预定义的宏,用于表示一个空指针。
int* ptr = NULL; if (ptr == NULL) { // 指针ptr是空指针 } if (ptr != NULL) { // 指针ptr不是空指针 }
需要注意的是,对于关系运算符(>, <, >=, <=),只有在指针指向同一数组的元素或有效分配的内存区域时,比较的结果才是有效的。在进行指针比较之前,最好确保指针指向的内存是有效的,否则可能会导致未定义行为。
五、 指针和数组
我们来看一个例子:
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,0}; printf("%p\n", arr); printf("%p\n", &arr[0]); return 0; }
运行结果:
可见数组名和数组首元素的地址是一样的。
结论:数组名表示的是数组首元素的地址。(一般情况下)
那么这样写代码是可行的:
int arr[10] = {1,2,3,4,5,6,7,8,9,0}; int *p = arr;//p存放的是数组首元素的地址
既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问一个就成为可能。
例如:
#include <stdio.h> int main() { int arr[] = {1,2,3,4,5,6,7,8,9,0}; int *p = arr; //指针存放数组首元素的地址 int sz = sizeof(arr)/sizeof(arr[0]); for(int i=0; i<sz; i++) { printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p+i); } return 0; }
运行结果:
所以 p+i 其实计算的是数组 arr 下标为i的地址。
那我们就可以直接通过指针来访问数组。
如下:
#include<stdio.h> int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; int *p = arr; //指针存放数组首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; for (i = 0; i<sz; i++) { printf("%d ", *(p + i)); } return 0; }
运行结果:
六、二级指针
二级指针(double pointer)是指指向指针的指针。我们可以使用指向指针的指针来处理指针的引用或修改。它们在一些情况下非常有用,特别是在涉及到函数参数传递和动态内存分配等方面。
定义二级指针的语法为在指针类型名称前加上两个星号(**):
int** doublePtr;
6.1二级指针与多级指针
#include<stdio.h> int main() { int a = 10; int* pa = &a; int** ppa = &pa; int*** pppa = &ppa; printf("%p\n", a); printf("%p\n", &a); printf("\n%p\n", pa); printf("%p\n", &pa); printf("\n%p\n", ppa); printf("%p\n", &ppa); printf("\n%p\n", pppa); printf("%p\n", &pppa); return 0; }
6.2二级指针运算
📌*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa .
1. int b = 20; 2. *ppa = &b;//等价于 pa = &b;
📌**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a .
1. **ppa = 30; 2. //等价于*pa = 30; 3. //等价于a = 30;
七、指针数组
指针数组是一个数组,其中的每个元素都是指针。我们可以创建指针数组来存储一组指向不同数据类型的指针。这些指针可以指向数组、字符串、函数等各种数据类型,从而实现更灵活的数据结构和操作。
定义指针数组的语法为在指针类型名称后加上方括号([]):
int* ptrArray[5]; // 定义一个包含5个int指针的数组
指针数组非常适用于存储字符串数组。在C语言中,字符串被表示为字符数组,并且每个字符串都以空字符('\0')结尾。使用指针数组可以更方便地管理字符串的集合:
const char* names[] = { "Alice", "Bob", "Charlie" };
在上面的示例中,我们创建了一个包含3个指针的指针数组,每个指针都指向一个字符串常量。
需要注意的是,指针数组中的每个指针应该指向有效的内存地址。否则,当我们尝试通过这些指针访问数据时,可能会导致未定义行为或者出现野指针的问题。确保在使用指针数组之前,为每个指针分配合适的内存或者让它们指向有效的数据。同时,记得在不再需要这些指针数组时,及时释放其所指向的内存,避免内存泄漏。
🔥今天的分享就到这里,如果觉得博主的文章还不错的话,请👍三连支持一下博主哦🤞