一、指针运算
指针与整数的加减运算,表示指针所指向的内存地址的移动(加,向后移动;减,向前移动),指针移动多少,与指针指向的数据类型有关,数据类型占据多少个字节,每单位就移动多少个字节,比如一个 int 类型指针,+1 向后移动 4 个字节,-2 向前移动 8 个字节。
数组的元素在内存中连续存储的,我们通过数组元素来演示指针加减整数的情况。
#include <stdio.h>
int main()
{
int nums[] = {10, 20, 30, 40, 50};
int *ptr = &nums[0];
// 打印指针变量ptr的值,和指针变量ptr指向的值
printf("ptr = %p,*ptr = %d \n", ptr, *ptr);
// ptr指针加3,指针变量ptr指向int类型,占4个字节,此时指针向后移动12个字节
ptr += 3;
printf("ptr = %p, *ptr = %d \n", ptr, *ptr);
// ptr指针减2,指针变量ptr指向int类型,占4个字节,此时指针指向向前移动8个字节
ptr -= 2;
// 此时指针会向前移动8个字节
printf("ptr = %p, *ptr = %d \n", ptr, *ptr);
return 0;
}
二、指针自增自减
指针自增、自减本质上就是指针加减整数,自增地址后移,自减地址前移。下面我们利用指针的自增自减实现数组的遍历,代码如下:
#include <stdio.h>
int main()
{
int arr[]={10,20,30,40,50};
int *ptr=&arr[0];
for (int i=0;i<5;i++)
{
printf("每个元素数据值为:%d,每个元素的内存地址:%p\n",*ptr,ptr);
ptr++;
}
printf("\n===========================================\n");
for(int i=0;i<5;i++)
{
ptr--;
printf("每个元素的值:%d,每个元素的内存地址:%p\n",*ptr,ptr);
}
return 0;
}
运行结果:
每个元素数据值为:10,每个元素的内存地址:00000052fcfffab0
每个元素数据值为:20,每个元素的内存地址:00000052fcfffab4
每个元素数据值为:30,每个元素的内存地址:00000052fcfffab8
每个元素数据值为:40,每个元素的内存地址:00000052fcfffabc
每个元素数据值为:50,每个元素的内存地址:00000052fcfffac0
===========================================
每个元素的值:50,每个元素的内存地址:00000052fcfffac0
每个元素的值:40,每个元素的内存地址:00000052fcfffabc
每个元素的值:30,每个元素的内存地址:00000052fcfffab8
每个元素的值:20,每个元素的内存地址:00000052fcfffab4
每个元素的值:10,每个元素的内存地址:00000052fcfffab0
三、用类型指针比较
#include <stdio.h>
int main()
{
int a[]={10,20,30,40,50};
int *ptr1=&a[0];
int *ptr2=&a[3];
printf("ptr2-ptr1=%d\n",ptr2-ptr1);
printf("ptr1-ptr2=%d\n",ptr1-ptr2);
printf("\n===========================================\n");
double d1=8.2;
double d2=6.3;
double *ptr_d1=&d1;
double *ptr_d2=&d2;
printf("d1的值:%.2lf,d1的内存地址为:%p\n",d1,&d1);
printf("d2的值:%.2lf,d2的内存地址为:%p\n",d2,&d2);
printf("ptr_d1-ptr_d2=%td\n",ptr_d1-ptr_d2);
return 0;
}
运行结果:
ptr2-ptr1=3
ptr1-ptr2=-3
===========================================
d1的值:8.20,d1的内存地址为:000000fcda7ffce8
d2的值:6.30,d2的内存地址为:000000fcda7ffce0
ptr_d1-ptr_d2=1
四、指针的比较运算
指针之间可以进行比较运算,如 ==、<、 <= 、 >、 >=,比较的是各自指向的内存地址的大小,返回值是 int 类型整数 1 (true)或 0 (false)。案例演示如下:
#include <stdio.h>
int main()
{
// 比较指针
int nums[] = {10, 20, 30, 40, 50};
double n = 1.0;
// 定义指针变量指向数组第一个元素的地址
int *ptr1 = &nums[0];
// 定义指针变量指向数组第四个元素的地址
int *ptr2 = &nums[3];
// 定义指针变量指向数组第一个元素的地址
int *ptr3 = &nums[0];
// 定义指针变量指向变量n的地址
double *ptr4 = &n;
// 输出指针指向的地址
printf("ptr1 = %p \n", ptr1);
printf("ptr2 = %p \n", ptr2);
printf("ptr3 = %p \n", ptr3);
printf("ptr4 = %p \n", ptr4);
// 比较
printf("ptr1>ptr2 = %d \n", ptr1 > ptr2);
printf("ptr1<ptr2 = %d \n", ptr1 < ptr2);
printf("ptr1==ptr3 = %d \n", ptr1 == ptr3);
// 由于是不同类型的指针进行比较,所以会有一个警告
printf("ptr4>ptr1 = %d \n", ptr4 > ptr1);
return 0;
}
ptr1=000000d4385ffd10
ptr2=000000d4385ffd1c
ptr3=000000d4385ffd10
ptr4=000000d4385ffd08
ptr1>ptr2: 0
ptr1<ptr2: 1
ptr1==ptr3: 1
ptr4>ptr1: 0
五、指针数组
1、数组名
数组名在大多数情况下会被隐式地转换为指向数组第一个元素的指针,在特定情况下数组名可以被视为一个指针,具有一些指针的特性。
但是数组名与真正的指针是不同的,主要有以下几点区别:
- 使用 sizeof 运算符,数组名得到的是整个数组的大小;指针得到的是本身的大小。
- 数组名不能进行自增、自减运算。
- 数组名的指向不可更改。
例子:
#include <stdio.h>
int main()
{
int arr[5]={10,20,30,40,50}; // 定义一个整型数组arr,并初始化
int *ptr=&arr[0]; // 定义一个整型指针ptr,并指向数组的第一个元素
printf("%d,%p\n",*arr,arr); // 输出数组的第一个元素和数组的地址
printf("%d,%p\n",*ptr,ptr); // 输出指针ptr指向的元素和指针ptr的地址
printf("%zu,%zu\n",sizeof(arr),sizeof(ptr)); // 输出数组arr和指针ptr的大小
ptr++; // 指针ptr指向数组的第二个元素
printf("%d,%p\n",*ptr,ptr); // 输出指针ptr指向的元素和指针ptr的地址
return 0;
}
10,000000f512dff9a0
10,000000f512dff9a0
20,8
20,000000f512dff9a4
指针几个字节跟语言无关,而是跟系统的寻址能力有关,譬如以前是16位系统,指针即为2个字节,32位系统,是4个字节,64位,则就为8个字节。
2、指针数组
指针数组(Pointer Array)是一个数组,其中的每个元素都是指针。
语法规则:
数据类型 * 指针数组名 [长度];
例子
#include <stdio.h>
int main()
{
int a[4]={1,2,3,4}; // 定义一个整型数组a,并初始化
int *arr[3]={&a[0],&a[1],&a[2]}; // 定义一个指向整型数组的指针数组arr,并初始化
for (int i = 0; i < 3; i++)
{
printf("%d %p\n",*arr[i],arr[i]); // 遍历arr数组,打印每个元素的值和地址
}
printf("\n========================================================\n");
int num1=9,num2=8,num3=7; // 定义三个整型变量num1、num2、num3,并初始化
int *arr1[3]; // 定义一个指向整型的指针数组arr1
arr1[0]=&num1; // arr1的第一个元素赋值为num1的地址
arr1[1]=&num2; // arr1的第二个元素赋值为num2的地址
arr1[2]=&num3; // arr1的第三个元素赋值为num3的地址
for (int j = 0; j < 3; j++)
{
printf("%d %p\n",*arr1[j],arr1[j]); // 遍历arr1数组,打印每个元素的值和地址
}
return 0;
}
1 0000009f1afffd00
2 0000009f1afffd04
3 0000009f1afffd08
========================================================
9 0000009f1afffcdc
8 0000009f1afffcd8
7 0000009f1afffcd4
3、数组指针
数组指针(Array Pointer)是一个指针,它指向一个数组。注意,数组指针指向的是整个数组的地址而不是第一个元素的地址,虽然二者值是相同的,但在运算中会表现出不同。
语法规则:
数据类型 (*数组指针名)[长度]
例子:
#include <stdio.h>
int main()
{
int arr[5]={1,2,3,4,5};
int (*ptr)[5]=&arr;
printf("%p,%p\n",arr,ptr);
printf("%p,%p\n",arr+1,ptr+1);
printf("\n==============================\n");
for (int i = 0; i < 5; i++)
{
printf("%d,%p\n",(*ptr)[i],ptr[i]);
}
return 0;
}
000000840f3ffcd0,000000840f3ffcd0
000000840f3ffcd4,000000840f3ffce4
==============================
1,000000840f3ffcd0
2,000000840f3ffce4
3,000000840f3ffcf8
4,000000840f3ffd0c
5,000000840f3ffd20
3、字符数组
字符指针变量(简称字符指针)是C语言中的一种指针类型,它用于指向字符或字符串(字符数组),通常用于处理字符串(字符数组)。
例子:
#include <stdio.h>
int main()
{
char *chs="hello";
printf(" %p\n",chs);
for (int i = 0; i < 5; i++)
{
printf("%c,%p\n",chs[i],&chs[i]);
}
return 0;
}
00007ff672de9000
h,00007ff672de9000
e,00007ff672de9001
l,00007ff672de9002
l,00007ff672de9003
o,00007ff672de9004