Visual Studio 文件输出错误
Visual Studio file output error
当我在 eclipse 上构建下面的代码时,命令行上的一切都按预期工作,输出文件中的一切看起来都应该如此。
#include <stdio.h>
#include <stdlib.h> // for exit()
#include <string.h>
int main(int argc, char *argv[])
{
FILE *in, *out; // declare two FILE pointers
int ch;
// check for command-line arguments
if (argc < 2)
{
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
// set up input
if ((in = fopen(argv[1], "r")) == NULL)
{
fprintf(stderr, "I couldn't open the file \"%s\"\n",
argv[1]);
exit(EXIT_FAILURE);
}
// set up output
char *ptd;
ptd = (char*)malloc(150 * sizeof(char));
char x;
int i = 0;
while ((x = getchar()) != '\n'){
ptd[i] = x;
i++;
}
ptd[i + 1] = '[=11=]';
strcat(ptd, ".txt"); // append .txt
if ((out = fopen(ptd, "w")) == NULL)
{ // open file for writing
fprintf(stderr, "Can't create output file.\n");
exit(3);
}
// copy data
while ((ch = getc(in)) != EOF)
putc(ch, out); // print every 3rd char
// clean up
if (fclose(in) != 0 || fclose(out) != 0)
fprintf(stderr, "Error in closing files\n");
free(ptd);
return 0;
}
但是当我用 visual studio 构建它时,我在文件名后得到这个东西“Í”直到它达到 .txt 并且它使用所有分配的内存作为文件名。为什么会这样?
您没有正确终止字符串:
while ((x = getchar()) != '\n'){
ptd[i] = x;
i++;
}
ptd[i + 1] = '[=10=]';
此处 i
在循环中递增,这样最后的 i
将已经超过构建的字符串。因此 ptd[i + 1] = '[=13=]';
行中的额外增量将留下一个未初始化的字符。所以正确的代码是 ptd[i] = '[=14=]';
int i = 0;
while ((x = getchar()) != '\n'){
ptd[i] = x;
i++; <=== increment, hence 'i' points to the char *after* the last one
}
ptd[i + 1] = '[=10=]'; <== now you move *one char more* from the last one
strcat(ptd, ".txt"); // append .txt
工作原理如下:
输入字符串(来自getchar
)="abc\n"
在malloc
ptd[]=<junk>
之后
读取标准输入后ptd[]="abc"<junk>
while 循环后 i
==3 并指向剩余部分的第一个字节 <junk>
;
现在 ptd[i+1]
即 ptd[4]
设置为 0
这是 ptd 的样子:ptd[]="abc"<junk char><zero>
因为 malloc
不会将内存初始化为零(calloc
会),这恰好发生在 GCC 中为零而不是 visual studio 中的零。
为什么?
您仍然可以在 eclipse 中重现它。
在 ptd = (char*)malloc(150 * sizeof(char));
行之前添加一个循环以从堆中分配并填充垃圾,以使 ptd
重用该垃圾。
char *p[20];
int n,j;
for(n=0;n<20;++n)
{
p[n]=(char*)malloc(150 * sizeof(char));
for(j=0;j<150;++j)
{
p[n][j]=(char)n*11+j+3;
}
}
for(n=0;n<20;++n)
{
free(p[n]);
}
这意味着(可能)用 GCC 编译的程序从干净的堆开始,而 MSVC 从充满垃圾的堆开始。
MSVC 在用垃圾填充堆时可以做到这一点,这有助于检测像您所做的那种人为错误。
查看此页面:https://msdn.microsoft.com/en-us/library/974tc9t1.aspx“使用调试堆查找缓冲区溢出”部分:
The debug heap also fills new memory blocks with a known value.
Currently, the actual byte values used are as follows:
New objects (0xCD)
New objects are filled with 0xCD when they are allocated.
那么,如果你看这里:http://www.idautomation.com/product-support/ascii-chart-char-set.html
你会看到:
Í 205 0xcd 0315 Í PostScript (”) hungarumlaut
就是您在文件名中看到的字符。
当我在 eclipse 上构建下面的代码时,命令行上的一切都按预期工作,输出文件中的一切看起来都应该如此。
#include <stdio.h>
#include <stdlib.h> // for exit()
#include <string.h>
int main(int argc, char *argv[])
{
FILE *in, *out; // declare two FILE pointers
int ch;
// check for command-line arguments
if (argc < 2)
{
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
// set up input
if ((in = fopen(argv[1], "r")) == NULL)
{
fprintf(stderr, "I couldn't open the file \"%s\"\n",
argv[1]);
exit(EXIT_FAILURE);
}
// set up output
char *ptd;
ptd = (char*)malloc(150 * sizeof(char));
char x;
int i = 0;
while ((x = getchar()) != '\n'){
ptd[i] = x;
i++;
}
ptd[i + 1] = '[=11=]';
strcat(ptd, ".txt"); // append .txt
if ((out = fopen(ptd, "w")) == NULL)
{ // open file for writing
fprintf(stderr, "Can't create output file.\n");
exit(3);
}
// copy data
while ((ch = getc(in)) != EOF)
putc(ch, out); // print every 3rd char
// clean up
if (fclose(in) != 0 || fclose(out) != 0)
fprintf(stderr, "Error in closing files\n");
free(ptd);
return 0;
}
但是当我用 visual studio 构建它时,我在文件名后得到这个东西“Í”直到它达到 .txt 并且它使用所有分配的内存作为文件名。为什么会这样?
您没有正确终止字符串:
while ((x = getchar()) != '\n'){
ptd[i] = x;
i++;
}
ptd[i + 1] = '[=10=]';
此处 i
在循环中递增,这样最后的 i
将已经超过构建的字符串。因此 ptd[i + 1] = '[=13=]';
行中的额外增量将留下一个未初始化的字符。所以正确的代码是 ptd[i] = '[=14=]';
int i = 0;
while ((x = getchar()) != '\n'){
ptd[i] = x;
i++; <=== increment, hence 'i' points to the char *after* the last one
}
ptd[i + 1] = '[=10=]'; <== now you move *one char more* from the last one
strcat(ptd, ".txt"); // append .txt
工作原理如下:
输入字符串(来自getchar
)="abc\n"
在malloc
ptd[]=<junk>
读取标准输入后ptd[]="abc"<junk>
while 循环后 i
==3 并指向剩余部分的第一个字节 <junk>
;
现在 ptd[i+1]
即 ptd[4]
设置为 0
这是 ptd 的样子:ptd[]="abc"<junk char><zero>
因为 malloc
不会将内存初始化为零(calloc
会),这恰好发生在 GCC 中为零而不是 visual studio 中的零。
为什么?
您仍然可以在 eclipse 中重现它。
在 ptd = (char*)malloc(150 * sizeof(char));
行之前添加一个循环以从堆中分配并填充垃圾,以使 ptd
重用该垃圾。
char *p[20];
int n,j;
for(n=0;n<20;++n)
{
p[n]=(char*)malloc(150 * sizeof(char));
for(j=0;j<150;++j)
{
p[n][j]=(char)n*11+j+3;
}
}
for(n=0;n<20;++n)
{
free(p[n]);
}
这意味着(可能)用 GCC 编译的程序从干净的堆开始,而 MSVC 从充满垃圾的堆开始。
MSVC 在用垃圾填充堆时可以做到这一点,这有助于检测像您所做的那种人为错误。
查看此页面:https://msdn.microsoft.com/en-us/library/974tc9t1.aspx“使用调试堆查找缓冲区溢出”部分:
The debug heap also fills new memory blocks with a known value.
Currently, the actual byte values used are as follows:
New objects (0xCD)
New objects are filled with 0xCD when they are allocated.
那么,如果你看这里:http://www.idautomation.com/product-support/ascii-chart-char-set.html
你会看到:
Í 205 0xcd 0315 Í PostScript (”) hungarumlaut
就是您在文件名中看到的字符。