IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> GCC AVR(Atmel Studio+ AVR Studio)如何在程序存储器(flash)空间存放字符串常量和使用字符串常量 -> 正文阅读

[C++知识库]GCC AVR(Atmel Studio+ AVR Studio)如何在程序存储器(flash)空间存放字符串常量和使用字符串常量

目录

一、program memory和data memory

二、如何将字符串变量定义到program memory(flash)

三、如何读取字符串变量

四、PSTR()

五、使用strcpy()函数将字符串常量拷贝字符串缓冲区会占用大量的data memory区空间;使用strcpy_P()函数将字符串常量拷贝到字符串缓冲区仅仅占用program memory区(flash)空间,不会占用data memory区空间。

?六、使用sprintf()函数将字符串常量打印到字符串缓冲区会占用大量的data memory区空间;使用sprintf_P()函数将字符串常量打印到字符串缓冲区仅仅占用program memory区(flash)空间,不会占用data memory区空间。

七、总结


一、program memory和data memory

? ? ? ? GCC AVR(Atmel Studio+ AVR Studio)默认将变量(包含普通变量、数组和字符串)定义在data memory,这个需要特别注意。

? ? ? ? 如果程序中定义有大量的常量,在定义时,一定要在头部写上PROGMEM,还要添加头文件#include <avr/pgmspace.h>,这样,定义的常量就保存在program memory(flash)中了。否则,系统就会将变量定义到data memory区,编译时由于data memory仅有4k而会出现内存溢出的现象。
? ? ? ? 当data memory区的变量读取program memory区的数据时,需要用库函数pgm_read_byte()进行读取。

二、如何将字符串变量定义到program memory(flash

? ? ? ? 第一步:必须包含头文件:#include <avr/pgmspace.h>

#include <avr/pgmspace.h>

? ? ? ? 第二步:用关键字PROGMEM定义字符串常量

PROGMEM const char flash_string[]= "Hello World.";

三、如何读取字符串变量

? ? ? ? 当data memory区的变量去读取program memory区的数据时,需要用库函数pgm_read_byte()进行读取,下面我们编写一个入口参数为字符串常量的函数,将入口的字符串常量发送到串口1,需要用到系统提供的函数pgm_read_byte()。

#include <avr/pgmspace.h>

PROGMEM const char flash_string[]= "Hello World.";


void HMISendString_const(const char *string)
{
	uint8_t ch;
    	
	
	while (pgm_read_byte(&(*string)) != '\0')
    {
		ch = pgm_read_byte(&(*string));
        UDR1 = ch;
        string++;
    }    
}


int main(void)
{
    HMISendString_const(flash_string); 
}

四、PSTR()

? ? ? ? AVR提供了一个宏 PSTR(),它在头文件pgmspace.h中,它可以用于将字符串定义到program memory区(flash)。宏 PSTR()的具体定义参见下图。

? ? ? ? ?上面的程序代码可以不定义PROGMEM const char flash_string[]= "Hello World.";? 而是可以直接用宏 PSTR()将字符串定义到program memory区(flash),改造后的程序如下:

#include <avr/pgmspace.h>

void HMISendString_const(const char *string)
{
	uint8_t ch;
    	
	
	while (pgm_read_byte(&(*string)) != '\0')
    {
		ch = pgm_read_byte(&(*string));
        UDR1 = ch;
        string++;
    }    
}


int main(void)
{
    HMISendString_const(PSTR("Hello World.")); 
}

五、使用strcpy()函数将字符串常量拷贝字符串缓冲区会占用大量的data memory区空间;使用strcpy_P()函数将字符串常量拷贝到字符串缓冲区仅仅占用program memory区(flash)空间,不会占用data memory区空间。

1、使用strcpy()函数拷贝字符串前,先在data memory空间定义了一个字符串缓冲区char HMI_string[255],编译程序后发现data memory空间占用了2362个字节。

?2、现在我们用库函数strcpy()编写一段程序,将字符串常量 "I am C++"拷贝到字符串缓冲区HMI_string,观察是否会占用data memory区空间。

? ? ? ? 程序代码参见下图,编译程序后发现data memory空间目前占用了2370个字节(之前为2362个字节)。

? ? ? ? 怎么只是在程序中调用了strcpy(HMI_string , "I am C++") ,data memory空间就多占用了8个字节呢?? ? ?

??3、现在我们用库函数strcpy()再编写一段程序,将字符串常量 "I am C#"拷贝到字符串缓冲区HMI_string,观察是否会继续占用data memory区空间。

? ? ? ??程序代码参见下图,编译程序后发现data memory空间目前占用了2378个字节,又比之前多占用了8个字节(之前占用了2370个字节)。

? ? ? ? 我们发现一个规律:(1)、只要调用strcpy(HMI_string , "I am C++"),编译器发现第二个入口参数是普通的字符串"I am C++",就会在data memory空间申请8个字节的空间。(2)、只要调用strcpy(HMI_string , "I am C#"),编译器发现第二个入口参数是普通的字符串"I am C#",就会在data memory空间申请8个字节的空间。? ? ? ?

?4、如何将一串字符串常量拷贝到字符串缓冲区,而且还可以确保第二个入口参数不占用data memory空间呢?

? ? ??strcpy()函数是GCC AVR的头文件string.h中的函数,要想将一串字符串常量拷贝到字符串缓冲区,而且字符串拷贝函数的入口参数还不占用data memory空间,就必须要使用头文件pgmspace.h中的库函数strcpy_P,库函数strcpy_P的原型如下:

? ? ? ? ? ? ? ? ??char *strcpy_P(char *, const char *);? ? ? ? ? ? ? ? ?

? ? ? ? 头文件pgmspace.h中的库函数与头文件string.h中的库函数的区别是:

? ? ? ? (1)、pgmspace.h中的库函数比头文件string.h中的库函数多了一个后缀? _P。

? ? ? ? (2)、char *strcpy_P(char *, const char *)? ?第二个参数是程序存储器program memory区(flash)。

? ? ? ? (3)、主程序调用strcpy_P(HMI_string , PSTR("I am C++"));时,第二个参数不会占用data memory空间。

? ? ? ? 因此,要想将一串字符串常量拷贝到字符串缓冲区,而且还要让入口参数的字符串常量不占用data memory空间,就必须要使用头文件pgmspace.h中的函数char *strcpy_P(char *, const char *)? ?

? ? ? ? 警告:strcpy_P()的第二个入口参数的类型是 const,因此需要在字符串常量前面添加宏PSTR(),将其定义到program memory区

? ? ? ? ? ? ? ? ? 例如:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?strcpy_P(HMI_string , PSTR("I am C++"));

5、验证

? ? ? ? (1)、程序中添加 strcpy_P(HMI_string , PSTR("I am C++")); ,程序编译程序后data memory空间仍然是2362字节,可见使用头文件pgmspace.h中的库函数 strcpy_P(), 第二个参数PSTR("I am C++")不会增加data memory空间。? ?

?? ? ? ? (2)、程序中再添加 strcpy_P(HMI_string , PSTR("I am C#")); ,程序编译程序后data memory空间仍然是2362字节,可见使用头文件pgmspace.h中的库函数 strcpy_P(), 第二个参数PSTR("I am C#")不会增加data memory空间。? ?

?六、使用sprintf()函数将字符串常量打印到字符串缓冲区会占用大量的data memory区空间;使用sprintf_P()函数将字符串常量打印到字符串缓冲区仅仅占用program memory区(flash)空间,不会占用data memory区空间。

1、使用sprintf()函数打印字符串前,编译程序后发现data memory空间占用了2362个字节。

?2、现在我们用库函数sprintf()编写一段程序,将字符串常量 "I am C++"和数值1275混合打印到字符串缓冲区HMI_string,观察是否会占用data memory区空间。

? ? ? ? 程序代码参见下图,编译程序后发现data memory空间目前占用了2372个字节(之前为2362个字节)。

? ? ? ? 怎么只是在程序中调用了sprintf(HMI_string , "I am C++%d" , 1278) ,data memory空间就多占用了10个字节呢?? ? ?

?? ? ? ?我们发现一个规律:(1)、只要调用sprintf(HMI_string , "I am C++%d" , 1278) 编译器发现第二个入口参数是普通的字符串"I am C++",就会在data memory空间申请10个字节的空间。

?3、如何将一串字符串常量和数值混合打印到字符串缓冲区,而且还可以确保第二个入口参数不占用data memory空间呢?

? ? ??sprintf()函数是GCC AVR的头文件string.h中的函数,要想将一串字符串常量和数值混合打印到字符串缓冲区,而且字符串打印函数的入口参数还不占用data memory空间,就必须要使用头文件pgmspace.h中的库函数sprintf_P,库函数sprintf_P的原型如下:

? ? ? ? ? ? ? ? ? ? ? int?? ?sprintf_P(char *__s, const char *__fmt, ...);

? ? 头文件pgmspace.h中的库函数与头文件string.h中的库函数的区别是:

? ? ? ? (1)、pgmspace.h中的库函数比头文件string.h中的库函数多了一个后缀? _P。

? ? ? ? (2)、 int?? ?sprintf_P(char *__s, const char *__fmt, ...);?第二个参数是程序存储器program memory区(flash)。

? ? ? ? (3)、主程序调用sprintf(HMI_string , "I am C++%d" , 1278)时,第二个参数不会占用data memory空间。

? ? ? ? ? 因此,要想将一串字符串常量和数值混合打印到字符串缓冲区,而且还要让入口参数的字符串常量不占用data memory空间,就必须要使用头文件pgmspace.h中的函数?sprintf_P(char *__s, const char *__fmt, ...)?

? ? ? ? 警告:sprintf_P()的第二个入口参数的类型是 const,因此需要在字符串常量前面添加宏PSTR(),将其定义到program memory区

? ? ? ? ? ? ? ? ? 例如:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?sprintf_P(HMI_string , PSTR("I am C++%d") , 1278);

4、验证

? ? ? ? (1)、程序中添加 sprintf_P(HMI_string , PSTR("I am C++%d") , 1278); ,程序编译程序后data memory空间仍然是2362字节,可见使用头文件pgmspace.h中的库函数 sprintf_P(), 第二个参数PSTR("I am C++%d")不会增加data memory空间。? ?

?

七、总结

1、要想定义字符串常量到program memory区,首先要添加头文件#include <avr/pgmspace.h>,

2、然后用关键字PROGMEM定义字符串常量,例如:PROGMEM const char flash_string[]= "Hello World.";

3、也可以宏定义PSTR()直接在用户函数的入口参数上进行限制,例如:HMISendString_const(PSTR("Hello World."));

4、要想将字符串常量拷贝字符串缓冲区而不占用data memory区空间,必须要使用头文件pgmspace.h中的strcpy_P(),而不能使用头文件string.h中的strcpy()。而且strcpy_P()的第二个入口参数必须要用宏PSTR()进行限定,例如:strcpy_P(HMI_string , PSTR("I am C++"));

5、要想将字符串常量和数值混合打印到字符串缓冲区而不占用data memory区空间,必须要使用头文件pgmspace.h中的sprintf_P(),而不能使用头文件string.h中的sprintf()。而且sprintf_P()的第二个入口参数必须要用宏PSTR()进行限定,例如:sprintf_P(HMI_string , PSTR("I am C++%d") , 1278);

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-27 17:12:03  更:2022-05-27 17:13:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/13 11:26:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码