zoukankan      html  css  js  c++  java
  • 1.3 C语言--指针与结构体

    指针

    指针概念的引入

    • 关于内存

      程序有数据和指令组成,数据和指令在执行过程中存放在内存中。变量是程序数据中的一种,因此变量也存储在内存中;内存中的每个字节都有一个唯一的编码,即内存地址。32位机的内存地址编码是32位的(所以32的内存最多4G),64位机的内存地址编码是64位的。地址是一个无符号的整数,从0开始递增,通常把地址写成十六进制数。地址的长度跟主机的字长相同。

    • 关于变量的寻址
      • 直接寻址(通过地址直接访问值)

        直接到变量名标识的存储单元中读取变量的值。最常见的就是scanf函数,scanf("%d",&a),这里的&表示取地址,是将获取到的值存储到a这个地址里面。如果我们这里不写&取地址运算符,获取的就是a的地址(?为什么直接写a,获取的是a的地址,&a是取a地址,这个不是相互矛盾吗?

      • 间接寻址

        通过其他变量间接的找到变量的地址,最常用的就是指针。指针类型就是指向变量的地址。

    • 变量在内存中所占存储空间的首地址,称为变量的地址;变量在存储空间的数据,称为数据的值;变量的名称可以看成是对程序中存储空间的抽象;声明一个变量实际上是记住这个变量的首地址和该数据类型的长度。
      • 变量名通常是首地址和长度的统一,可以理解是一种映射
      • 而&(取地址运算)是取的该变量的首地址。
    • 指针的定义

      int *pa; // 定义语句 表示pa保存的是一个int数据类型的地址(不要理解成地址是一个int类型的,表明首地址,并说明数据类型(推断出长度))

      int *pa = &a; // 初始化,也可以写成int a = 1;pa = &a;因为*pa是一个地址,而&a是取地址。所以两者是可以赋值的。

      我们可以吧指针理解为一个软连接的概念。

      • 如果将指针理解为java中的包装类,java中的包装类也是通过地址进行访问的
    • 指针使用必须被初始化
      • int *p = null ; // null是空指针,随便给一个地址。
    • 代码分析

      # include <stdio.h>

      int main(){

      int a=0,b=1;

      int *pa,*pb;

      pa = &a;

      pb = &b;

      printf("a = %d,b=%d ",a,b);

      printf("*pa=%d,*pb=%d ",*pa,*pb);

      return 0;

      }

    • 再次关于内存地址
      • 我们将的内存地址是逻辑地址,而不是物理地址。就像电流只要联通电线就可以到达一样,计算机访问在物理层面也只是电流的访问而已,根本不需要什么地址。内存地址是方便人理解的逻辑地址,这个可以理解为是电表的编号。
      • 所以int a = 10;中的a就是(逻辑)地址(一堆二进制的开关)没错,但是计算机无法理解逻辑地址,对它来说就是按照开关放电,所以直接就是a的值了。
      • &a是取(逻辑)地址,就是把这个a物理地址转换成可见逻辑地址,让我们看到
      • 指针并不等于内存地址。内存地址声明就无法更改。但是指针可以指向不同的内存逻辑地址。因此指针的指向的地址是可变的。
      • *pa一旦指向一个地址,我们访问*pa,就是访问*pa指向的(逻辑)地址,计算机自动将逻辑地址转换成物理地址,所以访问的其实就是*pa逻辑地址所代表的物理值。
      • scanf("%d",&a) // 表示将a的地址指向输入的值得地址

       

    指针的定义

    定义指针

    • 定义与初始化指针

      int *a=NULL; // 这里的int表示指针指向的地址是要存放int类型的数据。这里的*表示指针的解引用,a实际是指针指向的地址,而*a表示a指向的内存地址所代表的值;指针必须被初始化,没有初始化的指针是无法使用的。为了防止意外的发生,通常高我们会将指针命名为null。

    • 指针的赋值与取值运算
      • p=&a // 将a的地址给p;&表示取地址。必须理解的是指针是一种数据类型,专门涌过来存放地址。这跟基本类型的声明很像 int a=10;就是将10这个int类型的数据给变量a.
      • prinf("%d",*p); // 这里的*表示指针的解引用,就是说这里不输出p存储的地址,而输出p存储的地址里面存储的值。
      • printf("%p",p); // 这里的%p专门用来格式化输出指针里面存储的地址。这里输出的是指针p存储的地址。

    指针与变量的区别

    • 变量的声明与初始化

      int a = 0; // 这里的a只是我们标识某个内存空间的值,是方便我们识别用的。计算机会将a这个标识符映射为逻辑地址,再转换成物理地址,最后输出值的。而*p则是直接访问p所存储的地址的值

    • 区别

      int a = 0;

      int *p = null;

      p = &a;

      printf("%d",*p);

         

      解释: a表示0这个变量的长度和首地址,&a表示首地址;*p表示首地址和长度,p表示这个int类型的地址存什么值,&p是*p的地址。可以将a理解为抽屉A,p理解为抽屉B,通过指针访问就是抽屉B中放着抽屉A的钥匙.

      int a = 10;实际上a也是地址值,只是通过这个地址值能直接访问到该值。而p=&a 就是将a的地址值给p,当我们方位地址p中的地址是,就能通过地址获地址代表的值。

       

    指针的用处

    指针存在的意义就是为了间接引用

       

    #include <stdio.h>

       

    main(){

    // int *p = 10; // *p 接收的只能是地址

    int a = 10;

    // int *p = a; // 这句话是错误的。

    int *p = &a;

    printf("p is %d ",p);

    printf("*p is %d ",*p);

    printf("&p is %d ",&p);

    }

    必备代码:如何使用指针交换连个值。

    #include <stdio.h>

       

    void swap(int *a,int *b);

       

    /*

    * 使用指针交换用户输入的两个数据

    */

       

    void main() {

    int a,b;

    printf("Please type two number value of a,b: ");

    scanf("%d,%d",&a,&b);

    printf("before swap,the value of a is %d,the value of b is %d. ",a,b);

    swap(&a,&b);

    printf("after swap,the value of a is %d,the value of b is %d. ",a,b);

    }

       

    /*

    * 始终不是很明白为什么这里使用int middle 来接收指针*/

    void swap(int *a,int *b) {

    int middle ;

    middle = *a;

    printf("%d",*middle);

    *a = *b;

    *b = middle;

       

    }

       

    • 结构体与共用体
      • 结构体
        • 什么是结构体
          • 可以理解为java中的类,即只有成员变量没有成员方法的类。
        • 结构体的声明与使用
          • 声明格式(尤其注意几处分号)

            struct 结构体名称{

            数据类型 成员变量名称;

            ……

            };

          • 具体的声明,赋值与调用

            #include <stdio.h>

               

            struct student{

            int age;

            int class;

            int score[3];

            };

               

            void main(){

            // 初始化并赋值方式之一:

            struct student stu1;

            stu1.age=10;

            stu1.class=1;

            // stu1.socre[] = {11,22,33}; // 这种赋值错误的,反正我也不知道为什么

            stu1.score[0]=11;

            stu1.score[1]=22;

            stu1.score[2]=33;

               

            // 初始化并赋值方式之二

            /* struct student stu2;

            stu2 = {10,1,{11,22,33}}; // 这种赋值方式是错误的 */

               

            struct student stu2 ={10,1,{11,22,33}};

               

            printf("This is content of student struct: ");

            printf("student age is : %d ",stu1.age);

            for(int i=0;i<3;i++){

            printf("student's score:%d ",stu1.score[i]);

            }

            }

        • 各种使用方式
          • 使用typedef定义数据类型
            • typedef本质上只是一个已有数据类型定义别名的东西。主要是为了声明时节省一个关键字而已。

              #include <stdio.h>

                 

              void main(){

              /*

              * 定义一个结构体

              * 使用typedef 关键字,将student的别名设置为了STUDENT

              * 这样的话在声明结构体时就可以省掉一个struct关键字*/

              typedef struct student{

              int age=10;

              char name[10];

              }STUDENT;

              // 声明一个结构体

              STUDENT st1={1,{'h','u','a','b','i','n'}};

              }

          • 结构体的声明
            • 方式一: 结构体别名 具体结构体名;
            • 方式二:struct 结构体名 具体结构体名;
          • 结构体的赋值
            • 方式一:struct student stu2 ={10,1,{11,22,33}};
            • 方式二:stu1.score[0]=11;
            • 注意:有两种赋值方式是不允许的

              stu1.socre[] = {11,22,33};

              • 或者

                struct student stu2;

                stu2 = {10,1,{11,22,33}};

          • 结构体成员变量的使用
            • 结构体名.变量名
        • 结构体的字节数
          • 建议使用typeof()关键字来具体的查看
      • 结构体指针
        • 结构体是值传递,是不会修改具体的内容的
        • 一旦传递指针作为函数的参数,必须使用->来访问成员变量
      • 结构体中嵌套结构体
        • 使用结构体.结构体.成员变量 来实现访问

             

             

           

  • 相关阅读:
    344. 反转字符串
    942. 增减字符串匹配
    CGO内部机制
    CGO函数调用
    CGO类型转换
    CGO基础
    Go net/http代理
    GO-中间件(Middleware )
    Go如何巧妙使用runtime.SetFinalizer
    ARM基础
  • 原文地址:https://www.cnblogs.com/yanghuabin/p/9761085.html
Copyright © 2011-2022 走看看