C语言——指针详解(指针、指针与数组、指针与结构、指针与函数)

一、什么是指针

C语言中,变量存放于内存中,数据对象是指一个指定数据类型的数值或字符串,它们都有一个自己的地址,指针就是保存这个地址的变量。

二、为什么要使用指针
使用指针往往可以生成更高效、更紧凑的代码。
1)指针的使用使得不同区域的代码可以轻易的共享内存数据,这样可以使程序更为快速高效;
2)C语言中一些复杂的数据结构往往需要使用指针来构建,如链表、二叉树等;
3)C语言是传值调用,而有些操作传值调用是无法完成的,如通过被调函数修改调用函数的对象,但是这种操作可以由指针来完成,而且并不违背传值调用。

三、如何声明一个指针
3.1声明并初始化指针

int *p;//声明一个int类型的指针p
char *p;//声明一个char类型的指针p
int *arr[10];//声明一个指针数组,数组里的每一个元素都是指向int类型的指针
int (*arr)[10];//声明一个指针数组,该指针指向一个int类型的数组
int **p;//声明一个指针,该指针指向一个int类型的指针

指针的声明比普通变量的声明多了一个一元运算符 “*”。*号是间接寻址或者间接引用运算符。在上述的声明中: p 是一个指针,保存着一个地址,该地址指向内存中的一个变量; *p 则会访问这个地址所指向的变量。

声明一个指针变量并不会自动分配任何内存。在对指针进行间接访问之前,指针必须进行初始化:或是使他指向现有的内存,或者给他动态分配内存,否则我们并不知道指针指向哪儿,这将是一个很严重的问题。
初始化操作:

/*方法1:使指针指向现有的内存*/
int x=1;
int *p=&x;//指针p被初始化,指向变量x

/*方法2:动态分配内存给指针*/
int *p;
p=(int *)malloc(sizeof(int) *10);//malloc用于动态内存分配
free(p);// free 函数用于释放一块已经分配的内存,常与 malloc 函数一起使用,要使用这两个函数需要头文件 stdlib.h

3.2 未初始化和非法的指针
如果一个指针没有被初始化,那么程序就不知道它指向哪里。它可能指向一个非法地址,这时,程序会报错。
比如下面的程序:

#include<stdio.h>
int main()
{
    int *p;        
    *p=2;
    printf("%d\n",*p);
    return 0;
 }

要想程序运行起来,需要对指针p做初始化,应该修改为:

#include<stdio.h>
int main()
{
    int x=1;
    int *p=&x;        
    *p=2;
    printf("%d\n",*p);
    return 0;
 }

3.3NULL指针
NULL 指针是一个特殊的指针变量,表示不指向任何东西。可以通过给一个指针赋一个零值来生成一个 NULL 指针。

#include<stdio.h>
int main()
{
    int *p=NULL;      
    printf("p的地址为%d\n",p);
    return 0;
 }

四、指针的运算
C 指针的算术运算只限于两种形式:
1)指针 +/- 整数:
可以对指针变量 p 进行 p++、p–、p + i 等操作,所得结果也是一个指针,只是指针所指向的内存地址相比于 p 所指的内存地址前进或者后退了 i 个操作数。
在这里插入图片描述
2)指针 - 指针:
只有当两个指针都指向同一个数组中的元素时,才允许从一个指针减去另一个指针。两个指针相减的结果的类型是 ptrdiff_t,它是一种有符号整数类型。减法运算的值是两个指针在内存中的距离(以数组元素的长度为单位,而不是以字节为单位)。
在这里插入图片描述
五、指针与数组

5.1指针与数组的关系
1)声明一个数组

int a[10];

2)声明一个指针

int *p;

3)对p做初始化

p=&a[0]; // 对指针进行初始化,p将指向数组 a 的第 1 个元素 a[0]

我们知道,对指针进行自增操作会让指针指向与当前元素相邻的下一个元素,即 *(p + 1) 将指向 a[1] ;同样的, *(p + i) 将指向 a[i] 。因此,我们可以使用该指针来遍历数组 a[10] 的所有元素。可以看到,数组下标与指针运算之间的关系是一一对应的。而根据定义,数组类型的变量或表达式的值是该数组第 1 个元素的地址,且数组名所代表的的就是该数组第 1 个元素的地址,故,上述赋值语句可以直接写成:

p = a;        // a 为数组名,代表该数组最开始的一个元素的地址 

很显然,一个通过数组和下标实现的表达式可以等价地通过指针及其偏移量来实现,这就是数组和指针的互通之处。但有一点要明确的是,数组和指针并不是完全等价,指针是一个变量,而数组名不是变量,它数组中第 1 个元素的地址,数组可以看做是一个用于保存变量的容器。
在这里插入图片描述

在这里插入图片描述
可以看到, x 的值与 x[0] 的地址是一样的,也就是说数组名即为数组中第 1 个元素的地址。实际上,打印 &x 后发现,x 的地址也是这个值。而 x 的地址与指针变量 p 的地址是不一样的。故而数组和指针并不能完全等价。

5.2指针数组
指针是一个变量,而数组是用于存储变量的容器,因此,指针也可以像其他变量一样存储在数组中,也就是指针数组。 指针数组是一个数组,数组中的每一个元素都是指针。声明一个指针数组的方法如下:

int *p[10];    // 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指向int类型的指针

在上述声明中,由于 [] 的优先级比 * 高,故 p 先与 [] 结合,成为一个数组 p[];再由 int * 指明这是一个 int 类型的指针数组,数组中的元素都是 int 类型的指针。数组的第 i 个元素是 *p[i],而 p[i] 是一个指针。由于指针数组中存放着多个指针,操作灵活,在一些需要操作大量数据的程序中使用,可以使程序更灵活快速。

5.3数组指针
数组指针是一个指针,它指向一个数组。声明一个数组指针的方法如下

int (*p)[10];        // 声明一个数组指针 p ,该指针指向一个数组

由于 () 的优先级最高,所以 p 是一个指针,指向一个 int 类型的一维数组,这个一维数组的长度是 10,这也是指针 p 的步长。也就是说,执行 p+1 时,p 要跨过 n 个 int 型数据的长度。数组指针与二维数组联系密切,可以用数组指针来指向一个二维数组,如下:
在这里插入图片描述
六、指针与结构
6.1结构的介绍
结构是一个或多个变量的集合,这些变量可能为不同的类型,为了处理的方便而将这些变量组织在一个名字之下。由于结构将一组相关的变量看做一个单元而不是各自独立的实体,因此结构有助于组织复杂的数据,特别是在大型的程序中。声明一个结构的方式如下:

struct message{      //声明一个结构message
    char name[10];    //成员
    int age;
    int score;
 };
typedef struct message s_message; //typedef  类型定义符

s_message mess={"ytt",20,100};  //声明一个 struct message类型的变量  mess
/*另一种更简便的声明方法 */
typedef struct{
  char name[10];
  int age;
  int score;
}message;

可以使用 结构名.成员 的方式来访问结构中的成员,如下:

#include "stdio.h"

int main(){
  printf("%s\n",mess.name);    // 输出结果:ytt
  printf("%d\n",mess.age);     // 输出结果:20

  return 0;``
}

6.2结构指针
结构指针是指向结构的指针,以上面的结构为例,可以这样定义一个结构指针:

s_message *p;        // 声明一个结构指针 p ,该指针指向一个 s_message 类型的结构
*p = &mess;      // 对结构指针的初始化与普通指针一样,也是使用取地址符 &

C语言中使用 -> 操作符来访问结构指针的成员,举个例子:

#include "stdio.h"

typedef struct{
    char name[10];
    int age;
    int score;  
}message;

int main(){
    message mess = {"ytt",20,100};
    message *p = &mess;

    printf("%s\n",p->name);      // 输出结果为:ytt
    printf("%d\n",p->score);         // 输出结果为:100

    return 0;
} 

七、指针与函数
C语言的所有参数均是以“传值调用”的方式进行传递的,这意味着函数将获得参数值的一份拷贝。这样,函数可以放心修改这个拷贝值,而不必担心会修改调用程序实际传递给它的参数。

7.1指针作为函数的参数
传值调用的好处是是被调函数不会改变调用函数传过来的值,可以放心修改。但是有时候需要被调函数回传一个值给调用函数,这样的话,传值调用就无法做到。为了解决这个问题,可以使用传指针调用。指针参数使得被调函数能够访问和修改主调函数中对象的值。用一个例子来说明:
在这里插入图片描述在这里插入图片描述
7.2指向函数的指针
在C语言中,函数本身不是变量,但是可以定义指向函数的指针,也称作函数指针,函数指针指向函数的入口地址。这种类型的指针可以被赋值、存放在数组中、传递给函数以及作为函数的返回值等等。 声明一个函数指针的方法如下:

返回值类型 (* 指针变量名)([形参列表];

int (*pointer)(int *,int *);        // 声明一个函数指针

上述代码声明了一个函数指针 pointer ,该指针指向一个函数,函数具有两个 int * 类型的参数,且返回值类型为 int。下面的代码演示了函数指针的用法:
在这里插入图片描述

发布了25 篇原创文章 · 获赞 2 · 访问量 1713

猜你喜欢

转载自blog.csdn.net/ytt999/article/details/104577092