我似乎无法弄清楚我的输出发生了什么.我正在读取多行用户输入并输出超出下边界的相应输入.出于某种原因,当我输出时,输出的字符串省略了字符串的第一个字符.谁能告诉我为什么会这样?
#include <stdio.h>
typedef struct{
char name[4];
int population;
} state;
enum { MAX_STATES = 10 };
int main()
{
state myStates[MAX_STATES];
int c;
int i = 0;
while ((c = getchar())!= EOF)
{
scanf("%s %d\n", myStates[i].name, &myStates[i].population);
i++;
}
// printf("Last character is [%d]\n", c);
printf("");
if (c <= 0)
{
for(int j = 0; j <= MAX_STATES; j++)
{
if(myStates[j].population >= 10)
printf("%s %d\n", myStates[j].name, myStates[j].population);
else
break;
}
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输入:
TX 23
CA 45
Run Code Online (Sandbox Code Playgroud)
输出:
X 23
A 45
Run Code Online (Sandbox Code Playgroud)
更新的代码:
#include <stdio.h>
typedef struct{
char name[4];
int population;
} State;
enum { MAX_STATES = 10 };
int main()
{
State myStates[MAX_STATES];
int i, j;
// Function to read in multiple lines (up to 10) of user input; loop
// controls in place, detects format problems, prevents string buffer
// overflows.
for (i = 0; i < MAX_STATES; i++)
{
if (scanf("%2s %d\n", myStates[i].name, &myStates[i].population) != 2)
break;
}
// Function to output (stdout) array of State structs that exceed 10
// population.
for(j = 0; j < i; j++)
{
if(myStates[j].population >= 10)
printf("%s %d\n", myStates[j].name, myStates[j].population);
else
break;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
发布的输出只会在输入小于10并且突然出现循环之前发生.当我没有那个break语句时,我在最后一行得到了垃圾输出.有什么建议可以提高产量吗?
更换:
int i = 0;
while ((c = getchar()) != EOF)
{
scanf("%s %d\n", myStates[i].name, &myStates[i].population);
i++;
}
Run Code Online (Sandbox Code Playgroud)
有:
int i;
for (i = 0; i < MAX_STATES; i++)
{
if (scanf("%3s %d", myStates[i].name, &myStates[i].population) != 2)
break;
}
Run Code Online (Sandbox Code Playgroud)
这可以防止您进入太多状态,使用for循环将循环控件放在适当的位置,检测格式问题,防止字符串缓冲区溢出,并将第一个字符读入名称.此外,scanf()如果以交互方式输入输入,则格式字符串中的尾随空格(例如空格或换行符)在格式字符串中是一个非常糟糕的主意.如果输入来自文件,则不太严重,但在大多数情况下仍然是不必要的.(有关详细信息,请参阅格式尾随空白scanf().)
保持一个循环
如果你真的坚持你需要一个while循环,那么你可以使用:
int i = 0;
while (i < MAX_STATES && (c = getchar()) != EOF)
{
ungetc(c, stdin);
if (scanf("%3s %d", myStates[i].name, &myStates[i].population) != 2)
break;
i++;
}
Run Code Online (Sandbox Code Playgroud)
要么:
int i = 0;
while (i < MAX_STATES && (c = getchar()) != EOF)
{
myStates[i].name[0] = c;
if (scanf("%2s %d", &myStates[i].name[1], &myStates[i].population) != 2)
break;
i++;
}
Run Code Online (Sandbox Code Playgroud)
请注意,这些while循环仍然保留了大量溢出保护 - 溢出主阵列,并溢出名称字段.请注意,两个scanf()语句中的一个使用%3s和另一个%2s; 你应该能够解释原因.(是的,空字节不计算在内scanf(),因此您必须在转换规范中使用'off-by-one'长度.)
毫无疑问,还有其他技术也可以使用.但是,我认为你会发现for循环更接近惯用C.
一种通常合理的替代方法是使用fgets()(或POSIX,getline()如果可用)来读取整行,然后sscanf()解析这些行.这通常会导致更具弹性的程序和更好的错误报告.它还阻止了那些试图将所有50个州的信息放在一条线上的人,或者将每个数据放在一条单独的行上并且在它们之间都有一条空行的人,从而避免使用格式错误的数据.你可以悄悄地坚持两个领域(如果你小心,只有两个领域).
我可以询问有关正确显示输出的建议吗?
你有:
printf("");
if (c <= 0)
{
for(int j = 0; j <= MAX_STATES; j++)
{
if(myStates[j].population >= 10)
printf("%s %d\n", myStates[j].name, myStates[j].population);
else
break;
}
}
Run Code Online (Sandbox Code Playgroud)
第一个printf()什么也没做; 它应该去.该if (c <= 0)条件是有点可疑.可以键入一个空字节(通常Control-@或者Control-Shift-2),尽管要破坏原始循环会有点困难.该for循环应该更像for (int j = 0; j < MAX_STATES; j++)-这是安全模板for的循环中C.你最经常使用for (int i = 0; i < MAX; i++).但是,您只想打印已读取的状态,因此MAX_STATES不需要使用,而是需要使用i作为限制.如果你真的只想要打印前9个州(CA,TX,FL,NY,IL,PA,OH,GA,NC - 参见维基百科 ;密歇根州只差10M,它说),那么if情况就好了.
所以,您可以使用(注意输入循环设置i为成功读取的状态数):
for (int j = 0; j < i; j++)
printf("State: %.2s, Pop'n: %dM\n", myStates[j].name, myStates[j].population);
Run Code Online (Sandbox Code Playgroud)
当然,您可以调整格式以满足您的要求.如果没有读取状态,或者读取的状态数,则不会打印任何内容.如果你真的想在人口中应用这个条件,你可以使用:
for (int j = 0; j < i; j++)
{
if (myStates[i].population >= 10)
printf("State: %.2s, Pop'n: %dM\n", myStates[j].name, myStates[j].population);
}
Run Code Online (Sandbox Code Playgroud)