指针、数组指针、指针数组、指向指针的指针

1.指针

程序中一般通过变量名对变量进行存取操作,其实质是通过地址进行的。对变量的访问分为直接访问和间接访问,如语句“printf(“%d”,i);”,执行的时候,根据变量名 与地址的对应关系,找到i的地址,依次取相应的字节数中的数据(即i的值),把它输出。这种按照变量地址存取变量值的方式成为直接访问;还可以将变量i的地址存放在另一个变量中,假设该变量为i_pointer(在32位Windows操作系统下,无论指向什么类型的指针都只有4个字节),通过语句i_pointer=&i;将i的其实地址存放到i_pointer中,若要取变量,先找到存放i的地址的变量i_pointer,从中取出i的地址,然后从该地址开始的4个字节中取出i的值,这就是间接访问。一个变量的地址成为该变量的指针。用来存放变量地址的变量是指针变量。
指针变量可以做函数参数,我们知道,函数传参方式是“单向值传递”,即在被调用的函数中改变形参的值是无法改变调用处实参的值,因为形参是存放在栈中的,调用结束后,内存被释放,该数据的值也就不存在了。但是利用指针变量做函数参数,在函数执行的过程中使指针变量所指向的变量的值发生变化,函数调用结束后,这些变量值的变化依然保留下来,这样就实现了*”通过调用函数使变量的值发生变化,在主调函数中使用这些改变了的值“的目的。这种方式也遵循单向的”值传递“,即调用函数是不会改变指针变量的值,但是可以改变实参指针变量所指向变量的值。

2.数组指针

数组指针的实质还是指针,我们可以将其解释为指向由m个元素组成的一维数组的指针变量。我们先来了解二维数组名代表的含义,定义一个二维数组int a[3][4]={0};,我们知道,数组名代表数组首元素的地址,现在的首元素不是一个整型变量,而是由4个整型元素组成的一维数组,因此a代表的是首行的起始地址,即&a[0],a+1代表a[1]行的首地址,即&a[1]。假设a的首行起始地址为2000,则a+1所代表的地址为2000+4*4=2016。a[0],a[1],a[2]既然是一维数组名,而数组名又代表数组首元素地址,因此a[0]代表数组a[0]中0列元素的地址,即&a[0][0], a[1]的值是&a[1][0]。言归正传,现在我们想将数组名a赋值给一个指针变量,用语句 int *p=a;肯定是不行的,因为p指向一个整型数据,而a指向的是首行的4个数据,因此,我们定义语句int (*q)[4];其中p就是一个指针变量,它指向包含4个整型元素的以为数组。我们称q为数组指针。

3.指针数组

和数组指针一样,指针数组的实质是数组。如果一个数组,其元素均为指针类型数据,该数组称为指针数组。语句int p[4];中,[ ]比优先级高,因此p先和[ ]结合,显然p是一个数组名,有4个元素。再与p前面的结合,代表此数组是指针类型的,每个数组元素(相当于一个指针变量)都可指向一个整形变量。如 char name[]={“abc”,”def”,”ghi”};name是一个指针数组,他有3个元素,其初值分别是”abc”,”def”,”ghi”的起始地址。
下面我们来看一段运用指针数组的代码。

//对字符串从小到大排序

#include <stdio.h>
#include<stdlib.h>
#include<string.h>

void sort_str(char *name[], int n)
{
    int i, j, min;
    char *tem=NULL;
    for (i = 0; i < n - 1; i++)
    {
        min = i;
        //找最小元素下标
        for (j = i + 1; j < n;j++)
        if (strcmp(name[min], name[j])>0)//利用地址间接访问,name[i]即为第i个字符串首地址,根据地址找到字符串
            min = j;
        if (i != min)//如果最小元素下标变了,就进行交换,提高运行效率
        {
            tem = name[i];
            name[i] = name[min];
            name[min] = tem;
        }
    }
}

int main()
{
    char *name[] = { "china","apple", "hello","world"};
    int n=sizeof(name)/sizeof(name[0]);//结果为12,因为一个地址占4个字节
    sort_str(name, n);
    for (int i = 0; i < n; i++)
        printf("%s\n", name[i]);//name[i]中存放的是字符串的首地址
    system("pause");
    return 0;
}

4.指向指针的指针

指向指针数据的指针称为指向指针的指针。在上面的代码语句char name[] = { “china”,”apple”, “hello”,”world”};中,name是一个指针数组,它的每一个元素都是一个指针型数据,分别指向不同的字符串。数组名name代表该指针数组首元素的地址,name+是name[i]的首地址,由于name[i]的值是地址,因此name+i就是一个指向指针型数据的指针。所以,可以定义一个指针变量p,它指向指针数组的元素,由于该元素是指针型数据,所以,p就是指向指针型数据的指针变量,即指向指针的指针。定义一个指向指针型数据的指针变量格式为:类型 )p;如char ()p; 。运算符的结合性是从右至左的,因此可以改写为 char * * p;。

下面我们来看一段运用指针数组的代码。

#include <stdio.h>
#include<stdlib.h>

int main()
{
    char *name[] = { "china", "apple", "hello", "world" };
    char **p;
    p = name;//把首字符串的首地址赋值给p
    printf("%s\n", *p);
    //*p中存放的是"china"的首地址,根据这个地址,找到字符串"china"并输出。*p的类型是char *型的,即指向字符类型的指针变量。
    printf("%c\n", **p);
    //**p表示取*p这个地址所指向的数据,*p中存放的是"china"的首地址,根据首地址输出首字符c;**p是char 类型的,即一个字符。
    system("pause");
    return 0;
}

//结果是china    c

再来看下面这段代码,使我们对以上四个定义进一步了解:

#include <stdio.h>
#include<stdlib.h>

int main()
{
    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    //int *parr[10];//指针数组,parr是数组名,存放10和整型数据的地址
    //char** parr[10];//parr数组的大小10, 每个元素是char**类型的,即指向指针数据的指针
    //char ** (*ptr)[10];//ptr是指针,指向包含10个char **型元素的一维数组
    int(*ptr)[10];//数组指针,ptr是指针,指向包含10个整型元素的一维数组
    ptr = &arr;
    /*for (int i = 0; i < 10; i++)
        printf("%d ", (int *)ptr[i]);//加的是整个数组的偏移量,即ptr+1指向了0的下一个地址*/
    system("pause");
    return 0;
}

这段代码中只有定义ptr为int(ptr)[10];,即数组指针的形式, ptr = &arr;才能编译过去,因为&arr是取整个数组赋值给prt,所以接收它的prt也必须是指向10个整型元素的指针;注意这个语句,for (int i = 0; i < 10; i++) ,printf(“%d “, (int )ptr[i]);我将其注释掉,也就是不能企图通过这种方式将数组的10个元素输出,应为prt+1,1代表的是偏移量,此处偏移量为整个数组的长度,即10,所以,当i=0的时候输出1,i=1的时候输出的是数组最后一个元素的下一段内存地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值