问题描述
刚刚写一个用AT指令透传相关的函数,需要用到sprintf()
拼接字符串。
结果发现sprintf()
拼接出来的内容是错误的,简化后的代码如下:
const char AT_CIPSEND_FIX_LENGTH_HEADER[11] = "AT+CIPSEND="; // 错误的!!!
const char AT[] = "AT\r\n";void ESP8285_CipSend_FixLength(char* data, uint8_t length)
{static char AT_CIPSEND_FIX_LENGTH_FRALME[16] = {0};sprintf(AT_CIPSEND_FIX_LENGTH_FRALME, "%s", AT_CIPSEND_FIX_LENGTH_HEADER);
}
简化后的代码其实就是把AT_CIPSEND_FIX_LENGTH_HEADER
的内容打印到AT_CIPSEND_FIX_LENGTH_FRALME
里面,结果一看AT_CIPSEND_FIX_LENGTH_FRALME
里面出现了多余的内容。
很显然,多余的内容就是AT_CIPSEND_FIX_LENGTH_HEADER
字符串后面的内容,即AT
字符串的内容。
问题原因
问题的原因其实很简单,AT_CIPSEND_FIX_LENGTH_HEADER
字符串长度是11位的,没有把作为字符串结尾的\0
算进去
因此在用sprintf()
的时候,没有正确的找到字符串的结尾,把内存后面AT
字符串的结尾当成字符串的结尾了,所以spinrtf()
的输出结果是两个字符串的拼接
正确改法1
可以改写成如下:
const char AT_CIPSEND_FIX_LENGTH_HEADER[12] = "AT+CIPSEND="; // 指定长度为12
const char AT[] = "AT\r\n";
void ESP8285_CipSend_FixLength(char* data, uint8_t length)
{static char AT_CIPSEND_FIX_LENGTH_FRALME[16] = {0};sprintf(AT_CIPSEND_FIX_LENGTH_FRALME, "%s", AT_CIPSEND_FIX_LENGTH_HEADER);
}
正确改法2
也可以直接不指定长度,改写如下:
const char AT_CIPSEND_FIX_LENGTH_HEADER[] = "AT+CIPSEND="; // 不指定长度
const char AT[] = "AT\r\n";void ESP8285_CipSend_FixLength(char* data, uint8_t length)
{static char AT_CIPSEND_FIX_LENGTH_FRALME[16] = {0};sprintf(AT_CIPSEND_FIX_LENGTH_FRALME, "%s", AT_CIPSEND_FIX_LENGTH_HEADER);
}
错误改法!!!
但这个问题很容易被掩盖,因为像如下这么写结果可能也是对的(取消掉const,长度是11),但其实是错误的!在某些情况下会出错!!:
char AT_CIPSEND_FIX_LENGTH_HEADER[11] = "AT+CIPSEND="; // 错误的!!
const char AT[] = "AT\r\n";void ESP8285_CipSend_FixLength(char* data, uint8_t length)
{static char AT_CIPSEND_FIX_LENGTH_FRALME[16] = {0};sprintf(AT_CIPSEND_FIX_LENGTH_FRALME, "%s", AT_CIPSEND_FIX_LENGTH_HEADER);
}
在之前的写法中,两个字符串都是常量,在常量存储区,存储位置是连续的。
如果把第一个字符串的const
修饰去掉,那么AT_CIPSEND_FIX_LENGTH_HEADER
在全局变量存储区,会被加载到内存中,而AT
字符串则在常量存储区,这时候两个变量就不是连续的了,因此不会出现“spinrtf()
的输出结果是两个字符串的拼接”的情况了。
在这种情况下,sprintf()
的输出结果就取决于什么时候碰到\0
了。
所以使用sprintf()
有缓冲区溢出的风险,可以使用snprintf()
,其接受一个额外的参数size
,用于指定目标缓冲区的大小。它在写入时会限制写入的长度,以避免缓冲区溢出,更安全。