C语言浅谈指针

写在前面:

本篇博客仅限于c语言的入门阶段,博主也是c语言的新人,这篇博客着重讲述数组指针与指针数组,指针函数与函数指针的简单区别,比较适合刚接触的萌新,若有不足或其他错误,可以私信博主来进行修改。

一、指针概念:

计算机中所有的数据都必须放在内存中,不同类型的数据占用的字节数不一样。为了正确地访问这些数据,必须为每个字节都编上号码。每个字节的编号是唯一的,根据编号可以准确地找到某个字节。

几种类型所占字节如下:

char:1字节

int:2字节(16位系统),4位(32、64位系统)

long:4字节(16、32位系统),8字节(64位系统)

float:4字节

double:8字节

long long:8字节

指针:3字节(16位系统),4字节(32位系统),8字节(64位系统)

而内存中字节的编号就被称为地址,也就是指针。

定义指针一般用*a来表示,定义指针后*a表示取值,&a表示取址。

示例如下:

#include

int main()
{
    int n;
    int *a=&n;
    *a=1;
    printf("%p,%d\n",&a,*a);
    return 0;
}

输出结果:0x16fdff410,1

上述代码中,首先我们定义了一个变量n,n有他自己的地址,a为整形的指针变量。*本身位取值符号,但当我们使用int *a是,其只是单纯的定义指针,并无其他意义。指针只能存储地址,因此上式需要将n的地址赋给*a,在运用取值符号将数字1放在*a的地址中。

二、数组与指针

在我们学习数组时,例如:int a[3],其中a为数组名3为数组中所包含的元素的个数,而在我们在主函数中调用子函数arr(int a[],int n)时,其形式为arr(a,3)。我们不难发现数组与指针在某方面有着异曲同工之妙。而事实上他们之间也有着异常紧密的联系。

1、指针数组:

表示形式:int *a[3]。

解释:指针数组的含义便是指针类型的数组,即数组中存储的为指针。

示例如下:

#include

int main()
{
    int a1[3]={1,2,3};
    int a2[3]={1,2,3};
    int a3[3]={1,2,3};
    int *p[3]={a1,a2,a3};
    printf("%p\n",*p);
    printf("%d\n",**p);
}

输出结果:

0x16fdff408

1

分析:定义一个指针数组,其数组中存放的为地址,即三个数组中的数组名。*p取起地址内的第一个元素(即第一个地址)。**p为指针的指针,接下来为大家详解。

指针的指针

就上述例题而言,由于指针是从右向左编译的,因此我们可以把他看为两部步:*p去第一个元素,即a1的地址,在**p去a1的第一个元素,即1.这就是指针的指针。

计算指针数组所占的字节数

#include

int main()
{
    int a1[3]={1,2,3};
    int a2[3]={1,2,3};
    int a3[3]={1,2,3};
    int *p[3]={a1,a2,a3};
    printf("%p\n",&p);
    printf("%p\n",&p+1);
}

输出结果:

0x16fdff3d0

0x16fdff3e8

上述的输出结果相减即为其所占的字节,经计算为24个字节,这是为什么呢?是因为在64位系统下指针固定占8个字节,因此3*8即为24。

2、数组指针

与上面的指针数组一样,数组指针便是数组类型的指针,它的本质上还是指针。

示例:

#include

int main()
{
    int a[3]={1,2,3};
    int (*b)[3]=&a;
    printf("%p\n",b);
    printf("%p\n",b+1);
    printf("%p\n",*b);
    printf("%p\n",*(b+1));
    printf("%d\n",**b);
    printf("%d\n",**(b+1));
    printf("%d\n",*((*b)+1));
    return 0;
}

输出结果:

0x16fdff408

0x16fdff414

0x16fdff408

0x16fdff414

1

0

2

分析:

上述代码中(*b)便是数组a[3]数组名的首地址,而*b则是去b数组中第一个元素,*b中保存的是数组a,因此相当于取数组a的数组名,所以第一、三个printf的结果一至。而因为保存的是数组名,因此当b+1是,它便已经是在数组的整体上+1,而这个整形数组中有3个元素,一个整形为四个字节,因此第一、二,三、四个printf相差为12。

事实上是开辟一个空间b来存储数组a的首地址,在我们使用时要进行两次转化,才能得到原始数组里所包含的数字。因此,数组指针事实上我们使用的并不多,只是有时候需要这种思想。

3、指针函数

指针函数与上面的一样,可以解读为返回值为指针类型的函数。那么返回指针类型有什么好处呢?又一个很关键的一点就是我们可以借助它来返回数组,示例如下:

#include

int *paixu(int a[],int n)
{
    int *b;
    int i,t;
    for(i=0;i<2;i++)
    {
        if(a[i]>a[i+1])
        {
            t=a[i];
            a[i]=a[i+1];
            a[i+1]=t;
        }
    }
    b=a;
    return b;
}

int main()
{
    int a[3]={2,1,3};
    int *b;
    int i;
    b=paixu(a,3);
    for(i=0;i<3;i++)
    {
        printf("%d ",b[i]);
    }
    printf("\n");
    return 0;
}

上述为一个简单的排序函数,当我们在排完序后需要返回是,便要通过指针函数来返回一个指针,在定义一个指针去接收它。当然,其实有一种更为简单的方法去解决这个问题

#include

void paixu(int a[],int n)
{
    int i,t;
    for(i=0;i<2;i++)
    {
        if(a[i]>a[i+1])
        {
            t=a[i];
            a[i]=a[i+1];
            a[i+1]=t;
        }
    }
}

int main()
{
    int a[3]={2,1,3};
    int i;
    paixu(a,3);
    for(i=0;i<3;i++)
    {
        printf("%d ",a[i]);
    }
    printf("\n");
    return 0;
}

将子函数定义为void型,使他在内部完成交换,都可以不需要使用指针,而且代码也会简化不少,当然有时后这种方法也是有局限的,我们还是要掌握指针函数的用法。

4、函数指针

函数指针为指向函数的指针,即子函数的参数可以为函数。示例如下:

#include

int max(int a[],int n)
{
    int i,max=a[0];
    for(i=0;imax)
        {
            max=a[i];
        }
    }
    return max;
}

int arr(int a[],int n,int b())
{
    int max;
    max=b(a,n);
    return max;
}

int main()
{
    int a[]={1,2,3};
    int b=arr(a,3,max);
    printf("%d\n",b);
    return 0;
}

最终的输出结果为3

上述代码定义了一个子函数arr,其中一个参数为函数指针b(),当然这个函数可以为自己定义的任何人函数,如求最小值min,求和add等等,不单单局限于一个。函数指针主要是为了避免当定义的子函数过多时,需要调用的子函数格式过于多,这时便可以采用函数指针对其进行化简。

总结:

当我们学习指针以后,我们不难发现,指针非常灵活,可以与我们所学的其他的数组、函数等进行紧密的结合,使其更加的灵活与多变,而这也正是为什么指针会被人们称为“c语言的灵魂”。

你可能感兴趣的:(c语言,开发语言,后端)