对于一些背景知识,我正在用C编写一个抄表应用程序,用于运行专有版本DOS的小型16位掌上电脑。
我有一个显示仪表信息的屏幕,并提示用户输入读数。当用户按下设备上的回车键时,将执行以下代码:
/* ...
* beginning of switch block to check for keystrokes
* ...
*/
case KEY_ENTER: {
/* show what has been entered */
if(needNew == 0) {
/* calculate usage for new reading */
double usg = 0;
int ret = CalculateNewUsage(vlr, buf, &usg);
VerifyReadScreen(vlr, ret, buf, &usg);
needRedraw = TRUE;
}
break;
}
/* .... end switch statement */
vlr
是一个指向包含所有帐户/计量器信息的结构的指针,buf
的类型为char[21]
,用于存储在此块上方处理的读数的数字击键。当我在调用CalculateNewUsage
之前和之后检查它们时,我的变量都包含有效数据。
但是,当我在输入VerifyReadScreen
后再次检查变量数据时,newread
会在内存中随机指向并返回看似版权声明的内容。有趣的是,无论我输入什么帐户或阅读内容,都会在屏幕上打印newread
中VerifyReadScreen
的相同无效数据。我以与VerifyReadScreen
相同的方式将地址传递给CalculateNewUsage
,但不知怎的,我最终得到了不同的东西。
以下是VerifyReadScreen
:
BYTE VerifyReadScreen(const VLRREC * vlr,
const int status,
const char * newread,
const double * usage) {
/* snip a whole bunch of irrelevant formatting code */
printf("%s", (*newread)); /* prints funky copyright text */
/* snip more irrelevant formatting code */
return TRUE;
}
感谢Jefromi指出我在newread
中实际打印VerifyReadScreen
的代码应该真的读到:
printf("%s", newread); /* yay! */
因为我不需要取消引用newread
,因为printf
为我做了这个。我本质上是将指针传递给指针,指针在内存中是一个任意的位置。
答案 0 :(得分:7)
我认为我有足够的信心发布这个答案:
BYTE VerifyReadScreen(const VLRREC * vlr, const int status, const char * newread, const double * usage) {
...
LCD_set_cursor_pos(19 - strlen(newread), 3);
printf("%s", (*newread)); /* prints funky copyright text */
...
}
你有一个字符串(char*
)newread
,但在那个printf中,你取消引用它,它会为你提供字符串的第一个字符。然后使用它作为printf的%s
的参数,因此它会尝试转到该字符给出的内存地址并打印它在那里找到的内容。
P.S。你运气不好 - 一般来说,做这样的事情可能会给你一个段错误,所以你可以跟踪它到那一行并意识到那里有一个指针错误。
答案 1 :(得分:2)
我不知道您遇到的问题是否正确,但buffer
中使用的VerifyReadScreen
(长度为21个字符)很可能会溢出:
if(strlen((*vlr).ServAdd) >= 20) {
sprintf(buffer, "%20s", (*vlr).ServAdd);
}
%20s
格式说明符不会阻止sprintf
写入超过20个字符。如果字符串短于20个字符(或者你想在if条件中使用<= 20
),它只会用空格填充字符串吗?)。
else {
memset(buffer, 0x20, (int)(strlen((*vlr).ServAdd) / 2) + 1);
strcat(buffer, (*vlr).ServAdd);
}
这里根据字符串长度完成一些填充,但我不知道如何确保结果不超过20个字符。