欢迎光临
我们一直在努力

C语言 指针精要:内存操作与数组访问

目录

(重点、难点)第七章  指针

一、特点

二、基本概念

内存&地址&指针变量

三、指针相关的运算符

&(取地址运算符)

*(解引用运算符)

四、指针的定义

五、指针的初始化

!!(使用空指针时)

!注意注意!

六、指针的算术运算

 加 :+ 

减 :-

加加 :++

减减 :–

不同类型指针执行解引用操作

其他操作

七、!指针与数组(重要)

1、指针和数组的sizeof不同               

2、指针和数组的取地址不同

3、使用指针访问,数组的元素                                                                                                    

八、函数使用指针作为参数

1、值传递

2、地址传递

九、字符串数组的传参(地址传参)

1、字符串用指针表示刷出

2、字符串的有效长度  *strlen

3、字符串的复制  *strcpy

4、字符串的拼接 *strcat

5、字符串的比较 *strcmp


(重点、难点)第七章  指针

一、特点

  • 编写代码更加简洁高效
  • 指针可以直接访问内存,要访问合法的内存(不要越界访问)
  • 指针可以直接操作硬件,一般要通过软件操作硬件(51单片机、ARM开发板)

变量、数组,为了存储数据

指针,为了高效的访问数据,间接访问

二、基本概念

内存&地址&指针变量
  • 内存,memory:

是程序运行起来后的一个 数据暂存 的场所。在32bit下这段空间是0-4G。C语言中,操作内存的时候,最小单位是1byte(1个字节)。code(代码段),data(数据段),heap(堆区),map/share(映射/共享区,c库函数在这里),stack(栈,局部变量、函数参数、返回地址在这里)

  • 地址:

由于地址空间比较大,c语言中,最小操作单位是1byte,为了方便访问内存,给每个内存都分配了一个编号,被分配的编号叫内存地址

  • 指针变量:

指针变量是专门用来存储地址的变量。指针变量只装地址

#include <stdio.h>

int main()
{
int a=20;
//指针的定义,你是整型变量,指针也要整型;指针与类型是相对应的
int *p = &a; // &a: 获得a变量在内存中的地址

printf("a address:%p\\n",&a);
printf("p:%p\\n",p);
printf("p size :%lu\\n",sizeof(p));
return 0;
}

a address:   0x7ffde8d46a1c p:    0x7ffde8d46a1c p size :  8

  • a占4个字节,存放在0x7ffde8d46a1c这个地址,可以直接访问a,也可以间接通过指针访问,在64bit系统下,所有指针都是8个字节,指针里面装的是a的地址。

三、指针相关的运算符

  • &(取地址运算符)

&:获得变量在内存空间中的地址

  • 只能给左值,也就是变量取地址
  • int a;&a; &a是一个表达式,表达式的值,表示的是a这个变量在内存当中的首地址。             类型  int * 
  • int num = 0;
    &num; //地址,从这个地址开始往后四个字节的空间都是num的(因为int是4个字节)

    double num1=0;
    &num1; //从这个地址开始往后8个字节都是num1的

    • *(解引用运算符)

    只能是指针类型才需要解引用。

    *:先找到对应的内存地址值,获得内存地址空间中的内容

    第一个*是类型说明符,第二个*(指针访问被指向的对象)才是解引用

    *p;  值是对应内存空间中的数据,类型是int,*p在实际操作中既可以做左值也可以做右值。

    *p = 10;  // 给a写入数据,*p做的是左值

    int a=20;
    int *p= &a; //左边定义指针,指针里面存地址空间,把a的地址装进去
    a; //20,直接访问
    *p; //20,间接访问,值是对应内存空间中的数据,类型是int

    int b=*p;  //把*p对应的值装进去,这里*p做的是右值,读取*p也就是a的内容

    *&a;  20,会产生临时的指针变量, & 和 * 是相反操作的运算符

    int p;

    int *p;  //p    int –>int *

    *p;          //int *–>int

    四、指针的定义

    基类型   *    指针名;

    int *p1;       //int * char *p2;   //char * double *p3;   //double *

    float *p4;    //float *

    #include <stdio.h>
    int main()
    {
    int a=10;
    printf("a %p\\n",&a);

    //定义指针变量,注意这里的*不是解引用,是类型说明符
    int *p1;
    char *p2;
    double *p3;
    float *p4;
    //64bit操作系统,所有的指针都是8个字节
    printf("int * sizeof %lu\\n",sizeof(p1));
    printf("char * sizeof %lu\\n",sizeof(p2));
    printf("double * sizeof %lu\\n",sizeof(p3));
    printf("float * sizeof %lu\\n",sizeof(p4));
    return 0;
    }

    五、指针的初始化

    指针需要初始化后才可以使用,指针必须和某个内存区域关联后才可以使用

    • 野指针:未经初始化的指针或者,指针指向的内存空间是一个已经被释放的空间的指针      int *p;   //野指针,p中存储的地址是一个随机值,从逻辑上来说,这个值不能读也不能写。
    • 空指针:NULL(宏定义),在stdio.h中。                                                                           int*p1=NULL;  //空指针,NULL==((void*)0),指针已经初始化,稍后才会关联一个变量的地址。在没有关联变量地址之前,也不能读不能写。

    #include <stdio.h>
    int main()
    {
    int a=10;
    printf("a %p\\n",&a);

    int *p1; //int *
    char *p2; //char *
    double *p3; //double *
    float *p4; //float *

    #if 0
    //这个地方的代码,有崩溃的风险
    printf("p1 %d\\n",*p1); //p1:野指针
    *p1 = 20;
    #endif

    //定义一个变量,现在p5是空指针
    int *p5=NULL; // NULL是0地址,也就是 ((void*) 0)
    int *p6=&a;

    printf("p6 addr %p\\n",p6);
    printf("a addr %p\\n",&a);

    printf("a value:%d\\n",*p6); //通过指针,间接读取a变量的值

    *p6 =50; //左值,修改变量
    printf("修改后a value:%d\\n",*p6);
    printf("a value:%d\\n",a);// 直接访问a变量的值
    return 0;
    }

    !!(使用空指针时)

    int d=10;
    int* p=NULL;

    p=&d; //取地址d给p
    *p=d; //关联上,就可以取值了

    printf("p %p\\n",p);
    printf("%d\\n",*p);

    !注意注意!

    如果先写*p=d,两个还没有关联,不能这样操作!

    六、指针的算术运算

    +      –        *       /      %      ++         —

    *    /    %,相对于指针而言,无意义

    int a =20;

    int* p1=&a;

    +,p1+1; //依然是指针,往后走一个基类型(int类型有4个字节),如果一个指针需要+1、-1,一般要和数组操作的时候才会使用。

     加 :+ 

    两指针单独相加无意义

    +,p+1(数组),p(存储某个变量的地址)+q(存储另外一个变量的地址)==无意义

    • 不同指针类型在做+1时候的区别:
    指针类型+1 偏移量
    char* 1个字节
    int* 4个字节
    float* 4个字节
    double* 8个字节
    减 :-

    p-1  p-2;   p应该指向一个数组,操作完成后,指针应该落在数组范围以内

    int a[10]={1,2,3,4,5,6,7,8,9,0};
    int* p=a+1;
    int* q=a+5;

    printf("q-p:%ld\\n",q-p);

    • 两个地址相减,前提是这两个指针指向同一个数组。
    • 意义:相差的元素个数。
    • 另外,如果这两个指针指向其他不同的地方,相减无意义
    加加 :++

    p++;先获得当前地址,加上sizeof(基类型),计算的结果再写回指针变量本身。

    减减 :–

    p–;先获得当前地址,减去sizeof(基类型),计算的结果再写回指针变量本身。

    不同类型指针执行解引用操作
    不同类型指针解引用 操作
    char* 执行解引用操作,从指针存储的地址开始,往后(地址变大的方向)取1个字节的数据
    int* 执行解引用操作,从指针存储的地址开始,往后(地址变大的方向)取4个字节的数据
    float* 执行解引用操作,从指针存储的地址开始,往后(地址变大的方向)取4个字节的数据
    double* 执行解引用操作,从指针存储的地址开始,往后(地址变大的方向)取8个字节的数据

    char str[100]="hello";

    char* p=str;

    *p     :从存储地址的开始,往后走一个字节的数据

    其他操作

    指针在使用的时候,有两个概念需要注意:(1)指针变量本身(2)指针指向的内容

    对指针变量的修改

    *p =123;     修改指针指向的内容

    p=b;       修改指针的指向

    七、!指针与数组(重要)

        数组&指针  :数组名与指针名息息相关

        printf("a[0] %d\\n",a[0]);       printf("point *(p+0) %d\\n",*(p+0));     printf("*(a+0) %d\\n",*(a+0));     printf("point p[0] %d\\n",p[0]);

    • 数组的数组名,是一个指向数组中第一个元素的指针常量,数组一旦定义好,a中存储的地址不能发生变化。数组名的本质就是一个指针常量,需要注意的是:这个指针变量已经加了一些限制,让指针中存储的地址不能改变。(const)
    • int* ~=  int[ ]       
    1、指针和数组的sizeof不同               
    •  sizeof 数组:    int a[50];    sizeof(a)==sizeof(int)*50;                                                            
    •  sizeof 指针:    int* p;      sizeof(*p)永远是8byte;
    2、指针和数组的取地址不同
    •  &   int*   执行取地址操作    变成int**(二级指针),存储地址的地址
    •  int[ ]执行取地址操作,变成int(*p)[ ],数组指针,指向二维数组中的一行
    3、使用指针访问,数组的元素                                                                                                    

        访问元素的方式:a[n] –>  *(p+n)  –>  *(a+n)  –> p[n]   

    #include <stdio.h>
    int main()
    {
    int a[5]={1,3,5,7,9};
    printf("a addr %p\\n",a);
    printf("a[0] addr %p\\n",&a[0]);

    int* p =a;
    printf("a[0] %d\\n",a[0]);
    printf("point *(p+0) %d\\n",*(p+0));
    printf("*(a+0) %d\\n",*(a+0));
    printf("point p[0] %d\\n",p[0]);

    printf("a[0] addr %p\\n",&a[0]);
    printf("point *(p+0) addr %p\\n",&(*(p+0)));
    printf("*(a+0) addr %p\\n",&(*(a+0)));
    printf("point p[0] addr %p\\n",&p[0]);
    /*
    printf("a[1] %d\\n",a[1]);
    printf("point a[1] %d\\n",*(p+1));

    printf("a[4] %d\\n",a[4]);
    printf("point a[4] %d\\n",*(p+4));
    */

    return 0;
    }

    八、函数使用指针作为参数

    1、值传递

    形参是实参的复制品,只能读取实参的数据,在被调函数内部,无法修改形参而改变实参。

    涉及的变量有:基本数据类型的变量、构造类型的变量

    2、地址传递

    形参会接收到实参传递过来的地址,就可以在被调函数的内部,通过间接访问的方式,可以通过修改形参来修改实参。传递数组、指针。

    eg:两数交换用函数,值传递无法进行交换,地址传递可以通过间接访问,修改形参来改变实参,显而易见,swap()是一个值传递,在函数运行完成后,swap()释放,无法进行main函数中的两数交换;swap2()是一个地址传递,可以成功交换两个数。

    #include <stdio.h>

    void swap(int a,int b)
    {
    int t = a;
    a = b;
    b =t;
    }

    void swap2(int *pa,int *pb)
    {
    int t = *pa;
    *pa = *pb;
    *pb =t;

    }
    int main()
    {

    int a = 10;
    int b =20;

    //swap(a,b);
    swap2(&a,&b);

    printf("a :%d b:%d\\n",a,b);
    return 0;

    }

    九、字符串数组的传参(地址传参)

    1、字符串用指针表示刷出

    void myshow(char* pstr)
    {
    /*
    int i = 0;
    for(i=0;'\\0'!=pstr[i];i++)
    {
    printf("%c",pstr[i]);
    }
    */

    while(*pstr)
    {
    printf("%c",*pstr);
    pstr++;
    }
    return ;
    }

    2、字符串的有效长度  *strlen

    int mystrlen(char* pstr)
    {
    int len= 0;
    while(*pstr)
    {
    len++;
    pstr++;
    }

    return len;
    }

    3、字符串的复制  *strcpy

    方法1

    int mystrcpy(char* dst,char* src)
    {
    while(*dst=*src)
    {
    src++;
    dst++;
    }

    方法2

    int mystrcpy(char* dst,char* src)
    while(1)
    {
    *dst=*src;
    src++;
    if('\\0'==*src)
    {
    break;
    }
    dst++;
    }
    //*(dst+1)='\\0';
    *(++dst) ='\\0';
    return 0;
    }

    4、字符串的拼接 *strcat

    int my_strcat(char* dst,char* src)
    {
    while(*dst)
    {
    dst++;
    }
    while(*dst=*src)
    {
    src++;
    dst++;
    }
    return *dst;
    }

    5、字符串的比较 *strcmp

    int my_strcmp(char* str1,char* str2)
    {
    while(*str1==*str2 && *str1) //解引用,指针内容
    {
    str1++; //指针往后走
    str2++;
    }
    return *str1-*str2;
    }

    //main函数里
    int ret=my_strcmp(str,dst);
    if(0==ret)
    {printf("same\\n");
    }
    else
    {
    printf("not same\\n");
    }

    十、数组名与普通指针访问数据的注意事项

    int main() {     char str[100]="hello";     char* p=str; /*           while(*p)       {            printf("%c",*p);            p++;      //可以++、–操作,p本身被修改       }

    */

    /*

          while(*str)       {           printf("%c",*str);           str++;   //数组名,一个指针常量,本身不可以++、–。当数组被定义好后,str是一个地址标号,和开辟的内存空间的编号,终身绑定,不能修改。       }

    */

          show(str); }  

    赞(0)
    未经允许不得转载:171主机测评 » C语言 指针精要:内存操作与数组访问
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址