c语言指针浅析


前言

​ 指针是C语言中的一个重要概念及其特点也是掌握 C语言比较困难的部分。 指针也就是内存地址 ,指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的 存储空间 长度也不同。 有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。

一.指针概念

1.1 指针的类型

去掉指针本身剩下的就是指针的类型

  • intptr;//指针的类型是int
  • charptr;//指针的类型是char
  • intptr;//指针的类型是int
  • int(ptr)[3];//指针的类型是int()[3]
  • int*(ptr)[4];//指针的类型是int(*)[4]

1.2 指针所指向的类型

去掉指针本身和指针前面的*剩下的就是指针所指向的类

  • int*ptr; //指针所指向的类型是int
  • char*ptr; //指针所指向的的类型是char
  • int*ptr; //指针所指向的的类型是int
  • int(*ptr)[3]; //指针所指向的的类型是int()[3]
  • int*(ptr)[4]; //指针所指向的的类型是int()[4]

1.3 指针的值

​ 指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。64位也是如此,在64位程序中,所有类型的指针的值都是64位整数。指针所存储的值是地址。

1.4 &和*

  • &:&在指针运算中为取地址符号
  • *** :** *在指针运算中为获取地址所指向内存单元的值的符号
int* p;                      //定义指针
int a=1; //定义变量
p=&a; //赋值指针
printf("p:%d,a:%d",*p,a);
运行结果
p:1a:1

&*p和*&p的区别:

因为*和&具有相同的优先级,且是右结合性(从右向左),故分析可得:

1)&*p:相当于&(p),首先进行一次p运算再取地址。若p是一个非指针变量,p是非法的;若p是一个指针变量,&(p)=p;
2)*&p:相当于*(&p),先取地址再做*运算。若p是一个非指针变量,
(&p)=p;若p是一个指针变量,
(&p)=p。

1.5 指针的运算

​ 对于指向数组的指针变量,可以加上或减去一个整数n,这意味着把指针从当前指向的位置(指向的某个数组元素)向后或向前移动n个位置。

这里应当注意:
(1)数组指针变量向前向后移动一个位置和地址加1或减1在概念上是不同的。因为数组的元素可以由不同的数据类型,所占用的字节长度也是不同的。如指针变量加1,即表示指针变量指向下一个元素的首地址,而不是在原地址的基础上加1;
(2)指针变量的加减运算只能对数组指针变量进行,对只想其他数据类型的指针变量做加减运算是毫无意义的。
(3)两个指针变量之间的运算:只有指向同一数组的两个指针变量才能进行运算,否则运算是毫无意义的。
(4)两指针变量相减:两指针变量相减所得之差,是两个指针所指向的数据元素之间相差的元素个数,乘以该数组每个数据元素的长度(字节数);
(5)两指针变量相加:两指针变量不能进行加法运算,毫无实际意义。

int*p; //p所指向的类型为int类型

p++; //p+1地址数增加四个字节
char* p1; //p1所指向的类型为char类型

p1++; //p1+1地址数增加1个字节

二.指针与一维数组

2.1 一维数组

2.1.1 一维数组的初始化

(1)在定义数组时对所有数组元素赋初值

int data[5]={100,99,98,97,95};

(2)对数组部分元素赋初值

int data[5]={100,99};

将100、99赋给data[0]、data[1],其余数组元素自动赋值0。

(3)对全部数组元素赋处置,省略数组长度

int data[]={100,99,98,97,95};

将初值依次赋值给数组的各个元素,数组长度由初值个数决定

三.指针与二维数组

3.1.1 二维数组的初始化

(1)按行给二维数组初始化

int socre[4][3]={{99,98,97},{96,95,94},{93,92,91},{90,89,88}}
99 98 97
96 95 94
93 92 91
90 89 88

(2)按排列数序初始化

int score=[4][3]{99,98,97,96,95,94,93,92,91,90,89,88}

(3)对部分元素赋初值

int socre[4][3]={{99,98},{96,95,}}

int score[4][3]={99,98,97,96,95,94,93,92}

对二维数组进行赋值,为赋初值的数组元素,系统自动赋值为0

(4)初始化时省略第一维长度

int socre[][3]={{99,98},{96,95,}}

int score[][3]={99,98,97,96,95,94,93,92}

3.2 二维数组与指针

3.2.1 二维数组与指针的关系

​ 在c语言中,二维数组可以看作是一维数组的嵌套而构成的,一个二维数组可以按行分解成多个一维数组。例如,有如下定义:

int a[3][4];

​ 二维数组a可分解为三个一维数组,其数组名分别为a[0],a[1],**a[2]**。每个一维数组有四个元素。

​ c语言规定数组名代表数组的首地址,因此a[i]是第i行第0列的数组元素(a[i][0])的地址,即a[i]与&a[i][0]等价,即a[i]+j就是第i行第j列的数组元素a[i][j]的地址,即a[i]+j与&a[i][j]等价。所以对于二维数组a有以下关系:

a[i]+j等价于&a[i][j]

*(a[i]+j)等价于a[i][j]

​ 根据一维数组与指针的关系可知:a[i]等价于*(a+i),进而可推出二维数组与指针的关系:

(1)*(a+i)+j等价于&a[i][j]

(2)*(*(a+i)+j)等价于a[i][j]

3.3 数组指针

**数组指针:**指向数组的指针。

int(*p)[i];

p++;

首先要明确优先级顺序:()>[]>*

​ (* p)[n]:根据优先级,先看括号内的,p是一个指针,指向一个一维数组,数组的长度是n,这是指向数组的指针,叫做数组指针。

​ 指针的类型为int(*)[i],指针所指向的类型为int()[i],在运行p+1时,p所指向的地址在原有的地址上增加sizeof(int)*i。

img

点击并拖拽以移动编辑

3.4 指针数组

指针数组:装着指针的数组。

int* p[4];
p++;

p[n]:根据优先级,先看[],则p是一个数组,再结合,这个数组的元素是指针类型,共n个元素,是装着指针的数组,叫做指针数组。

img

点击并拖拽以移动编辑

​ 指针数组++是指将指向指针数组第一个元素的指针向后移动一个元素的位置。具体来说,如果有一个指针数组p,其中包含n个指针,每个指针指向不同的内存地址,那么p++将把指针向后移动一个元素的位置,即指向p[1]的指针。如果再执行p++,则指针将指向p[2],以此类推。

3.5 指针数组和数组指针

​ 指针数组和数组指针是两个不同的概念。

​ 指针数组是一个数组,其中的每个元素都是一个指针,每个指针指向不同的内存地址。例如,int *ptrArr[10]定义了一个包含10个指向int类型的指针的数组,每个指针都可以指向不同的int类型变量。

​ 数组指针是一个指针,它指向一个数组的首地址。例如,int (*arrPtr)[10]定义了一个指向包含10个int类型元素的数组的指针。在这种情况下,arrPtr指向的是整个数组,而不是数组中的一个元素。

​ 因此,指针数组和数组指针的本质区别在于它们的类型不同,一个是数组,一个是指针。指针数组中的每个元素都是一个指针,而数组指针指向的是一个数组。在使用时需要根据具体情况选择合适的类型。

问题:指针数组名到底是数组名还是指针名?

​ 指针数组名既可以被视为指针名,也可以被视为数组名,具体取决于它的上下文环境。

​ 在指针数组作为函数参数传递时,指针数组名被视为指针名。因为在函数调用时,指针数组会被转换为指向其首元素的指针,并传递给函数。因此,指针数组名被视为指向数组首元素的指针名。

例如,下面的函数原型中,ptrArr被视为指向数组首元素的指针名:

void func(int *ptrArr[], int len);

​ 在指针数组被定义时,指针数组名被视为数组名。因为指针数组本质上是一个数组,指针数组名表示整个数组。在定义指针数组时,需要指定数组的长度,这个长度就是数组的元素个数。

例如,下面的定义中,ptrArr被视为数组名:

int *ptrArr[10];

​ 因此,指针数组名既可以被视为指针名,也可以被视为数组名,具体取决于它的上下文环境。

四.指针与字符串

4.1 字符数组

对字符数组的初始化有以下几种情形

(1)和其他数组一样,对字符数组逐个初始化

char str[5]={'a','b','c','d','\0'};

注意:c语言规定以字符’\0’作为字符串的结束标志。如果没有’\0’,只能说数组str中存储了一串字符,但不能说str存储了字符串。

(2)用字符串直接初始化字符数组

char str[6]={"hello"};

char str[6]="hello";

​ 编译器将双括号括起来的字符依次赋值给字符数组的各个元素,并自动在末尾补上字符结束标志字符’\0’,一起存在数组str中,所以数组的个数要比字符串中字符的个数要多一个。在输出字符串时,末尾’\0’不输出,只用于判断字符串是否结束。

char str[]="hello";

​ 按这种方式定义和初始化字符串数组,不必指定数组的大小,编译系统会根据字符串中字符的个数确定数组的大小。由于字符串常量”hello”的末尾字符时’\0’,因此字符数组的长度为字符串的额中世纪的个数加1,即:字符数组str的长度为6。

4.2 字符指针

​ 字符指针时指向字符型数据的指针变量。每个字符串在内存中都占用一段连续的存储空间,并有唯一的首地址。因此,只要把字符串的首地址赋值给字符指针,即可让字符指针指向字符串。

char* p;
p="hello";

char str[]="hello";
char* p;
p=str;

五.指针函数与函数指针

5.1 指针函数

1.指针函数的概念

​ 指针函数是一种特殊类型的函数,其特点是其返回值的类型为某一类型的指针。也就是说,这种函数的返回值是一个地址,这个地址指向函数内部某种特定类型的变量或者数据结构。

2.指针函数的定义

指针类型 * (函数名称)(函数的参数列表类型) 

3.指针函数的使用

#include <stdio.h>

int *find_max(int a[], int n) {
int *p = &a[0]; // 定义一个指向数组首元素的指针
for (int i = 1; i < n; i++) {
if (*p < a[i]) {
p = &a[i]; // 如果当前元素大于指针所指向的元素,则更新指针的值
}
}
return p; // 返回指向最大元素的指针
}

int main() {
int arr[] = {3, 5, 2, 7, 1};
int n = sizeof(arr) / sizeof(arr[0]);
int *max_ptr = find_max(arr, n); // 调用find_max函数,将最大元素的地址赋给max_ptr指针变量
printf("Max value is %d", *max_ptr); // 输出最大值
return 0;
}

点击并拖拽以移动

5.2 函数指针

1.函数指针的概念

​ 函数指针是一种特殊类型的指针,它指向函数而非变量。这种指针在内存中储存的是函数的入口地址,我们可以通过这个地址来调用相应的函数。

2.函数指针的定义

函数的返回值类型(*指针名)(函数的参数列表类型)

3.函数指针的使用
下面是一个函数指针的使用,首先定义一个max函数,然后在主函数内声明函数指针,再将max函数地址传递给函数指针,最后调用函数指针即可。

#include <stdio.h>

int max(int x, int y) {
return x > y ? x : y;
}

int main() {
int (*func_ptr)(int, int); // 声明一个函数指针变量
func_ptr = max; // 将函数max的地址赋给函数指针变量
printf("Max value is %d", func_ptr(10, 20)); // 输出最大值
return 0;
}

文章作者: Joe
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Joe !
评论
  目录