社区编辑申请
注册/登录
关于结构体中指针的一些探讨
开发 后端
在上篇文章中,有读者对下列代码有疑问,这位读者意识到了name成员是个指针,在没有对指针分配内存时,就直接复制“xiaoming”字符串,这是错误的。先说下结论,这个是没有问题的,在下文我会详细说明下。

01 起因

在上篇文章《STM32编程中枚举和结构体的结合》中,有读者对下列代码有疑问

  1. typedef struct{ 
  2.   char *name;  //姓名 
  3.   int num;  //学号 
  4.   int age;  //年龄 
  5.   float score;  //成绩 
  6. }stuff_s; 
  7. stuff_s xiaoming; 
  8. void xiaoming_inf_init() 
  9.   xiaoming.name = "xiaoming"
  10.   xiaoming.num = 1; 
  11.   xiaoming.age = 18.0; 
  12.   xiaoming.score = 100; 

留言到

很明显,这位读者意识到了name成员是个指针,在没有对指针分配内存时,就直接复制“xiaoming”字符串,这是错误的。先说下结论,这个是没有问题的,在下文我会详细说明下。

02 解释

首先,实践是检验真理的唯一标准,我们直接在编译器运行代码即可,这里我使用的是IAR编译,在VisualStudio中运行结果也是一样的,这里我使用IAR为例

可以看到,运行没有问题的,name成员被正常赋值。这里注意name指针指向的位置是0x8002A5C,这是在flash的地址范围,也就是编译器直接把“xiaoming”字符串放到了flash中,作为一个常量,然后把这个常量的指针赋给name指针,所以不用提前给name指针申请内存空间。关于STM32的内存分配,可以看之前推文《C语言在STM32中的内存分配》。这样写也是合法且正确的,当然我们最熟悉方式如下

  1. xiaoming.name = (char *)malloc(10); 
  2. memcpy(xiaoming.name,"xiaoming",8); 
  3. xiaoming.num = 1; 
  4. xiaoming.age = 18.0; 
  5. xiaoming.score = 100; 
  6. free(xiaoming.name); 

运行结果如下

可以看出,name指针是指向内存的,和刚开始的代码是有区别的。那么像刚开始的写法,如下

  1. xiaoming.name = "xiaoming"

编译正常,运行正常,在使用中有什么限制吗?答案是有的

  1. char test_char; 
  2. xiaoming.name = "xiaoming"
  3. test_char = xiaoming.name[2]; 

这样写是正确的,test_char可以被正确的赋值字符a;但如下写法是错误的

  1. xiaoming.name = "xiaoming"
  2. xiaoming.name[2] = 'Q'

这样写可以编译通过,执行的时候也不报错,但是并不能达到修改第3个字符的目的。

本质上因为name指针指向的是Flash,可以通过上面的方法进行读取操作,但是不能按上面方法进行写入操作。

如果按下面的写法,读取和写入的操作的操作都是没有问题的,因为name指针指向的是内存,具有可读可写的属性。

  1. xiaoming.name = (char *)malloc(10); 
  2. memcpy(xiaoming.name,"xiaoming",8); 
  3. xiaoming.num = 1; 
  4. xiaoming.age = 18.0; 
  5. xiaoming.score = 100; 
  6. free(xiaoming.name); 

所以日常代码编写中需要注意这些,我的观点是:按照上述方法,先对指针申请内存,然后再赋值。

当然,万事没有绝对,需要视情况而定,下列情况,你也可以直接将字符串赋给指针

确认指针不会有写入操作,只有读操作,且你认为多加一句memcpy语句影响你的代码运行速度了。

确认指针不会有写入操作,只有读操作,且系统没有多余的内存给指针申请了。

03 const关键字

上文既然提到了只读属性,那么我们就再说一下const关键字。大家先看如下代码操作

  1. typedef struct{ 
  2.   const char *name;  //姓名 
  3.   int num;  //学号 
  4.   int age;  //年龄 
  5.   float score;  //成绩 
  6. }stuff_s; 
  7. stuff_s xiaoming; 
  8. int main(void) 
  9.   xiaoming.name = (char *)malloc(10); 
  10.   memcpy(xiaoming.name,"xiaoming",8); 
  11.   xiaoming.name[2] = 'Q'
  12.   xiaoming.num = 1; 
  13.   xiaoming.age = 18.0; 
  14.   xiaoming.score = 100; 
  15.   free(xiaoming.name); 

指针name前加了const关键字,这段代码在IAR编译器中是根本编译不通过的。

原因很简单,就是因为指针name具有const属性,不能被写入。

所以,在上一节最有一部分说到,当你确认指针不会有写入操作,只有读操作,你可以在这个指针定义前加一个const属性,因为项目代码不是你一个维护的,你设计时认为这个指针只有读操作,就加const,这样别人进行写访问时直接就会在IAR报错,而不会将这个隐藏的隐患遗留在产品中。

当然,上文定义的const char *name;也是不规范的,当这个指针加了const,就应该在指针的名字中体现到,这个不同公司有不同的命名规范,每个人也有每个人的规范,这里不在演示了。

本文转载自微信公众号「知晓编程」

【编辑推荐】

 

责任编辑:姜华 来源: 知晓编程
相关推荐

2022-05-18 23:42:08

网络安全安全分析工具

2020-03-30 09:22:36

C语言结构体

2022-04-22 18:48:46

LinuxLinux 内核C 语言

2022-03-22 11:33:13

AT模块Harmony鸿蒙

2022-03-10 17:02:51

Rust单链表数据结构

2022-03-29 08:30:47

2016-12-12 12:37:45

结构C代码赋值

2022-03-16 10:14:55

C语言C++

2022-05-13 09:15:21

三层交换机二层交换机VLAN

2022-01-09 23:04:19

2022-03-08 11:17:54

函数指针回调函数C语言

2022-04-26 10:55:32

数据模型

2021-05-11 11:31:52

C语言类型指针

2022-04-12 08:30:52

2022-04-23 16:30:22

Linux磁盘性能

2022-04-23 17:49:05

区块链元宇宙MetaCon

2022-04-24 15:37:26

LinuxCPU

2009-08-13 15:41:50

C#结构体指针

2009-08-31 15:02:22

C#解析结构体指针

2022-04-25 11:27:34

LinuxCPU

同话题下的热门内容

Python 字符串总结,建议收藏!这份Java日志格式规范,拿走不谢!Mybatis-Plus官方发布分库分表神器,一个依赖轻松搞定!后端思维篇:如何应用设计模式优化代码改变 Python 对象规则的黑魔法 Metaclass几种限流算法的Go语言实现JMeter关联之正则表达式提取器在 Go 中实现一个支持并发的 TCP 服务端

编辑推荐

使用Kotlin做开发一个月后的感想面试官问你什么是消息队列?把这篇甩给他!五大自动化测试的Python框架图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)2018年最流行的十大编程语言,其中包括你用的语言吗?
我收藏的内容
点赞
收藏

51CTO技术栈公众号