C语言指针【常见类型】

指针

指针是很多人头疼的部分,但它的应用无处不在,是编程中不可或缺的一环。无论是在C语言中,还是是C++,亦或是Java,Python中,都有它的应用,各具的名字不同罢了。比如C++中的引用,它的实质和指针相同,均是将一个地址内容赋值给变量,通过地址去访问对应内存的内容

指针是一种数据类型,比如说int ,long ,short ,float, double,char…,这些都是数据类型,他们都能存储对应的数据。那么指针存放什么呢?

int k = 10;//k是一个整型数据,存储整型数字
int *p;//p是一个整型数据指针,存储整型变量的地址
p = &k;//p的内容是k的地址,通过‘&’符号获取变量地址
*p = 5;//p的内容是k的地址,不变。但k = 5

通过上面四行代码,能够感受一下,我们可以知道以下几点:

  • 指针的类型名是已有的数据类型加上‘*’号,得到的便是指针类型。
  • 对一个元素获取地址的办法是加上‘&’符号,得到地址。
  • ‘*’在非初始化中,作为解引用符号,当其作用于一个指针时,便是在访问该指针所存的地址内容。
  • 通过地址直接寻找到对应内存后,修改内存内容则会修正所有指向该内存的值。

那么,我们现在知道指针指向对应的数据类型地址,扩展一下,因为数组的每一个元素也是一个数据内存。

int a[5] = {0,1,2,3,4};//可以通过a访问到数组中的任意元素。
int *p;
p = &a[1];
*p = 2;//a[5] = {0,2,2,3,4}
a[3] = 4;//a[5] = {0,2,2,4,4}
/*数组特性:a[2] = *(a+2) */

首先,对于单个数组元素地址的取址方式可以和单个元素相同,但是因为数组本身所具有的特性:数组的空间连续存放,对元素的访问实质也是通过指针地址相加的办法得到对应元素位置的地址,然后解引用

12345
01234
0x80000x80040x80080x800C0x8010

指针地址+1,1代表数据类型的字节数

e.g. a = 0x8000,a+1 = 0x8004(+1,实际上加了4个字节)

也就是说,数组名实际上也是一个指针。【数组指针】

指针的简单应用

首先,我们已经知道了通过指针可以直接访问内存,并且可修改该地址内容的值。对指针加减一个常数n,是在移动指针向前或者向后移动n个数据类型的字节数。那么我们可以用指针代替数组下标进行循环访问。

int a[10];
int *p = a;//a为数组指针,其内存内容为数组首地址(a = &a[0])
for(p = a;p<(a+10);p++)//p++,指针指向的地址 + 1*sizeof(int)
{
    //循环体
}

同样的,我们可以通过两个指向同一个数组的指针,计算他们之间的元素个数。

int a[10];
int *p = &a[3];
int *q = &a[6];
int k = q-p;//k = 3;

说完了指针的加减意义,指针在C语言中还有一个重要的应用——参数传递。

在C语言的函数声明和函数定义中,其中的参数都是形式参数而非实际参数,形式参数拥有和实际参数相同的值,但是形式参数只是实际参数的副本,对形式参数作用的操作,在函数结束时都会被释放掉,对于实际参数的值无改变。即:

int p = 10;
int q = p;//q = 10;
q = 5;//q = 5,p = 10;
/*q只是p的副本,对副本修改,主体不变*/

但是,我们回顾指针的最基本的功能,指针存储了操作空间的地址,在计算机中,所有的内存都是有唯一地址编码,那么就是说我们可以通过指针在函数中去访问并修改原始的数据。

#include<stdio.h>
void fun(int *p,int k)//p = &a,k = b;
{
    *p = 3;
    k = 5;
}
int main()
{
    int a = 10,b = 10;
    fun(&a,b);
    printf("%d %d",a,b);//a = 3,b = 10
    return 0;
}

通过以上代码,我们可以分析得到,对于非指针传递的参数,我们无法在另一个函数中修改其在另一个函数中的值,但是对于指针传递的参数,因为变量的地址在计算机中唯一,因此可以通过直接访问地址修改内容,同时也就改变了原值。

刚刚我们也讲到了,数组名也是一个指针,其存储了数组的首地址。那我们是不是同样的可以将数组作为指针参数进行传递,修改数组中的值。

#include<stdio.h>
void fun(int a[],int n)//a = &b[0],n为输入的数目,切忌n值大于数组大小
{
    int i;
    for(i = 0;i<n;i++)
        scanf("%d",&a[i]);//input:1,2,3,4,5,6
}
int main()
{
    int b[6]={0},i;
    fun(b,6);//b = &b[0]
    for(i = 0;i<6;i++)
        printf("%d ",b[i]);//output:1 2 3 4 5 6 
    return 0;
}

指针数组

前面我们知道了指针其实也和int等一样,是一种数据类型。既然我们的int可以有数组,那我们指针是不是也可以建立数组呢?数组的每一个元素都是一个指针。

int* a[10];//10个int* 型的指针

每一个指针都是int*,以数组的形式获取各个指针。在debug中查看a[i]的数据类型为:int *。这里是理解指针的指针关键过渡。

指针的指针(二级指针)

在说指针的指针之前,我先和大家回顾一下之前一级指针和数组的关系,我们知道a[i] = *(a+i).数组名也是一个指针,也就是说 *a = a[]。那么对于指针的指针 p = &a。(a为一维数组的首地址,p为二级指针)。对指针的内存取地址得到的便是二级指针的内存内容。

int a = 10;//a = 10,&a = 0x8000			int元素
int* p = &a;//p = 0x8000,&p = 0xA808	int* 一级指针
int** q = &p;//q = 0xA808				int** 二级指针
printf("%d",*p);//10
printf("%d",**q);//10
printf("%d",a);//10

把指针数组和一级指针的知识扩展开来,将指针数组中的每一个指针指向一块数组空间。可以思考一下,现在的结构在逻辑上已经是一个二维数组。那么,可以分析得到,二维数组和二级指针对应。

int a[10][10] = {0};
int** p;
p = a;//a为2维数组的数组名,实际类型为 int(*) [10];
p[1][5];//等价于a[1][5]
int* x = *(p+1);//x = p[1] 为int* 型,一级指针
int y = *(*(p+2)+3)//y = p[2][3]

函数调用时,

#include<stdio.h>
void fun(int a[][10],int n)//a[n][m],m必须给出,并且和实参的m相同
/*a[m][n]可由int** a代替 */
{
    for(int i = 0;i<n;i++)
        for(int j = 0;j<10;j++)
            scanf("%d",&a[i][j]);
}
int main()
{
    int a[5][10];
    fun(a,5);
    return 0;
}

在字符串排序中的作用:

void swap(char *a,char *b)
{
    char * tmp = a;
    a = b;
    b = tmp;
}
void BubbleSort(char** s,int n)
{
    int i,j;
    for(i = 0;i<n-1;i++)
        for(j = 0;j<n-i-1;j++)
            if(strcmp(s[j],s[j+1])>0)
                swap(s[j],s[j+1]);
}

很显然,在这里我们可以完全把一级指针更具体的看作一个数据类型,和int,double等并无太大的差别,二级指针对一级指针中的内容进行修改,改变指针的指向是最常使用的功能。

函数指针

当我们在程序中定义了一个函数,在编译时系统就会为这个函数代码自动分配一段存储空间,函数名和数组名一样,作为一个指针,指向了这段存储空间的首地址。声明方法:

函数返回值类型 (* 指针变量名)(函数的参数);

使用时要求作为实际参数的函数其返回值,函数参数个数,类型一致。

#include<stdio.h>
void fun1(int *a, int *b){
    int tmp = a;
    a = b;
    b = tmp;
}
void fun2(int a[],int n,void (*f)(int*,int*)){
    int i , j;
    for(i = 0;i<n-1;i++)
        for(j = 0;j<n-1-i;j++)
            if(a[j]<a[j+1])
        	    (*f)(a+j,a+j+1);//函数指针调用
}
int main(){
	int a[5] = {0,2,3,4,1};
	fun2(a,5,fun1);//fun1作为函数指针的实参调用
	return 0;
}

注意

指针作为一个变量,它可以直接修改指向内存空间的数据,倘若没有对一个指针初始化,或者赋予一个可操作的空间,由系统给出的未定义数据,即野指针对数据进行修改将造成难以估计的影响,使用指针应当对每一个指针的指向都了然于胸,确保不会出现野指针的情况。

  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

registor11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值