C++ 打印字符串生成函数的输出未返回预期结果

C++ 打印字符串生成函数的输出未返回预期结果,c++,arduino,C++,Arduino,我正在编写一个函数,从包含小时和分钟的两个全局整数输出24小时格式的基本小时和分钟字符串 我在初始化过程中定义了以下内容: int g_alarmHours = 7; int g_alarmMinutes = 0; 返回字符串的函数是: char* getAlarmTime() { int hours = g_alarmHours; int minutes = g_alarmMinutes; char t[6]; t[0] = (hours/10) + '0'; t[1]

我正在编写一个函数,从包含小时和分钟的两个全局整数输出24小时格式的基本小时和分钟字符串

我在初始化过程中定义了以下内容:

int g_alarmHours = 7;
int g_alarmMinutes = 0;
返回字符串的函数是:

char* getAlarmTime() {
  int hours = g_alarmHours;
  int minutes = g_alarmMinutes;
  char t[6];
  t[0] = (hours/10) + '0';
  t[1] = (hours%10) + '0';
  t[2] = ':';
  t[3] = (minutes/10) + '0';
  t[4] = (minutes%10) + '0';
  t[5] = 0;
  return t;
}
全局变量是在添加到另一个设备的串行通信时要替换的存根,从中检索这些值

调用该函数会在字符指针处生成以下十六进制值:

0x20 0x4b 0x00
当我将getAlarmTime函数的前两行替换为以下内容时

int hours = 7;
int minutes = 0;
那么,输出就是我所期望的:

07:00\0

为什么使用这些全局变量会导致getAlarmTime的输出变得如此不稳定?

您正在返回指向堆栈上局部变量的指针。指针指向的内存不再有效,访问该内存将调用未定义的行为。您看到这种奇怪行为的原因是,当您调用未定义的行为时,任何事情都可能发生

解决问题的方法是用C++编写代码,使用STD::String。

std::string t;
t.push_back((hours/10) + '0');
...

return t;

您返回的是一个指向数组的指针,该数组仅位于函数的本地。因此,当函数退出时,在函数中创建的数组将不再存在,任何访问该内存的尝试都将导致未定义的行为

为什么使用这些全局变量会导致getAlarmTime的输出变得如此不稳定

实际上,您在这里看到的是未定义的行为,因为您返回的是本地堆栈变量的地址

发生以下顺序:

您可以调用getAlarmTime

编译器为其变量小时、分钟和t分配堆栈空间

然后t被填满了

您返回t的地址

控件退出函数,返回的地址指向未使用的堆栈空间

随后声明的堆栈数据变量或其他函数调用将覆盖此空间


解决方案:考虑返回一个STD::String,而不是char *.< /p> ,您正在返回一个局部变量作为指针。 返回t

Ideone编译器在编译时返回以下错误:

prog.cpp:在函数“char*getAlarmTime”中:prog.cpp:8:8:警告: 局部变量“t”的地址返回[-Wreturn local addr]char t[6]

但是我不明白当你把前两行换成

int hours = 7;
int minutes = 0;

使用字符串或传递尊重来解决您的问题。甚至一个全局变量也可以解决您的问题

您正在返回指向本地数组的指针。在调用方可以访问它之前,它被销毁,从而导致未定义的行为;实际上,它可能会也可能不会被其他人的数据覆盖

通常的解决方案是返回一个动态数组,如std::string;但既然你说你有极端的内存限制,那在这里就不是个好主意

我将修改该函数,以便调用者提供缓冲区:

void getAlarmTime(char t[6]) {
  int hours = g_alarmHours;
  int minutes = g_alarmMinutes;
  t[0] = (hours/10) + '0';
  t[1] = (hours%10) + '0';
  t[2] = ':';
  t[3] = (minutes/10) + '0';
  t[4] = (minutes%10) + '0';
  t[5] = 0;
}
请注意,调用者现在负责确保缓冲区足够大。尽管我将参数声明为char[6],但它仅用作文档;对于编译器来说,它与char*相同

另一种可能是使本地缓冲区静态;但是请注意,该函数将不再是可重入的或线程安全的,这可能会导致奇怪的bug

为什么使用这些全局变量会导致getAlarmTime的输出变得如此不稳定


我的猜测是,当您使用常量初始化局部变量时,编译器会删除它们并使用常量。这会将数组移动到堆栈中的其他位置,在检查它之前,它恰好不会被覆盖。但是这些都是未定义行为的领域,因此确切的细节没有任何实际意义。

出于好奇,什么是t[5]=0;平均值?@Rohit将空字符分配给最后一个数组元素不是'\0'空字符。@Rohit'\0'==0。我只是一个懒惰的打字员,用最短的路径得到同样的结果。谢谢你的解释:我在一个有4KB内存的arduino上做这件事,并且执行了很多代码,包括一个迷你Web服务器,如果可能的话,我宁愿避免使用字符串库,因为它占用了相当大的空间。不过我明白你的意思,我假设另一个不涉及的解决方案是在函数外部声明字符数组,并将指针作为参数传递给它作为缓冲区写入。@bdx:你熟悉吗?我是,但是,有人一再警告说,如果包含字符数组,它的内存占用比字符数组大得多。我正在接近RAM限制,我正在寻找尽可能减少内存使用的方法。@bdx:那么你的解决方案就可以了。我这样做时,它始终工作正常,这似乎是运气不佳。你在使用什么编译器?它还没回来吗 尝试返回时,如果没有错误,则发出警告