C语言-7月19日-指针的学习

目录

什么是指针?

指针的大小:

*的含义 :

解引用:

指针对数组的操作:

“[]”也具有解引用的功能

* (p + 1) <=> ar[2] 也就是 p + 1 <=>  &ar[2]

作业练习:

作业一:

作业二:

利用数组:

利用指针:

作业三:


今天对指针进行了系统的学习,回顾一下今天所讲的知识点:

什么是指针?

首先需要明确的是:指针,就是地址的别名,用来存放一个值的地址。

C语言-7月19日-指针的学习_第1张图片

指针的大小:

指针的大小与指针的级别无关,也与指针的类型无关。

例如我在这里分别设置int类型指针和double类型指针,我们分别来看他们的大小:

C语言-7月19日-指针的学习_第2张图片

如图,int类型的指针和double类型的指针的大小都是8字节,所以指针的大小与指针的类型并没有关系。

例如我在这里设置一级指针p和二级指针q,我们分别来看他们的大小:

C语言-7月19日-指针的学习_第3张图片

如图,一级指针和二级指针的大小都是8字节,所以指针的大小与指针的级数无关。 

指针的大小只和操作系统(OS)的种类有关,在x86架构(32bit)下,指针的大小就是4字节,在x64架构(64bit)下,指针的大小就是8字节。例如我的苹果电脑是64位操作系统,指针的大小就是8字节:

C语言-7月19日-指针的学习_第4张图片

*的含义 :

*的在C语言编程中有三种含义:

第一种:乘法的运算符号,表示方法为:数据*数据

第二种:解引用:*变量

第三种:指针的定义:类型 *名称

解引用:

在这里我查阅了相关资料,更加深入了解了解引用,“*” 属于指针的操作符,它的作用就是引用指针指向的变量值,前面说到过,指针的别名就是地址,也就是指针存放变量的地址,然后指针指向变量,所以*的作用就是引用指针所指向的变量值,引用其实就是引用该变量的地址,然后再利用指针操作符“*”将该地址对应的东西解开,所以就是解引用。

例如我这里写的代码:

整形变量a的值是10毋庸置疑,这里设置两个指针分别是p和q,其中指针p存放了变量a的地址,指向a,二级指针q存放了一级指针p的地址,而p因为存放了a的地址又指向a,所以二级指针q指向p最终指向a,所以解引用*p就是a本身,解引用**q也是a本身。

C语言-7月19日-指针的学习_第5张图片

如图分别将a,*p,**p以十进制输出,得到的结果均为10,上面论述有据可依。

指针对数组的操作:

我在这里给出一个元素个数为5的一维数组,通过指针随意调换数组中的两个数值。

例如我需要更换数组下标为0和数组下标为5的数,对应数组里面的第一个数和第五个数:

#include
#include
void Swap(int *ar,int index1,int index2)
{
    assert(ar != NULL && index1 >= 0 && index2 >=0);
    int temp = ar[index1];
    ar[index1] = ar[index2];
    ar[index2] = temp;
}
int main()
{
    int ar[5] = {1,2,3,4,5};
    int len = sizeof(ar) / sizeof(ar[0]);
    Swap(ar,0,4);
    for(int i = 0;i < len;i++){
        printf("%d ",ar[i]);
    }
    return 0;
}

 如图为运行结果,调换完成: 

 C语言-7月19日-指针的学习_第6张图片

这里我写了一个Swap函数,其中形参index1和index2对应的就是需要交换的元素的下标,通过指针指向数组的下标实现元素的调换。

对此程序进行分析:

main主程序中有ar(地址为0x1),在main主程序中调用Swap(ar,0,4),在Swap函数中,有指针int *占四字节,ar保存下方实参和形参的关系,属地址拷贝。*ar与主函数中的ar数组进行关联(地址拷贝),在程序中temp保存index1位置元素,通过swap中的ar指针访问到数组的index1号下标元素,此时0号下标对应的元素为1,赋值给temo,然后index2号下标元素,赋值给ar指向的index2号下标元素,然后把temp赋值给index1号下标。

“[]”也具有解引用的功能

在此程序中,解引用通过数组的下标来进行元素的访问,比如程序中的语句:ar[index1] = ar[index2];*ar为解引用,ar[0]也具有解引用的功能,这两者其实是有关联的:

*ar <=> *(ar + 0);

ar[2] <=> *(ar+2);

这类似于我的上一篇博客,二维数组的学习 中所提到的数组名加1,我在计算二维数组的行数的时候曾经说过,行数 = 数组的总大小 / 数组类型,而当我以十进制输出数组和数组+1时发现两个地址所产生的差值 / 数组中元素的类型名的大小时,得出来的结果正好是二维数组一行的个数:

C语言-7月19日-指针的学习_第7张图片

所以类型对指针变量起到两个作用:

(1)解析存储单元的大小

例如,int a = 10; int *p = &a; 因为指针类型为int 所以存储单元的大小为4字节

(2) 指针变量加1的能力

 因为在这里牵扯到计算机的存储方式问题,因为我是macOS,不同于Windows操作系统,已知Windows电脑的存储方式是小端存储,即最低地址存放最低字节,先来验证我的电脑是大端存储还是小端存储:

#include
void judge_litteEndian()
{
    int i = 48;
    int *p = &i;
    char c = 0;
    c = *((char *)p);
    if(c == '0'){
        printf("你的计算机的存储方式为小端存储\n");
    }
    else{
        printf("你的计算机的存储方式为大端存储\n");
    }
}
int main()
{
    judge_litteEndian();
    return 0;
}

将int 48存起来,然后取得其地址,再将这个地址转为char* 这时候,如果是小端存储,那么char*指针就指向48;
48对应的ASCII码为字符‘0’ 

运行结果为:

C语言-7月19日-指针的学习_第8张图片

我的操作系统的存储方式为小端存储。

在这里给出一个一位数组:

int ar[5] = {1,2,3,4,5};

C语言-7月19日-指针的学习_第9张图片

 按照小端存储的方式进行存储,数组元素1和2的地址分别为:0x00000001、0x00000002,如图填充地址,当定义指针p指向数组时,(p + 1)则代表的是指针向后偏移一个单元格,此时指向20,所以*(p + 1)就是引用(p + 1)指向元素的地址,然后解开对应地址的元素,也就是数组中的第二个元素,2 

所以在这里:

* (p + 1) <=> ar[2] 也就是 p + 1 <=>  &ar[2]

作业练习:

作业一:

编一个程序,输入月份号,输出该月的英文月名,例如,输入3,则输出“March”.要求用指针数组处理:

#include
int main()
{
    int m = 0;
    printf("Please input a number:\n");
    scanf("%d",&m);
    char *month[13] = {"Illegal_month","Januray","Feburay","March","April","May","June","July","August","Septmber","October","November","December"};
    if(m <= 12 && m>= 0){
        printf("The month is %s",*(month + m));
    }
    else{
        printf("The month is %s",*(month));
    }
    return 0;
}

我分别输出0和12,运行结果为:

C语言-7月19日-指针的学习_第10张图片 C语言-7月19日-指针的学习_第11张图片

输出完成 

作业二:

有n个整数,使前面各数顺序向后移m个位置,最后m个数变成最前面m个数,见图。写一个函数实现以上功能,在主函数中输入n个整数和输出调整后的n个数:

C语言-7月19日-指针的学习_第12张图片

利用数组:

#include
#include
#include
int array_backwards(int *array,int n,int m)
{
    assert(array != NULL && n > 0 && m >0);
    int i = 0;
    int j = 0;
    int temp = 0;
    for(i = 0;i < m;++i){
        temp = array[n - 1];
        for(j = n - 2;j >= 0;j--){
            array[j + 1] = array[j];
        }
        array[0] = temp;
    }
    return 0;
}
int main()
{
    int m = 0;
    int n = 0;
    printf("请输入数组元素的个数:\n");
    scanf("%d",&n);
    int array[n];
    printf("请输入这些元素:\n");
    for(int i = 0;i < n;++i){
        scanf("%d",&array[i]);
    }
    printf("请输入您需要移动的元素个数:\n");
    scanf("%d",&m);
    array_backwards(array,n,m);
    for(int i = 0;i < n;++i){
        printf("%d ",array[i]);
    }
    return 0;
}

运行结果:

C语言-7月19日-指针的学习_第13张图片

利用指针:

这个方法的优点就是不用开辟额外的空间,直接利用指针在原数组进行操作,时间和空间复杂度都较低,先来理解这个方法,

例如在这里我给出一个数组:

int ar[5] = {1,2,3,4,5};

这里我需要将前两位挪到后面,后面的三个数挪到最前面,最终结果为:3 4 5 1 2

这里分三步进行:

第一步:将原数组整个进行逆转:变为:5 4 3 2 1

第二步:对前三个元素进行操作,使其变成3 4 5,那么我只需要将0号下标元素和2号下标元素进行调换即可,此为局部逆转

第三步:对后两个元素进行操作,使其变成1 2,也就是将3号下标元素,与末尾元素进行调换

由于所有的逆转都要进行调换工作,在这里直接写一个利用指针进行调换的函数Swap

上述三步操作如何写成适用于所有数组的函数?

全局逆转可以利用调换函数Swap,从第一个元素和最后一个元素调转开始,依此类推,逆转的终止条件就是左右指针重合或者相邻。

局部逆转分为两段,第一段可以从0号下标元素和len - m - 1号下标元素逆转(m代表需要挪动的数据个数),循环;

第二段从len - m号下标元素到末尾元素(len - 1号下标元素)调换,循环。

将思路代码化:

void Swap(int *p,int *q)
{
    assert(p != nullptr && q != nullptr);
    int temp = *p;
    *p = *q;
    *q = temp;
}
void Reverse(int *ar,int begin_index,int end_index)
{
    assert(ar != nullptr && begin_index >= 0 && end_index >= 0);
    int *p = ar + begin_index;
    int *q = ar + end_index; 
    assert(p != nullptr && q != nullptr);
    while(p < q){//指针p和q的不重合和不相邻是循环的条件,反之则终止
        Swap(p,q);
        p++;
        q--;//每次循环后p指针向后偏移一位,q指针向前偏移一位
    }
}
void Adjust(int *ar,int len,int m)//调整函数
{
    assert(ar != nullptr && len >= 0 && m >= 0);
    Reverse(ar,0,len - 1);//对数组中的元素进行全部逆转
    Reverse(ar,0,len - m - 1);//区间逆转
    Reverse(ar,len -m,len - 1);//区间逆转
}
int main()
{
    int ar[5] = {1,2,3,4,5};
    int m = 3;
    Adjust(ar,5,3);
    for(int i = 0;i < 5;i++){
        printf("%2d",ar[i]);
    }
    return 0;
}

 运行结果:

C语言-7月19日-指针的学习_第14张图片

调换完成 

作业三:

写一函数,实现两个字符串的比较。 即自己写一个strcmp函数,函数原型为

mtstrcmp(char ·*pl.char ·><' p2);

设 pl 指向字符串 sl, p2 指向字符串 sZ。 要 求 当 sl=s2 时,返回值为 O;若 sl-::/=-s2.返回它 们二者第 1个不同字符的 ASCII码差值(如"BOY"与"BAD",第 2个字母不同,0与 A之差为79-65=14)。 如果sl>s2,则输出正值;如果sl

#include
int my_strcmp(char *p1,char *p2)
{
    int i = 0;
    while(*(p1 + i) == *(p2 + i)){
        if(*(p1 + i++) == '\0'){
            return (0);
        }
        else{
            return(*(p1 + i) - *(p2 + i));
        }
    }
}
int main()
{
    char str1[100];
    char str2[100];
    char *p1 = &str1[0];
    char *p2 = &str2[0];
    printf("请输入需要比较的字符串:\n");
    scanf("%s%s",str1,str2);
    printf("字符串比较后的结果是:%d ",my_strcmp(p1,p2));
    return 0;
}

例如我分别输入字符串:BOY和BAD,运行结果如图:

 C语言-7月19日-指针的学习_第15张图片

你可能感兴趣的:(C语言程序设计,c语言,学习,c++)