C语言:指针

指针的概念
-
系统给虚拟内存的每个存储单元分配了一个编号,从0x00000000 ~ 0xffffffff ,这个编号称之为地址
-
指针变量:用来存放一个地址的变量
-
作用 :可以通过指针来间接访问内存
-
每定义一个变量,程序会向计算机申请一个内存空间,空间的大小取决于数据类型

pic
#include<stdio.h>
int main(){
printf("int类型指针占用内存:%d\n",sizeof(int *)); printf("float类型指针占用内存:%d\n",sizeof(float *)); printf("double类型指针占用内存:%d\n",sizeof(double *)); printf("char类型指针占用内存:%d\n",sizeof(char *));
return 0;}运行结果
64位int类型指针占用内存:8 float类型指针占用内存:8 double类型指针占用内存:8 char类型指针占用内存:8
32位int类型指针占用内存:4 float类型指针占用内存:4 double类型指针占用内存:4 char类型指针占用内存:4
扩展在32位操作系统下,指针占用4byte内存,在64位操作系统下,指针占用8byte内存
指针变量的定义
-
语句:数据类型 * 变量名
-
eg.
#include<stdio.h>int main(){// 定义一个指针int num1 = 10;// 指针的定义语法: 数据类型 *变量名// 指针的数据类型需要和变量的数据类型相同int *p;// &取地址符p = &num1;printf("num1的地址是:%p\n",&num1);printf("指针p是:%p\n",p);return 0;} -
运行结果(不同设备运行结果不同,但两个地址结果一定相同)
num1的地址是:000000000061FE14 指针p是:000000000061FE14
请按任意键继续…
指针的解引用
-
语法:*指针名称
-
作用:通过指针找到对应的内存地址
#include<stdio.h>int main(){// 定义一个指针int num1 = 10;// 指针的定义语法: 数据类型 *变量名// 指针的数据类型需要和变量的数据类型相同int *p;// &取地址符p = &num1;// 使用指针// 可以通过解引用的方式来找到指针指向的内存// 指针前面加一个*表示解引用printf("指针p指向的内存的值为:%d\n",*p);*p = 100; // 可以通过修改指针p指向内存的值来修改变量的值printf("现在num1的值:%d\n",num1);printf("现在指针p指向的内存的值是:%d\n",*p);return 0;}
运行结果
指针p指向的内存的值为:10 现在num1的值:100 现在指针p指向的内存的值是:100
请按任意键继续…
指针与数组元素
-
我们定义的数组,就是多个相同类型变量的集合,,每个元素都有对应的内存空间,都有对应的地址,因此,指针也可以存放数组元素的地址。
-
eg.
#include<stdio.h>int main(){int a[5];// 定义指针变量p,保存a[0]的地址int *p = &a[0];return 0;}

-
应用(数组元素的引用)
-
数组名[下标]
int a[5];a[2] = 100; -
指针加下标
- c语言规定:数组的名字就是数组的首地址,即第0个元素的地址,就是&a[0] 。
int a[5];int *p;p = a;p[2] = 100; // 等同于a[2] = 100;! ! ! p和a的不同,p是指针变量,而a是个常量。所以可以用等号给p赋值,但不能给a赋值
p = &a[0]; // 正确a = &a[0]; // 错误 -
通过指针变量运算加取值的方法来引用数组的元素
- p是数组的第0个元素,相当于a[0],那么p + 2就相当于a[2]
int a[5];int *p;p = a;*(p + 2) = 100; // 等同于a[2] = 100; -
通过数组名+取值的方法引用数组的元素
- 由于C语言中数组的名字也是一个指针,因此我们可以通过如下方式引用数组中的元素
int a[5];*(a+2)=100; // 等同于a[2]=100;
-
指针的运算
-
指针加整数,往下指变量(仅指针指向数组时有意义)
int a[5];int *p;p=a;p+2; // p是&a[0],p+2是&a[2] -
两个相同类型指针比较大小(仅两个相同类型指针变量指向同一数组时有意义)
#include<stdio.h>;int main(){int a[10];int *p,*q;p = &a[0];q = &a[6];printf("%d\n%d\n",p,q);/* 64219846422008请按任意键继续. . .两个结果前面的指针小于后面的指针,由于整型为4byte,因此两者间相差24 */printf("%d\n",q - p); // 6 即两个指针指向的之间有多少个元素return 0;} -
两个相同类型的指针做减法(仅两个相同类型指针变量指向同一数组时有意义)
#include<stdio.h>;int main(){int a[10];int *p,*q;p = &a[0];q = &a[6];printf("%d\n",q - p); // 6 即两个指针指向的之间有多少个元素return 0;} -
两个相同类型的指针可以相互赋值(void*类型除外)
#include<stdio.h>;int main(){int a[10];int *p,*q;p = &a[0];q = &a[6];// 两个相同类型的指针可以互相复制,例如下面的例子中,q保存了p的地址也指向ap = a;q = p;// 编译运行的结果是p和q指向的同一个地址printf("%p\n%p\n",p,q);return 0;}
指针数组
-
概念:指针数组是若干个相同类型的指针变量构成的集合(数组)
-
语法:数据类型 * 数组名 [元素个数];
-
eg.
#include<stdio.h>int main(){char *chr[6] = {"China","America","Russia","French","Btitain","Welcome"};for(int i = 0;i < 6;i++){printf("%s\n",chr[i]);}return 0;}运行结果
China
America
Russia
French
Britain
Welcome

指针的指针
-
概念:指针变量的地址
-
eg.
#include<stdio.h>int main(){// 指针的指针int a = 0;int *p = &a;int **q = &p; // q保存了p地址return 0;}理论上,我们可以创建指针的指针的指针乃至更多,无限套娃
指针与字符串
- 字符串的概念:字符串就是以’\0’结尾的若干的字符的集合(数组)
- 字符串的地址:即字符串中第一个字符的地址,例如字符串“helloworld”中字符串的地址即为字符“h”的地址
char *s = "helloworld";数组指针
-
概念:指向数组的地址
-
语法:指向数组的数据类型(*指针变量名)[指向数组的元素个数]
-
eg.
#include<stdio.h>int main(){int b[2][3] = {1,2,3,4,5,6};// 这里二维数组需要两次解引用才能的到b[0][0] 具体原因见下方补充说明printf("b = %d\n",**b);printf("b + 1 = %d\n",b[0][0]);int (*t)[3];printf("b = %p\n",b);printf("b + 1 = %p\n",b + 1);t = b;printf("t = %p\n",t);printf("t + 1 = %p\n",t + 1);return 0;}运行结果
b = 1 b + 1 = 1 b = 000000000061FDD0 b + 1 = 000000000061FDDC t = 000000000061FDD0 t + 1 = 000000000061FDDC
补充说明
这里二维数组b的数组名实际上指向的是该二维数组中的第一个一维数组的地址,b[2] [3]可以具象化为一个2*3的表格,*b得到的是第0行的地址,**b得到的就是第0行第0列的元素的地址了

Snipaste_2025-08-23_10-09-44 -
各种数组指针的定义
-
一维数组指针,加1即为下一个一维数组
如 int (*p)[5]; ,配合每行有5个int型元素的二维数组如 int a[3] [5] 、int b[4] [5]、 int c[5] [5]、int d[6] [5]
-
二维数组指针,加1即为下一个二维数组
如 int (*p)[4] [5]; 配合三维数组来用。三维数组中由若干个4行5列二维数组构成,如int a[3] [4] [5];、 int b[4] [4] [5];、int c[5] [4] [5];、 int d[6] [4] [5]; 这些三维数组,有个共同的特点,都是有若干个4行5的二维数组构成。
eg.
#include<stdio.h>int main(){int a[3][4][5];printf("a=%p\n", a);printf("a+1=%p\n", a + 1);// a 和 a+1 地址编号相差 80 个字节// 验证了a+1 跳一个4行5列的一个二维数组int(*p)[4][5];p = a;printf("p=%p\n", p);printf("p+1=%p\n", p + 1);//p 和 p+1 地址编号相差也 80 个字节return 0;} -
数组名字与指针变量的区别
-
相同点:a为数组的名字,是a[0]的地址,p = a则p保存了a[0]的地址,两者都指向a[0],所以在引用数组时两者等价
#include<stdio.h>int main(){int a[3] = {0,1,2};int* p;p = a;printf("a[2] = %d\n", a[2]); // 2printf("*(a + 2) = % d\n", *(a + 2)); // 2printf("p[2] = %d\n", p[2]); // 2printf("*(p + 2) = % d\n", *(p + 2)); // 2return 0;} -
不同点:a作为数组的名字,它是一个常量,值永远是a[0]的地址;而p则为指针变量,可以为其赋值。对a取地址相当于数组指针,而对p取地址则为指针的指针。
指针与函数
-
指针传参
- eg.
int num;scanf("%d",&num);- 可以将变量的地址传入函数,在被调函数中来修改主调函数中的变量(局部变量)
#include<stdio.h>void swap(int *p,int *q){int temp;temp = *p;*p = *q;*q = temp;}int main(){int a = 10,b = 20;swap(&a,&b);printf("a = %d,b = %d",a,b); // a = 20,b = 10return 0;} -
指针传数组
- 传一维数组
#include<stdio.h>void printArray(int *p){printf("%d\n",p[2]); // 3printf("%d\n",*(p + 3)); // 4}int main(){int a[5] = {1,2,3,4,5};printArray(a);return 0;}- 传二维数组
#include<stdio.h>// int(*p)[3] 将二维数组的三个一维数组依次传入void printArray(int(*p)[3]){// 通过数组调用printf("%d\n",p[1][1]); // 5// 通过指针调用printf("%d\n",*(*(p + 1) + 2)); // 6}int main(){int a[2][3] = {1,2,3,4,5,6};printArray(a);return 0;}- 传指针数组
#include<stdio.h>void printChar(char **p){for(int i = 0;i < 3;i++){printf("%s\n",p[i]);}}int main(){char *p[3] = {"Hello","World","Programe"};printChar(p);return 0;} -
指针作返回值
-
一个函数可以返回整型数据、字符数据、浮点型的数据,也可以返回一个指针。
-
这里返回的是fun函数里面局部变量str的地址,函数运行结束,内存就被释放了,返回这个地址,也没有意义了
#include<stdio.h>char* fun() {char str[100] = "hello world";return str;}int main(){char* p;p = fun();printf("%s\n", p); // 乱码return 0;}- 在函数里面给局部变量加static修饰,因为静态数组的内容,在函数结束后,仍然存在
#include<stdio.h>char* fun() {static char str[100] = "hello world";return str;}int main(){char* p;p = fun();printf("%s\n", p); // hello worldreturn 0;} -
-
函数指针
- 概念:定义一个指针变量,来存放函数的地址
- 语法:返回值类型(*函数指针变量名)(形参列表);
#include<stdio.h>int add(int a,int b){return a + b;}int main(){int (*p)(int,int);p = add;// 通过函数名调用printf("%d\n",add(1,2));// 通过函数指针调用printf("%d\n",(*p)(1,2));return 0;} -
函数指针数组
- 由若干个相同类型的函数指针变量构成的集合,在内存中连续的顺序存储。函数指针数组是个数组,它的每个元素都是一个函数指针变量
- 语法:类型名(*数组名[元素个数])(形参列表)
#include<stdio.h>int max(int a,int b){if(a > b){return a;}else{return b;}}int min(int a,int b){if(a < b){return a;}else{return b;}}int add(int a,int b){return a + b;}int main(){int (*p[3])(int,int) = {max,min,add};printf("%d\n",(*p[2])(4,8)); // 12return 0;}
特殊指针
-
空类型指针void *
- void * 并不是指向void类型的变量,而是一种通用指针,任何数据类型的地址都可以为其赋值
int *p;void *q;q=p // 是可以的,不用强制类型转换 -
空指针NULL
- p哪里都不指向,用于指针的初始化
int *p = NULL;
main 函数传参
- int argc:argc 表示命令行参数个数(argument count),包括程序本身,即 argc 最小为1
- char *argv[]:使用argument value来记忆,参数值的意思,argv[] 是一个指向字符串数组的指针,其中每个元素是一个指向传递给程序的参数的指针(argument vector),这些字符串是命令行参数。
#include<stdio.h>
int main(int argc,char *argv[]){ int i; printf("argc=%d\n", argc); for ( i = 0; i < argc; i++) { printf("argv[%d]=%s\n", i,argv[i]); } return 0;}无参数运行结果
argc=1 argv[0]=test.exe
test.exe abc 123有参数运行结果
argc=3 argv[0]=test.exe argv[1]=abc argv[2]=123
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!












