C语言基础之指针

一、地址概念的引入

1.看代码

#include<stdio.h>
int main()
{
    int juHuatai = 10;
    int meiGuiTai = 9;
    
    printf("变量名获取ju=%d\n",juHuaTai);
    printf("变量名获取mei=%d\n",meiGuiTai);
   
    printf("ju的地址是:%p\n",&juHuaTai);
    printf("mei的地址是:%p\n",&meiGuiTai);

    printf("通过地址来获取ju=%d\n",*(&juHuaTai));
    printf("通过地址来获取:%d\n",*(&meiGuiTai));
    return 0;
}

2.看运行结果
在这里插入图片描述
3.所以

1)变量的访问方式有两种:变量名和地址

2) & 取地址,取变量名所代表的变量的地址

3) 这里的 * 代表的是一个运算符,跟 ±*/ 类似,该运算符的作用是:取出地址内存中数据的值(取内容)

4) 指针 = 地址!!!

二、通过指针来访问变量

1.什么是指针变量?

  • 指针变量是存放指针的变量

  • 指针变量是存放地址的变量

2.比如看代码

#include<stdio.h>

int main()
{
    int a;   
    a = 10;

    char c;
    c = 'h';

    int *p;//定义了一个整型数指针 p 
    p = &a;//初始化指针 p = a的地址

    char *lp;//定义了一个字符型指针 lp
    lp = &c;//初始化指针 lp = c的地址

    printf("a = %d\n",a);
    printf("c = %c\n",c);

    printf("p = %d\n",*p);
    printf("lp = %c\n",*lp);
    return 0;
}

3.看运行结果
在这里插入图片描述
4.所以

a.整形变量存放的是整数
b.字符变量存放的是字符
c.指针变量存放的是地址

注意: 指针只有在定义一个指针变量的时候,才算是指针的标识符,其他情况,都是一种运算符,表示取内容

三、指针分类型&指针和数组

1.检测指针的偏移值

#include<stdio.h>

int main()
{
    int *p;
    char *p2;

    int a = 10;
    char c = 'A';

    p = &a;
    p2 = &c;

    //指针++ 
    printf("a的地址为:%p\n",p);
    printf("a的地址++后为:%p\n",++p);
    printf("c的地址为:%p\n",p2);
    printf("c的地址++后为:%p\n",++p2);

    int array[3] = {1,2,3};
    int *parray;
    parray = array;

    printf("第一个元素的地址是:%p\n",parray++);
    printf("第二个元素的地址是:%p\n",parray++);
    printf("第三个元素的地址是:%p\n",parray);
    //注意:此时数组的地址已经移到最后面了,如果使用要重新把地址移向数组的首地址 
    parray = array;//也可以 parray = array[0];
    int i;
    for(i=0;i<3;i++){
        printf("数组的第%d个元素是:%d\n",i+1,*parray++);//也可以用数组的下标来访问 array[i]

    }

    return 0;
}

2.看运行结果
在这里插入图片描述
3.所以

1.指针分类型
2.指针++根据类型不同,偏移值也不同
3.指针和数组关心的是数组的首地址,数组的第一个元素的地址,也可以是数组名
4.注意:注意指针偏移的位置,不要超出了数组的范围,因为数组的元素数据地址是连续的,超出了就会出现乱码,因为访问不到

四、数组指针函数的结合

#include<stdio.h>
#include<stdlib.h>
//提示用户输入10个成绩,找最高分,找最低分,算出平均分


void initScores(int *datas,int size)
{
     int i;
     for(i=0;i<size;i++){
         printf("请输入第%d个分数:\n",i+1);
         scanf("%d",datas);

         if(*datas > 100 || *datas < 0){
            printf("请输入0-100的分数成绩!\n");
            exit(-1);
         }
            datas++;
     }
}

void printScores(int *datas,int size)
{
     int i;
     for(i=0;i<size;i++){
         printf("你输入的第%d个成绩是:%d\n",i+1,*datas);
         datas++;
     }
}
int getMax(int *datas,int size)
{
    int i;
    int max;
    max = *datas;

    for(i=0;i<size;i++){
        if(max < *datas){
           max = *datas;
        }
        datas++;
    }

    return max;
}

int getMin(int *datas,int size)
{
    int i;
    int min;
    min = *datas;

    for(i=0;i<size;i++){
        if(min > *datas){
           min = *datas;
        }
        datas++;
    }
        return min;
}

float getAverage(int *datas,int size)
{
      int i;
      float average;
      int total = 0;
      for(i=0;i<size;i++){
          total += *datas;//total = total + datas[i];
          datas++;
      }
      average = (float)total / size;
      return average;
}

void printRet(int max,int min,float average)
{
     printf("最高分是:%d 最低分是:%d 平均分是:%f\n",max,min,average);
}
int main()
{
    int scores[10];
    int len;
    int max;
    int min;
    float average;

    len = sizeof(scores) / sizeof(scores[0]);

    initScores(scores,len);//函数调用中,传参就是一个赋值的过程!实际参数的值给形式参数
    printScores(scores,len);
    max = getMax(scores,len);
    min = getMin(scores,len);
    average = getAverage(scores,len);

    printRet(max,min,average);

    return 0;
}

五、为什么要用指针?

1.用指针让某个地址强制转换成我们需要的值

#include<stdio.h>
int main(){
    int *p = (int *)0x0060ff00;
    *p = 10;
    printf("在内存的%p的位置,存放的值是:%d\n",p,*p);
    //结果是:在内存的0x0060ff00的位置,存放的值是:10
    return 0;
}

2.来看两段代码
第一段

#include<stdio.h>

void jiajiaA(int p)
{
     p = p + 1;
     printf("调用函数中:a=%d\n",p);

}
int main()
{
    int a = 10;

    jiajiaA(a);

    printf("主函数的a=%d\n",a);

    return 0;
}

运行结果:
在这里插入图片描述

可以看到,主函数里面的a的值没有还是10,没有改变,从某种意义上说,这样调用函数毫无意义

第二段

#include<stdio.h>

void jiajiaA(int *p)//这里把a的地址传过来了,int *p = &a
{
     *p = *p + 1;
     printf("调用函数中:a=%d\n",*p);

}
int main()
{
    int a = 10;

    jiajiaA(&a);

    printf("主函数的a=%d\n",a);

    return 0;
}

看运行结果
在这里插入图片描述

可以看到,主函数里面的a的值也变成了11,前面学习函数说了函数传参是一个赋值的过程,调用函数中函数调用完就释放了数据内存,这里因为通过传递地址可以从根本上改变想要变量的值,而无关局部内存数据的释放,这也达到了它的意义

六、指针数组

1.顾名思义就是指针的数组,好多个指针放在一起,是一个数组,每一个元素都是指针

#include<stdio.h>

int main()
{
    int a = 2;
    int b = 3;
    int c = 4;//三个毫无关系的整型变量

    int array[3];//多个整数,叫做整数数组

    //这么定义一个指针数组 
    int *parray[3];//多个指针,叫做指针数组,数组中的每一项元素都是一个指针变量
                   //指针变量是存放地址的变量

    parray[0] = &a;
    parray[1] = &b;
    parray[2] = &c;//三个普通没有任何关系的整型变量的地址存入指针数组

    int i;
    for(i=0;i<3;i++){
        printf(" %d\n",*(parray[i]));//输出的结果无疑是:2 3 4 
    }

    return 0;
}

七、数组指针

1.顾名思义就是数组的指针,一个指针,这个指针存放的是数组的地址

#include<stdio.h>

int main()
{
    int array[3] = {1,2,3};

    int (*p)[3];//这样定一个指针
    p = array;

    int *p2;
    p2 = array;

    printf("偏移前p的地址为:%p\n",p);
    printf("偏移前p2的地址为:%p\n",p2);

    printf("偏移后p的地址++为:%p\n",++p);
    printf("偏移后p2的地址++为:%p\n",++p2);
}

2.看运行结果:
在这里插入图片描述
3.所以

数组的指针,强调的是类型,数组的个数,偏移值是偏移了整个数组的大小

八、函数指针

1.函数的指针,还是一个指针变量,它存放的是函数的地址
看代码

#include<stdio.h>
void print()
{
        printf("欢迎你的到来!\n");
}
int main()
{
        //如何定义一个函数指针!
        void (*p)(); //1.如何表示指针 * 2.如何知道是函数:() 3.函数指针是专用的,格式要求很强(参数类型,个数,返回值),就像数组指针一样

        //2.如何给函数指针赋值
        p = print;//函数名就是地址,就像数组一样,数组名就是地址

        //3.如何通过函数指针调用函数
        p();//直接通过指针名字+()

        (*p)();//取内容 (*指针名字)()
        
        return 0;
}

2.看运行结果:
在这里插入图片描述

九、无类型的指针&&malloc

  • 原型是: void *malloc(size_t size); 不知道指向的空间是什么属性
  • 通常用在没有做初始化的野指针上,为野指针从新定义类型和大小

1.下面的代码是,用无类型的指针malloc开辟出来的数据空间与数组的结合

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int n;
    printf("请输入数组元素总个数:\n");
    scanf("%d",&n);

    int *array = (int *)malloc(n * sizeof(int));
    if(array == NULL){
       printf("开辟空间失败!\n");
    }

    int i;
    for(i=0;i<n;i++){
        printf("请输入第%d个学生的成绩:\n",i+1);
        scanf("%d",&array[i]);
    }
    
    for(i=0;i<n;i++){
        printf("你输入的第%d个学生的成绩是:%d\n",i+1,array[i]);

    }
    
    return 0;
}

2.看运行结果:
在这里插入图片描述

十、什么是内存泄漏,如何避免

1)内存泄漏是指:程序刚跑起来很好,跑了几个小时,或者几天,几周,程序崩溃了

2)如何避免:a.首要怀疑对象就是循环中有没有,使用malloc函数不断的申请开辟空间 b.及时合理的释放,利用free(指针名); 指针名 = NULL;

十一、指针收官

1)定义一个整型变量? int a;

2)定义p为指向整形数据的指针变量? int *p;

3)定义整型数组a,他有五个元素? int array[5];

4)定义指针数组p,它有四个指向整型数据的指针元素构成? int *p[4];

5)p为指向包含4个元素的一维数组的指针变量? int (*p)[4];

6)f为返回整型函数值得函数? int f();

7)p为返回一个指针的函数,该指针指向整型数据? int *p();

8)p为指向函数的指针,该函数返回一个整型值? int (*p)();

9)p是一个指针变量,它指向一个指向整型数据的指针变量? int **p;

10)p是一个指针变量,其类型为void(空类型),不指向具体的对象? void *p;**

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值