7月30日笔记C语言基础指针1

目录

1.什么是指针(存放地址的变量)?

     指针是一个变量,是用于存放地址的变量,我们称之为指针    cpu往往对地址处理的效率较高,仅次于汇编,能不能通过指针来操作    对应存放地址的变量呢?可以通过指针操作    2.如何获取对应数据存放的内存所对应的地址,是通过&(取地址符)获取对应变量的地址

    int a;//&a 获取a的地址    printf("%p\n",&a);    3.指针的定义

4.地址偏移量

字节序    1.大端模式:高位数据存放在内存的低地址端,低位数据存放在内存的高地址端    2.小端模式:高位数据存放在内存的高地址端,低位数据存放在内存的低地址端

5.函数传递地址

6.数组与指针    数名有两个含义        1.第一个含义,表示整个数组        2.第二个含义,表示首元素地址    // 例子    

6.1指针转数组

7.char型指针    因为我们对字符串的处理尤为重要,所以有时候需要用char *型指针对它进行处理

8.字符串常量与指针    字符串常量在内存中实际就是一个匿名数组    匿名数组满足数组的两个条件        1.第一个含义,表示整个数组        2.第二个含义,表示首元素地址

9.指针函数    函数的返回值为指针称为指针函数

10.函数指针    指向函数的指针变量称之为函数指针    特点:函数指针和普通指针本质上是没有任何区别    但是??        定义:        // 函数指针,注意*p需要()括起来,否则就是普通的函数了        int (*p)(int a,int b) 

11.回调函数(必须要掌握)    调用一个函数,给这个函数传递函数指针,被调用的这个    函数通过这个函数指针进行调用其它函数,我们把这种方式    称为回调函数    demo

12.指针数组    用于存放指针的数组称为指针数组    指针一般是用于指向字符串的首地址    // 二维数组    char buf[2][4] = {"abc","def"};        // 指针数组        char *buf[2];        demo 

13.数组指针    用于存放数组的地址的变量称为数组指针    又称为数组名的一个指针,及指向数组首元素的地址    // 一维数组    char buf[] = "123";    // 一维数组指针    char *p = buf;        //二维数组    char buf1[2][3] = {"ab","cd"};    // 注意(*p)一定要加括号,否者就是指针数组    char (*p)[3]   <===> char [3] (*p)

  段错误总结:


1.什么是指针(存放地址的变量)?

     指针是一个变量,是用于存放地址的变量,我们称之为指针
    cpu往往对地址处理的效率较高,仅次于汇编,能不能通过指针来操作
    对应存放地址的变量呢?可以通过指针操作
    
2.如何获取对应数据存放的内存所对应的地址,是通过&(取地址符)获取对应变量的地址

    int a;//&a 获取a的地址
    printf("%p\n",&a);
    
3.指针的定义

    int a = 1;
    printf("%d\n",1);
    printf("%p\n",&a);
    // 找一个变量存放地址,定义一个int *类型的便变量存放地址我们把 int * 这种类型称为指针
    int *p = &a;
    printf("%d\n",a);
    
    int b = *p; // *p表示获取地址a里面的数据,把这种方式称为解引用 
    printf("%d\n",*p); // 解引用获取p变量保存的地址里面的数据
    printf("%d\n",b);


注意:
    1. int *p; 是定义一个指针变量,int *p表示指针类型
       p = &a; p指向a ,实际就是p变量存放a的地址
    
    2. int b = *p;//*p表示为解引用,获取p变量所保存的地址里面的内容


    short int a = 10;
    short int *ps = &a;
    printf("%hd\n",*ps); // 
    printf("%ld\n",sizeof(ps));
    
    int b = 20;
    int *pb = &b;
    printf("%d\n",*pb);
    printf("%ld\n",sizeof(pb));
    
    long int c = 20;
    long int *pl = &c;
    printf("%ld\n",*pl);
    printf("%ld\n",sizeof(pl));
    
    float d = 3.14;
    float *pd = &d;
    printf("%f\n",*pd);
    printf("%ld\n",sizeof(pd));
    
    double e = 3.14;
    double *pe = &e;
    printf("%lf\n",*pe);
    printf("%ld\n",sizeof(pe));
    
    long double f = 3.14;
    long double *pf = &f;
    printf("%Lf\n",*pf);
    printf("%ld\n",sizeof(pf));

    总结 :
    注意所有的类型的指针大小都是8字节(64位系统),32位系统一般为4字节
    但是指针所存放的地址对应的空间的大小是与对应的数据类型相关  比如   int 4字节   char 1字节


4.地址偏移量

    int a[2] = {1,2};
    char *p = (char *)&a[0]; // 将p指向a0地址,因为a为数组,所以a[0]的地址称为基地址
    printf("%d,%d\n",*p,*(p+1));// 强制类型转换偏移1字节

    int *q = &a[0];
    printf("%p,%p\n",q,(q+1)); // 偏移4字节
    
    char b[2] = {'a','b'};
    char *q = &b[0]; // 将p指向a0地址,因为a为数组,所以a[0]的地址称为基地址
    printf("%c,%c\n",*q,*(q+1));
    


    int a = 0x11223344;
    int (*p) = &a;
    
    printf("p = %p\n",p); // 基地址
    printf("&a = %p\n",&a);
    
    printf("p+1 = %p\n",p+1); // 偏移后的地址   偏移4字节
    
    
    char c = 'b';
    char (*q) = &c;
    printf("q1 = %p\n",q); // 基地址
    printf("q+1 = %p\n",q+1); // 偏移后的地址    

总结 :

         地址偏移量是与数据类型的大小一致,比如int类型,它们之间的地址间隔是4字节, char 类型是1字节  强制类型转换会发生改变。


字节序
    1.大端模式:高位数据存放在内存的低地址端,低位数据存放在内存的高地址端
    2.小端模式:高位数据存放在内存的高地址端,低位数据存放在内存的低地址端


5.函数传递地址

void func(int *a)
    {
        int b = *a;
        printf("%d\n",b);
    }
    

 int main()
    {
        int a = 1;
        func(&a)
    }


练习:
    传递两个地址给子函数,子函数交换两个数值后,主函数将结果打印出来
    // 值传参
    void func1(int a,int b)
    {
        int temp = a;
        a = b;
        b = temp;
        printf("%d,%d\n",a,b);
        
    }
    // 地址传参
    void func2(int *a,int *b)
    {
        int temp = *a; // 解引用,获取a变量里面存放的地址里面的内容
        *a = *b;
        *b = temp;
    }
    void main()
    {
        int a =1,b=2;
        int c = 3,d = 4;
        func1(a,b);
        func2(&c,&d);
        printf("%d,%d\n",a,b); // 不能改变
        printf("%d,%d\n",c,d); // 可以改变
    }

总结:

        地址传参可以修改对应传递的数值,基本所有接口(函数)调用都是使用地址传递,
    所以一般接口的return都是返回对应的执行状态(成功与失败),类型为bool值。
    


6.数组与指针
    数名有两个含义
        1.第一个含义,表示整个数组
        2.第二个含义,表示首元素地址
    // 例子    

   void func(int *p)
    {
        printf("%d\n",*p);
        printf("%d\n",*(p+1));
        printf("%d\n",*(p+2));
    }

int main()
    {
        int a[3] = {1,2,3};
        
        // 在此情形下表示整个数组
        printf("%ld\n",sizeof(a));
        //int *p = &a;  // &a : 写法和编译器编译的时候会有出入,警告,不会报错,慎用,基本不用
        printf("p = %d\n",*(p+1));
        printf("p addr = %p\n",p);
  


 // 其它情形下数组都视为首元素地址,常用
        printf("%p\n",a);
        printf("%p\n",&a[0]);
        
        int *q = a;
        printf("q = %d\n",*(q+1));


        func(a);


   数组下标
    int a[3] = {1,2,3};
    int b = a[0];
    
    int c1 = *(a+0); // a[0]
    int c2 = *(a+1); // a[1]
    int c3 = *(a+2); // a[2]
    
    int d1 = *(0+a); // a[0]
    int d2 = *(1+a); // a[1]
    int d3 = *(2+a); // a[2]
    
    printf("2[a] = %d\n",2[a]); //  *(2+a) 仅限面试用

   总结:数组最后编译器会自动转为指针操作,数组运算其实就是指针运算。


6.1指针转数组

 int b[10];
    // 指针没有让它指向对应的空间,会出现段错误
    int *a = b;
    a[0] = 1;
    printf("%d\n",a[0]);
    

  总结 : 1.指针一定要指向一块合法的空间,否则出现段错误,没有空间自行分配
               2.可以将指针转换成数组使用,如上所示


 char buf[10] = "abc";
    int len = strlen(buf);
    
    int mc_Strlen(char *str)
    {
        ....
        return count;
    }
    int main()
    {
        MyStrLen(buf);
    }

  编译一个程序实现strlen()功能,将这个函数封装起来方便以后调用
    主函数调用此函数实现


7.char型指针
    因为我们对字符串的处理尤为重要,所以有时候需要用char *型指针对它进行处理

    char buf[4] = "abc";
    char *p = buf;
    

    char *q = "def";
    
    char *p1; // 段错误,因为p1没有给它指向对应的空间
    strcpy(p1,"hello");
    
    
    printf("%s,%s,%s\n",buf,p,q); // %s不需要解引用会自动解析地址里面的内容,只要地址空间连续,直到遇到'\0'结束

注意 : p指针所指向的空间一定要能存放对应的内容,因为对这个指针操作就相当于间接的对这个空间操作,因为这个指针p存放的是这个空间的地址    
    


8.字符串常量与指针
    字符串常量在内存中实际就是一个匿名数组
    匿名数组满足数组的两个条件
        1.第一个含义,表示整个数组
        2.第二个含义,表示首元素地址

    //demo
        char buf[] = "abcd";
    printf("%c\n",buf[1]);
    

    printf("%c\n","abcd"[1]);
    
    char *p = "abcd"; // 将p指向一块匿名数组的一个首地址
    printf("%p,%p\n","abcd",&"abcd"[0]);


9.指针函数
    函数的返回值为指针称为指针函数

    //demo
    // 指针函数
    char *func(char *buf)
    {
        return buf;
    }

 // 普通函数调用
    int func1(int a)
    {    // 因为变量的声明周期是属于这个函数,函数结束空间释放
        // 所以返回地址会出现段错误
        int b = a+1;
        return b;
    }

    int main()
    {
        char *str = func("abc");
        printf("%s\n",str);
        
        int a = 123;
        int b = func1(a);
        printf("b = %d\n",b);
        
        return 0;
    }


10.函数指针
    指向函数的指针变量称之为函数指针
    特点:函数指针和普通指针本质上是没有任何区别
    但是??
    
    定义:
        // 函数指针,注意*p需要()括起来,否则就是普通的函数了
        int (*p)(int a,int b) 

    demo1:
    int max(int a,int b)
    {
        return a>b?a:b;
    }

    // main其实就是程序的入口地址
    int main()
    {
        // 定义一个指针,*不能去掉
        int (*p)(int a,int b);
        // p指向的函数名与定义的函数名一致
        // 函数名就是这个函数的一个首地址
        //p = &max; // 将max的地址给p
        p = max;
        int a = 1,b = 2;
        // 通过指针调用max函数
        //int ret = (*p)(a,b);
        int ret = p(a,b);
        printf("%d\n",ret);
        
        return 0;
    }


    demo2:
    // 通过typedef,给函数指针定义一个类型,类型的名字叫p
    // 这时候p就是一个变量,和int char.. 相似
    // 增加函数指针的易用性
    // typedef给这个函数指针取别名相当于给它生成一个数据类型  ,和  int char 相似
    typedef int (*p)(int a,int b);


    int max(int a,int b)
    {
        return a>b?a:b;
    }
    int max1(int a,int b)
    {
        return a>b?a:b;
    }
    int main()
    {
        // 函数注册
        p q = max;
        p q1 = max1;
        int a = 1,b = 2;
        // 通过指针调用max函数

        int ret = q1(a,b);
        printf("%d\n",ret);
        return 0;
    }
    


11.回调函数(必须要掌握)
    调用一个函数,给这个函数传递函数指针,被调用的这个
    函数通过这个函数指针进行调用其它函数,我们把这种方式
    称为回调函数
    demo

    #include <stdio.h>
    #include <unistd.h>
    // 客户
    void printPhone(int len)
    {
        printf("手机好漂亮---%d\n",len);
    }

    // 手机店,回调函数,参数是一个函数指针类型
    void callback(int times,void (*print)(int len))
    {
        for(int i = 0;i < times;i++)
        {
            print(i+1);
            sleep(1);
        }
    }

    int main()
    {
        // 执行回调函数
        callback(5,printPhone); 
        return 0;
    }
    


12.指针数组
    用于存放指针的数组称为指针数组
    指针一般是用于指向字符串的首地址
    // 二维数组
    char buf[2][4] = {"abc","def"};
    
    // 指针数组    
    char *buf[2];
    
    demo 

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

    int func(char *buf[])
    {
        printf("%s\n",buf[1]);
        
        return 0;
    }

    int func2(char **buf)
    {
        //printf("%s\n",buf[1]);
        printf("%s\n",*(buf+1));
        
        return 0;
    }

    // 外部参数传递,程序外面传进来的参数
    // 例如  ./a.out abc def   传递了三个参数 分别为"./a.out","abc","def"
    int main(int argc,char *argv[])
    {
        
        if(argc != 2)
        {
            printf("请输入一个内容\n");
            return 0;
        }
        
        printf("argv = %d\n",argc);
        for(int i = 0;i < argc;i++)
            printf("argv[%d] = %s\n",i,argv[i]);
        
        // atoi将字符串转整数
        /*
            #include <stdlib.h>
        
            int atoi(const char *nptr);
            参数 nptr: 需要转为整数的内容
        */
        printf("%d\n",atoi(argv[1]));

        // 定义一个指针数组
        char *buf[2] = {"abc","def"};
        
        printf("%c\n",buf[0][0]);
        printf("%c\n",**buf);
        
        func(buf);
        func2(buf);
        
        
        return 0;
    }
    


13.数组指针
    用于存放数组的地址的变量称为数组指针
    又称为数组名的一个指针,及指向数组首元素的地址
    // 一维数组
    char buf[] = "123";
    // 一维数组指针
    char *p = buf;
    
    //二维数组
    char buf1[2][3] = {"ab","cd"};
    // 注意(*p)一定要加括号,否者就是指针数组
    char (*p)[3]   <===> char [3] (*p)


  段错误总结:

    1.内存没有分配

    2.内存溢出

    3.分配了内存被释放了
    一般遇到的段错误问题是,用一个指针直线给他赋值,没有让这个指针合理的指向空间导致的。
    
    
    
    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值