当前位置 博文首页 > CW_qian的博客:7月30日笔记C语言基础指针1

    CW_qian的博客:7月30日笔记C语言基础指针1

    作者:[db:作者] 时间:2021-08-25 21:45

    目录

    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;