自学内容网 自学内容网

嵌入式开发学习日记——深入了解指针,初步了解结构体(c语言)

目录

一、const 指针和指针const

二、多级指针

三、空指针

四、野指针

五、枚举

六、结构体

声明结构体变量

结构体指针

 总结:


一、const 指针和指针const

1.const 指针

        const放在*的左边,限制的是指针指向的内容,意思是不能通过指针来修改指针指向的内容,但是指针变量本身是可以修改的。

#include <stdio.h>
int main()
{
  // const 指针:就是一个指针,指向一个内存地址
  int num1 = 10, num2 = 20;
  const int *ptr1 = &num1; // 写法1,const 指针指向一个内存地址
  printf("num1 = %d,*ptr1 = %d \n", num1, *ptr1);
  int const *ptr2 = &num2; // 写法2,const 指针指向一个内存地址
  printf("num2 = %d,*ptr2 = %d \n", num2, *ptr2);
  // *ptr1 = 200; // 此时ptr1指向的内存地址中的值不能更改
  // *ptr2 = 300; // 此时ptr2指向的内存地址中的值不能更改
  int num3 = 100;
  ptr1 = &num3;
  ptr2 = &num3;
  printf("num3 = %d,*ptr1 = %d,*ptr2 = %d \n", num3, *ptr1, *ptr2);
  // 总结:const 指针,指向可以改,指向的内存地址中的值不能更改
  return 0;
}

2.指针const

        const放在*的右边,限制的是指针变量本身,意思是不能修改指针变量的指向,但是可以修改指针指向的内容。

#include <stdio.h>
int main()
{
  // 指针 const:就是一个常量,存储了一个内存地址
  int num1 = 10, num2 = 20;
  int *const ptr1 = &num1; // 常量ptr1 存储了变量的内存地址
  printf("num1 = %d,*ptr1 = %d \n", num1, *ptr1);
  // ptr1 = &num2; // 会报错 此时ptr1指向的内存地址不可更改的.
  *ptr1 = 100; // ptr1常量指向的内存第中的值是可以修改的
  printf("num1 = %d,*ptr1 = %d \n", num1, *ptr1);
  // 总结:指针 const,指向的内存地址不可以更改,指向的内存地址中的值可以进行更改
  return 0;
}

二、多级指针

        指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

多级指针的定义与使用

1.声明多级指针时,需要使用多个星号来表示指针的级别。

 int num = 10;
  int *ptr1; // 一级指针
  int **ptr2; // 二级指针
  int ***ptr3; // 多级指针

2.初始化多级指针时,你需要逐级给指针赋值,确保每个级别的指针指向正确的目标。

  int num = 10;
  int *ptr1 = &num;    // 一级指针指向了num变量的内存地址
  int **ptr2 = &ptr1;  // 二级指针指向了ptr1指针变量的内存地址
  int ***ptr3 = &ptr2; // 三级指针指向了ptr2指针变量的内存地址

3.解引用多级指针时,需要根据指针的级别使用适当数量的星号解引用操作。

 printf("num = %d \n",num); 
  printf("*ptr1 = %d \n",*ptr1); // 解引用一次
  printf("**ptr2 = %d \n",**ptr2); // 解引用两次
  printf("***ptr3 = %d \n",***ptr3); // 解引用三次

三、空指针

        赋为NULL 值的指针被称为空指针,NULL 是一个定义在标准库 <stdio.h>中的值为零的宏常量。

        声明指针变量的时候,如果没有确切的地址赋值,为指针变量赋一个NULL 值是好的编程习惯。

四、野指针

        野指针就是指针指向的位置是不可知(随机性,不正确,没有明确限制的)。

        指针使用前未初始化

        指针变量在定义时如果未初始化,其值是随机的,此时操作指针就是去访问一个不确定的地址,所以结果是不可知的。此时p就为野指针。

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

        在没有给指针变量显式初始化的情况下,一系列的操作(包括修改指向内存的数据的值)也是错误的。(该指针不在咱们受控的范围,如果在单片机上访问其他真实物理地址,数据不受保护)

        指针越界访问

int arr[3] = {10,20,30};
  int *p = arr;
  p += 3;
  printf("%d \n",*p);  // 此时*p就是越界访问

        当 p += 3之后,此时 *p 访问的内存空间不在数组有效范围内,此时 *p 就属于非法访问内存空间,p为野指针。

        指针指向已释放的空间

#include <stdio.h>
int *test(){
  int a = 10;
  return &a;
}
int main()
{
  int *p = test(); // 此时p指向的是释放的空间
  printf("%d \n",*p);
  return 0;
}

        调用test()函数将返回值赋给p,test函数的返回值是局部变量a的地址,函数调用结束局部变量会被销毁,其所占用的内存空间会被释放,p 指向的是已经释放的内存空间,所以 p 是野指针。

注意:

  1. 指针初始化如果没有确切的地址赋值,为指针变量赋一个 NULL 值是好的编程习惯。
  2. 小心指针越界。
  3. 避免返回指向局部变量的指针。
  4. 指针使用之前检查指针是否为 NULL。

变量定义

类型表示

含义

int i

int

i是一个整型变量。

int *p

int *

p 是一个指向整型数据的指针

int a[5]

int[5]

a 是一个5个元素的整型数组。

int *p[4]

int *[4]

p 是一个指针数组,每个元素都是指向整型数据的指针。

int (*p)[4]

Int (*)[4]

p 是一个数组指针,指向一个4个元素的整型数组。

int f()

Int ()

f是一个返回整型数据的函数

int *f()

int *()

f是一个指针函数,返回指向整数数据的指针。

int (*p)()

int (*)()

p 是一个函数指针,指向一个返回整型数据的函数。

int **p

int **

p是一个指向指针的指针。

五、枚举

   语法格式

        枚举使用 enum 关键字来定义,枚举名称和枚举元素名称都是标识符,定义一个枚举就是定义了一种枚举数据类型,语法如下:

enum 枚举名称 
{
    枚举元素1,
    枚举元素2,
    ...
    枚举元素N
};

枚举元素也称为枚举成员或枚举常量,具有如下特点:

  1. 枚举元素的值必须在同一枚举中是唯一的
  2. 枚举元素的值必须是整数类型,通常是int
  3. 如果没有为枚举元素指定值,编译器会自动为它们分配值,从0开始,依次递增。
  4. 定义枚举的时候也可以为枚举元素自定义值,需保证唯一性和整型类型。
enum Weekday
{
  MONDAY,
  TUESDAY,
  WEDNESDAY,
  THURSDAY,
  FRIDAY,
  SATURDAY,
  SUNDAY
};

// 定义枚举类型,标识三原色。 给枚举元素RED设置了值1,后面的枚举元素从1开始自增,分别是 1到3
enum Color
{
  RED = 1,
  GREEN,
  BLACK
};

// 定义枚举类型,表示北京地区一年四季的平均温度。为每个枚举元素都设置值,自己设置的值需要保证唯一性和整型类型
enum Season{
  SPRING = 15,
  SUMMER = 37,
  AUTUMN = 25,
  WINTER = -10
}

如果枚举常量的值是连续的,我们可以使用循环遍历;如果枚举常量的值不是连续的,则无法遍历

六、结构体

        C语言提供了struct关键字,允许自定义复合数据类型,将不同类型的值组合在一起,这种类型称为结构体(structure)类型。

        C语言没有其他语言的对象(object)和类(class)的概念,struct 结构很大程度上提供了对象和类的功能。

格式:

struct 结构体名
{ 
    数据类型1 成员名1;   
    数据类型2 成员名2; 
    ……
    数据类型n 成员名n;
};
声明结构体变量

方式1 先定义结构体,然后再创建结构体变量

//先定义结构体
struct Student
{
  int id;        // id
  char name[50]; // 名字
  char gender;   // 性别
  int age;       // 年龄
};
// 定义结构体变量
struct Student stu1, stu2;

方式2 在定义结构体的同时定义结构体变量

//定义结构体的同时定义stu1和stu2变量
struct Student2{
  int id;
  char name[50];
  char gender;
  int age;
} stu1, stu2;

方式3 在定义时也可以不给出结构体名

// 没有结构体名字
struct{
  int id;
  char name[50];
  char gender;
  int age;
} stu3, stu4;
结构体指针

声明结构体指针的语法格式:

struct 结构体名字 *结构体指针变量名;

        通常需要先定义一个结构体变量,再创建结构体指针,并取结构体变量的地址作为结构体指针的值。 

#include <stdio.h>
int main()
{
  struct Student // 定义结构体
  {
    int id; // id
    char *name; // name
    int age; // age
  };
  // 定义结构体变量
  struct Student stu;
  // 定义结构体指针并初始化
  struct Student *ptr = &stu;
  return 0;
}

通过结构体指针访问成员

结构体指针可以通过“->” 操作符访问结构体的成员。

“->” 成员访问符

#include <stdio.h>
int main()
{
  // 定义结构体
  struct Student{
    int id;
    char *name;
    int age;
  };
  // 定义结构体变量并初始化
  struct Student stu = {118, "jack", 18};
  // 通过结构体变量访问成员
  printf("id=%d,name=%s,age=%d \n",stu.id,stu.name,stu.age);
  // 定义结构体指针变量并初始化
  struct Student *ptr = &stu;
  // 通过结构体指针变量解引用访问结构体成员
  printf("id=%d,name=%s,age=%d \n",(*ptr).id,(*ptr).name,(*ptr).age);
  // 通过结构体指针->访问结构体成员
  printf("id=%d,name=%s,age=%d \n",ptr->id,ptr->name,ptr->age);
  return 0;
}

总结:如果指针变量p指向一个结构体变量stu,以下3种用法等价:

  1. stu.成员名
  2. (*p).成员名
  3. p->成员名

案例

  1. 编写一个Dog结构体,包含name(char *)、age(int)、weight(double)属性。
  2. 编写一个say函数,返回字符串,方法返回信息中包含所有成员值。
  3. 在main函数中,创建Dog结构体变量,调用say 函数,将调用结果打印输出。
#include <stdio.h>
// 定义结构体Dog
struct Dog{
  char *name; // 名字
  int age; // 年龄
  double weight; // 重量
};
char * get_gog_info(struct Dog dog){
  static char info[50]; // 局部静态变量,存储信息数据
  sprintf(info,"name=%s,age=%d,weight=%.2f",dog.name,dog.age,dog.weight);
  // 修改名字
  dog.name = "大黄";
  return info;
}
int main()
{
  // 定义结构体变量
  struct Dog dog = {"旺财", 5, 5.78};
  // 定义接收信息的字符指针变量
  char *info = NULL;
  // 调用函数并接收结果
  info = get_gog_info(dog);
  printf("狗狗的信息:%s \n",info);
  printf("main中狗狗的名字:%s \n",dog.name);
  return 0;
}

 总结:

        区分三个概念:结构体、结构体变量、结构体变量的成员。

        结构体是自定义的数据类型,表示的是一种数据类型。结构体变量代表一个具体变量。类比:

int num1;        // int 是数据类型, 而num1是一个具体的int变量
struct Car car1; // Car 是结构体数据类型,而car1是一个Car变量

        Car就像一个汽车图纸,生成出来的具体的一辆辆汽车,就类似于一个个的结构体变量。这些结构体变量都含有相同的成员,将结构体变量的成员比作零件,同一张图纸生产出来的零件的作用都是一样的。


原文地址:https://blog.csdn.net/Find_tim/article/details/142885107

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!