strlen return c 中的错误答案
strlen return wrong answer in c
我正在编写一个将十进制数转换为罗马数的程序。我使用 4 个数组 thousands
、hundreds
、tens
、units
来存储罗马数字中的数字,然后将每个数字复制到 res
数组,我使用 str
指向跟踪 res
中字符串开始位置的指针。当我用输入 128
进行测试时,它会打印出 CXXVIIIIX
,它必须是 CXXVIII
。我试过调试得到的结果是 tmp=8
是 strlen(units[tmp-1]) = 6
,这意味着 strlen
也算作 IX
。对于某些情况,例如 3888
,程序会打印出垃圾值。
这是我的代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#include <string.h>
#include <windows.h>
int main(){
int n=128;
char thousands[][4]={"M","MM","MMM"};
char hundreds[][5]={"C","CC","CCC","CD", "D", "DC", "DCC", "DCCC", "CM"};
char tens[][5]={"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
char units[][5]={"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
char res[16];
res[15]='[=11=]'; //add nul to the last character of res array
char *str=res+14; //str tracks where the string start
int tmp;
//store roman digits in res array in reverse order,
//start from units->tens->hundreds->thousands
if (n!=0)
{
tmp=n%10;
if (tmp!=0)
{
str-=strlen(units[tmp-1]); //str steps back several address to store new digits
strncpy(str, units[tmp-1], strlen(units[tmp-1])); //use strncpy to copy without '[=11=]'
}
n/=10;
}
if (n!=0)
{
tmp=n%10;
if (tmp!=0)
{
str-=strlen(tens[tmp-1]);
strncpy(str, tens[tmp-1], strlen(tens[tmp-1]));
}
n/=10;
}
if (n!=0)
{
tmp=n%10;
if (tmp!=0)
{
str-=strlen(hundreds[tmp-1]);
strncpy(str, hundreds[tmp-1], strlen(hundreds[tmp-1]));
}
n/=10;
}
if (n!=0)
{
tmp=n%10;
if (tmp!=0)
{
str-=strlen(thousands[tmp-1]);
strncpy(str, thousands[tmp-1], strlen(thousands[tmp-1]));
}
n/=10;
}
printf("%s", str);
return 0;
}
那么为什么会发生这种情况以及如何解决它?
如有任何帮助,我们将不胜感激。
修复所有字符串文字的数组大小后,出现差一错误和未初始化的 char 数组元素。
char res[16];
对于 "MMMDCCCLXXXVIII"
和尾随 '[=14=]'
就足够了。
- 使用
res[15]='[=15=]';
将字符串终止存储在最后一个元素中。
char *str=res+14;
设置指向在此之前未初始化字符的指针。
str-=strlen(something)
根据要插入的字符串的长度移动指针。以下strncpy
不会覆盖未初始化的字符
结果将始终包含未初始化的尾随字符,该字符可能可见也可能不可见。
由于尾随未初始化字符,最大长度 ("MMMDCCCLXXXVIII"
) 的结果将在第一个数组元素之前的一个字符开始。您将得到一个结果字符串 "MMMDCCCLXXXVIII*"
,其中 *
未初始化。第二个 M
是 res
的第一个数组元素,第一个 M
将是第一个元素之前的元素。
演示差一错误的示例:
基于问题的原始(错误)代码。
请注意,这不是完整代码,而是一个简化的摘录,旨在显示变量值如何随输入值变化 n=3888
。 (为了使代码更短,此处缺少原始代码中的 if (tmp!=0)
守卫。)
在显示变量值的注释中,*
用于表示未初始化的字符。
int n=3888;
char res[16];
// * indicates an uninitialized character
res[15]='[=10=]'; // "***************[=10=]"
// The following initialization is wrong.
char *str=res+14; // str = &res[14] (the last uninitialized character)
int tmp;
// The following block leaves the last character of res uninitialized.
tmp=n%10; // tmp = 8
str-=strlen(units[tmp-1]); // "VIII" (strlen = 4) -> str = &res[10]
strncpy(str, units[tmp-1], strlen(units[tmp-1])); // "**********VIII*[=10=]"
n/=10; // n = 388
// Here, str points to the first character written by the previous block.
tmp=n%10; // tmp = 8
str-=strlen(tens[tmp-1]); // "LXXX" (strlen = 4) -> str = &res[6]
strncpy(str, tens[tmp-1], strlen(tens[tmp-1])); // ******LXXXVIII*[=10=]"
n/=10; // n = 38
/* ...*/
// final result would be
// str = &res[-1]
// str -> "MMMDCCCLXXXVIII*[=10=]"
// res = "MMDCCCLXXXVIII*[=10=]"
// The last strncpy tries to write a character 'M' one
// element before the beginning of the array res
// corresponding to strncpy(res[-1], "MMM", 3)
我建议作为修复
char res[16] = {0}; // initialize the whole array with 0
char *str=res+(sizeof(res)-1);
... and how to fix it?
考虑以 1,000 为单位编写单位,然后是 100、十、个位的顺序。
在每个单元列表中添加一个“零”。
滚入辅助函数。
将类型从字符数组更改为指针数组。
例子
void print_roman(int n) {
static const char *thousands[] = {"", "M", "MM", "MMM"};
static const char *hundreds[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
static const char *tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
static const char *ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
static const char **units[] = {thousands, hundreds, tens, ones};
int units_n = sizeof units / sizeof units[0];
assert(n > 0 && n < 4000);
char res[16];
char *p = res;
int multiplier = 1000;
for (int i = 0; i < units_n; i++) {
strcpy(p, units[i][n / multiplier]);
p += strlen(p);
n %= multiplier;
multiplier /= 10;
}
printf("<%s>\n", res);
}
int main() {
print_roman(3888);
print_roman(3999);
print_roman(1010);
print_roman(42);
}
输出
<MMMDCCCLXXXVIII>
<MMMCMXCIX>
<MX>
<XLII>
我正在编写一个将十进制数转换为罗马数的程序。我使用 4 个数组 thousands
、hundreds
、tens
、units
来存储罗马数字中的数字,然后将每个数字复制到 res
数组,我使用 str
指向跟踪 res
中字符串开始位置的指针。当我用输入 128
进行测试时,它会打印出 CXXVIIIIX
,它必须是 CXXVIII
。我试过调试得到的结果是 tmp=8
是 strlen(units[tmp-1]) = 6
,这意味着 strlen
也算作 IX
。对于某些情况,例如 3888
,程序会打印出垃圾值。
这是我的代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#include <string.h>
#include <windows.h>
int main(){
int n=128;
char thousands[][4]={"M","MM","MMM"};
char hundreds[][5]={"C","CC","CCC","CD", "D", "DC", "DCC", "DCCC", "CM"};
char tens[][5]={"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
char units[][5]={"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
char res[16];
res[15]='[=11=]'; //add nul to the last character of res array
char *str=res+14; //str tracks where the string start
int tmp;
//store roman digits in res array in reverse order,
//start from units->tens->hundreds->thousands
if (n!=0)
{
tmp=n%10;
if (tmp!=0)
{
str-=strlen(units[tmp-1]); //str steps back several address to store new digits
strncpy(str, units[tmp-1], strlen(units[tmp-1])); //use strncpy to copy without '[=11=]'
}
n/=10;
}
if (n!=0)
{
tmp=n%10;
if (tmp!=0)
{
str-=strlen(tens[tmp-1]);
strncpy(str, tens[tmp-1], strlen(tens[tmp-1]));
}
n/=10;
}
if (n!=0)
{
tmp=n%10;
if (tmp!=0)
{
str-=strlen(hundreds[tmp-1]);
strncpy(str, hundreds[tmp-1], strlen(hundreds[tmp-1]));
}
n/=10;
}
if (n!=0)
{
tmp=n%10;
if (tmp!=0)
{
str-=strlen(thousands[tmp-1]);
strncpy(str, thousands[tmp-1], strlen(thousands[tmp-1]));
}
n/=10;
}
printf("%s", str);
return 0;
}
那么为什么会发生这种情况以及如何解决它?
如有任何帮助,我们将不胜感激。
修复所有字符串文字的数组大小后,出现差一错误和未初始化的 char 数组元素。
char res[16];
对于"MMMDCCCLXXXVIII"
和尾随'[=14=]'
就足够了。- 使用
res[15]='[=15=]';
将字符串终止存储在最后一个元素中。 char *str=res+14;
设置指向在此之前未初始化字符的指针。str-=strlen(something)
根据要插入的字符串的长度移动指针。以下strncpy
不会覆盖未初始化的字符
结果将始终包含未初始化的尾随字符,该字符可能可见也可能不可见。
由于尾随未初始化字符,最大长度 ("MMMDCCCLXXXVIII"
) 的结果将在第一个数组元素之前的一个字符开始。您将得到一个结果字符串 "MMMDCCCLXXXVIII*"
,其中 *
未初始化。第二个 M
是 res
的第一个数组元素,第一个 M
将是第一个元素之前的元素。
演示差一错误的示例:
基于问题的原始(错误)代码。
请注意,这不是完整代码,而是一个简化的摘录,旨在显示变量值如何随输入值变化 n=3888
。 (为了使代码更短,此处缺少原始代码中的 if (tmp!=0)
守卫。)
在显示变量值的注释中,*
用于表示未初始化的字符。
int n=3888;
char res[16];
// * indicates an uninitialized character
res[15]='[=10=]'; // "***************[=10=]"
// The following initialization is wrong.
char *str=res+14; // str = &res[14] (the last uninitialized character)
int tmp;
// The following block leaves the last character of res uninitialized.
tmp=n%10; // tmp = 8
str-=strlen(units[tmp-1]); // "VIII" (strlen = 4) -> str = &res[10]
strncpy(str, units[tmp-1], strlen(units[tmp-1])); // "**********VIII*[=10=]"
n/=10; // n = 388
// Here, str points to the first character written by the previous block.
tmp=n%10; // tmp = 8
str-=strlen(tens[tmp-1]); // "LXXX" (strlen = 4) -> str = &res[6]
strncpy(str, tens[tmp-1], strlen(tens[tmp-1])); // ******LXXXVIII*[=10=]"
n/=10; // n = 38
/* ...*/
// final result would be
// str = &res[-1]
// str -> "MMMDCCCLXXXVIII*[=10=]"
// res = "MMDCCCLXXXVIII*[=10=]"
// The last strncpy tries to write a character 'M' one
// element before the beginning of the array res
// corresponding to strncpy(res[-1], "MMM", 3)
我建议作为修复
char res[16] = {0}; // initialize the whole array with 0
char *str=res+(sizeof(res)-1);
... and how to fix it?
考虑以 1,000 为单位编写单位,然后是 100、十、个位的顺序。
在每个单元列表中添加一个“零”。
滚入辅助函数。
将类型从字符数组更改为指针数组。
例子
void print_roman(int n) {
static const char *thousands[] = {"", "M", "MM", "MMM"};
static const char *hundreds[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
static const char *tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
static const char *ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
static const char **units[] = {thousands, hundreds, tens, ones};
int units_n = sizeof units / sizeof units[0];
assert(n > 0 && n < 4000);
char res[16];
char *p = res;
int multiplier = 1000;
for (int i = 0; i < units_n; i++) {
strcpy(p, units[i][n / multiplier]);
p += strlen(p);
n %= multiplier;
multiplier /= 10;
}
printf("<%s>\n", res);
}
int main() {
print_roman(3888);
print_roman(3999);
print_roman(1010);
print_roman(42);
}
输出
<MMMDCCCLXXXVIII>
<MMMCMXCIX>
<MX>
<XLII>