本地数组在循环内重复! C++
Posted
技术标签:
【中文标题】本地数组在循环内重复! C++【英文标题】:A local array repeats inside a loop! C++ 【发布时间】:2015-11-11 23:00:55 【问题描述】:current_name
是以下循环内的本地 char
数组。我在循环中声明了它,所以每次我从文件中读取新行时它都会改变。但是,由于某种原因,之前的数据没有从current_name
中删除!如果旧数据没有被下一行的新字符覆盖,它会打印出旧数据。
有什么想法吗?
while (isOpen && !file.eof())
char current_line[LINE];
char current_name[NAME];
file.getline(current_line, LINE);
int i = 0;
while (current_line[i] != ';')
current_name[i] = current_line[i];
i++;
cout << current_name << endl;
【问题讨论】:
没有异常。标准中没有规定块局部变量必须在每次迭代时改变值。只是您不能假设它将保持其价值。 局部变量不会自动初始化,因此在分配之前访问它会导致未定义的行为。 whywhile(!file.eof())
is always wrong
删除之前的数据有什么关系?您正在用当前行中的数据覆盖它。问题是您没有添加空终止符。
每次迭代就像一个新变量。所以正如@Barmar 所说,如果您在写入之前阅读它(在新的迭代中),它的数据是 undefined.
【参考方案1】:
您不会在填写 current_name
后终止它。在cout
之前的内部循环之后添加current_name[i] = 0
。如果您阅读 abcdef
然后阅读 jkl
并可能得到 jkldef
输出,您可能会看到这一点
更新
您想知道是否有更好的方法。有——我们会解决的。但是,来自 Java,您的问题和后续行动发现了一些我认为您应该注意的更大问题。小心你想要的——你实际上可能会得到它 [and more] :-)。以下都是基于爱...
所有 Java 程序员注意!欢迎来到“美丽新世界”!
基本概念
在我们了解C
语言之前,我们需要先谈谈一些概念。
计算机架构:https://en.wikipedia.org/wiki/Computer_architecturehttps://en.wikipedia.org/wiki/Instruction_set
计算机程序的内存布局:http://www.geeksforgeeks.org/memory-layout-of-c-program/
内存地址/指针与 Java 引用的区别:Is Java "pass-by-reference" or "pass-by-value"?https://softwareengineering.stackexchange.com/questions/141834/how-is-a-java-reference-different-from-a-c-pointer
Java 程序员的陌生概念
C 语言让您可以直接访问底层计算机架构。它不会做任何你没有明确指定的事情。在这里,我提到了C
[为简洁起见],但我真正谈论的是内存布局和计算机架构的组合。
main
调用函数fnca
,它将用任何数据填充堆栈。如果然后main
调用fncb
,它将看到 fnca 的值,就 fncb 而言,这些值是半随机的。 fnca 和 fncb 都必须在使用堆栈变量之前对其进行初始化。
C 变量声明没有 initializer 子句not 初始化变量。对于 bss 区域,它将为零。对于堆栈变量,您必须明确执行此操作。
没有对 C 中的数组索引 [或指向数组或数组元素的指针] 进行范围检查。如果您写入超出定义的区域,您将写入接下来已映射/链接到内存区域的任何内容。例如,如果您有一个内存区域:int x[10]; int y;
,而您 [不经意间] 写入 x[10]
[最后一个] 您将损坏 y
无论您的数组位于哪个内存部分(例如数据、bss、堆或堆栈),这都是正确的。
C 有 no 的 string 概念。当人们谈论“c 字符串”时,他们真正在谈论的是一个 char
数组,它有一个“字符串结尾”(又名 EOS)sentinel 字符在有用数据的末尾。 “标准” EOS char 几乎被普遍定义为 0x00 [自 ~1970]
架构支持的唯一内在类型是:char
、short
、int
、long
/pointer
、long long
和 float/double
。给定的拱门上可能还有其他一些,但这是通常的列表。其他所有内容(例如,class
或 struct
由编译器“构建”,以方便来自 Arch intrinsic 类型的程序员)
以下是关于 C [和 C++] 的一些内容:
- C 有 预处理器 宏。 Java 没有宏的概念。预处理器宏可以被认为是元编程的一种粗略形式。
- C 有inline
函数。它们看起来就像常规函数,但编译器会尝试将它们的代码直接插入到任何调用它的函数中。如果函数定义清晰但很小(例如几行),这很方便。它节省了实际调用函数的开销。
示例
以下是您的原始程序的几个版本作为示例:
// myfnc1 -- original
void
myfnc1(void)
istream file;
while (isOpen && !file.eof())
char current_line[LINE];
char current_name[NAME];
file.getline(current_line, LINE);
int i = 0;
while (current_line[i] != ';')
current_name[i] = current_line[i];
i++;
current_name[i] = 0;
cout << current_name << endl;
// myfnc2 -- moved definitions to function scope
void
myfnc2(void)
istream file;
int i;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof())
file.getline(current_line, LINE);
i = 0;
while (current_line[i] != ';')
current_name[i] = current_line[i];
i++;
current_name[i] = 0;
cout << current_name << endl;
// myfnc3 -- converted to for loop
void
myfnc(void)
istream file;
int i;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof())
file.getline(current_line, LINE);
for (i = 0; current_line[i] != ';'; ++i)
current_name[i] = current_line[i];
current_name[i] = 0;
cout << current_name << endl;
// myfnc4 -- converted to use pointers
void
myfnc4(void)
istream file;
const char *line;
char *name;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof())
file.getline(current_line, LINE);
name = current_name;
for (line = current_line; *line != ';'; ++line, ++name)
*name = *line;
*name = 0;
cout << current_name << endl;
// myfnc5 -- more efficient use of pointers
void
myfnc5(void)
istream file;
const char *line;
char *name;
int chr;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof())
file.getline(current_line, LINE);
name = current_name;
line = current_line;
for (chr = *line++; chr != ';'; chr = *line++, ++name)
*name = chr;
*name = 0;
cout << current_name << endl;
// myfnc6 -- fixes bug if line has no semicolon
void
myfnc6(void)
istream file;
const char *line;
char *name;
int chr;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof())
file.getline(current_line, LINE);
name = current_name;
line = current_line;
for (chr = *line++; chr != 0; chr = *line++, ++name)
if (chr == ';')
break;
*name = chr;
*name = 0;
cout << current_name << endl;
// myfnc7 -- recoded to use "smart" string
void
myfnc7(void)
istream file;
const char *line;
char *name;
int chr;
char current_line[LINE];
xstr_t current_name;
xstr_t *name;
name = ¤t_name;
xstrinit(name);
while (isOpen && !file.eof())
file.getline(current_line, LINE);
xstragain(name);
line = current_line;
for (chr = *line++; chr != 0; chr = *line++)
if (chr == ';')
break;
xstraddchar(name,chr);
cout << xstrcstr(name) << endl;
xstrfree(name);
这是一个类似于您习惯的“智能”字符串 [buffer] 类:
// xstr -- "smart" string "class" for C
typedef struct
size_t xstr_maxlen; // maximum space in string buffer
char *xstr_lhs; // pointer to start of string
char *xstr_rhs; // pointer to start of string
xstr_t;
// xstrinit -- reset string buffer
void
xstrinit(xstr_t *xstr)
memset(xstr,0,sizeof(xstr));
// xstragain -- reset string buffer
void
xstragain(xstr_t xstr)
xstr->xstr_rhs = xstr->xstr_lhs;
// xstrgrow -- grow string buffer
void
xstrgrow(xstr_t *xstr,size_t needlen)
size_t curlen;
size_t newlen;
char *lhs;
lhs = xstr->xstr_lhs;
// get amount we're currently using
curlen = xstr->xstr_rhs - lhs;
// get amount we'll need after adding the whatever
newlen = curlen + needlen + 1;
// allocate more if we need it
if ((newlen + 1) >= xstr->xstr_maxlen)
// allocate what we'll need plus a bit more so we're not called on
// each add operation
xstr->xstr_maxlen = newlen + 100;
// get more memory
lhs = realloc(lhs,xstr->xstr_maxlen);
xstr->xstr_lhs = lhs;
// adjust the append pointer
xstr->xstr_rhs = lhs + curlen;
// xstraddchar -- add character to string
void
xstraddchar(xstr_t *xstr,int chr)
// get more space in string buffer if we need it
xstrgrow(xstr,1);
// add the character
*xstr->xstr_rhs++ = chr;
// maintain the sentinel/EOS as we go along
*xstr->xstr_rhs = 0;
// xstraddstr -- add string to string
void
xstraddstr(xstr_t *xstr,const char *str)
size_t len;
len = strlen(str);
// get more space in string buffer if we need it
xstrgrow(xstr,len);
// add the string
memcpy(xstr->xstr_rhs,str,len);
*xstr->xstr_rhs += len;
// maintain the sentinel/EOS as we go along
*xstr->xstr_rhs = 0;
// xstrcstr -- get the "c string" value
char *
xstrcstr(xstr_t *xstr,int chr)
return xstr->xstr_lhs;
// xstrfree -- release string buffer data
void
xstrfree(xstr_t *xstr)
char *lhs;
lhs = xstr->xstr_lhs;
if (lhs != NULL)
free(lhs);
xstrinit(xstr);
建议
在尝试“绕过”“c 字符串”之前,请先接受它。你会在很多地方遇到它。这是不可避免的。 了解如何像操作索引变量一样轻松地操作指针。它们更灵活,[一旦你掌握了它们]更容易使用。我见过没有学过这个的程序员编写的代码,他们的代码总是比它需要的复杂得多[而且通常充满了我需要修复的错误]。 良好的注释在任何语言中都很重要,但在某些方面,在 C 中可能比在 Java 中更重要。 始终使用-Wall
-Werror
进行编译并修复任何警告。您已被警告 :-)
我会尝试一下我给你的myfnc
示例。这会有所帮助。
在您...之前牢牢掌握基础知识...
现在,谈谈 C++ ...
以上大部分内容是关于架构、内存布局和 C。所有这些仍然适用于 C++。
C++确实在函数返回并且超出范围时对堆栈变量进行更有限的回收。这有其优点和缺点。
C++ 有许多类来减轻常见函数/习语/样板的乏味。它有std
标准模板库。它也有boost
。例如,std::string
可能会做你想做的事。但是,首先将它与我的 xstr 进行比较。
但是,我想再次提醒您。在你目前的水平上,从基础开始工作,而不是围绕它们。
【讨论】:
没错!我要试试这个 效果很好!!您能解释一下我将如何以更好的方式防止这种情况发生(如果有的话)?因为,在 Java 中,我写的应该可以正常工作! @Mosedince C 风格的数组很奇怪。您可以改用 C++ 样式的数组,或者在本例中使用 C++ 字符串。【参考方案2】:添加current_name[i] = 0
;如上所述对我不起作用。
此外,我在 isOpen 上遇到错误,如问题所示。
因此,我徒手编写了一个修改后的程序,从问题中提供的代码开始,并进行调整直到它正常工作,因为输入文件包含两行文本,每组三个字母字符由“;”分隔,不带引号。也就是说,分隔代码是空格、分号、空格。此代码有效。
这是我的代码。
#define LINE 1000
int j = 0;
while (!file1.eof())
j++;
if( j > 20)break; // back up escape for testing, in the event of an endless loop
char current_line[LINE];
//string current_name = ""; // see redefinition below
file1.getline(current_line, LINE, '\n');
stringstream ss(current_line); // stringstream works better in this case
while (!ss.eof())
string current_name;
ss >> current_name;
if (current_name != ";")
cout << current_name << endl;
// End if(current_name....
// End while (!ss.eof...
// End while(!file1.eof() ...
file1.close();
cout << "Done \n";
【讨论】:
这重复了while...eof
的错误(两次)
没有错。注意一个是在文件上使用 eof,另一个是在字符串流上使用 elf。搜索它,您应该会在 *** 中看到示例。在标记错误然后将其标记下来之前,请确保您是正确的。建议你收回 -1。
不,这是一个错误。存在具有相同错误的其他帖子并不能使其成为非错误。 Read this page
没有错。检查文件流与字符串流的文档。你会得到同样的答案。并且不要忽视这样一个事实,这是完美的。请记住,字符串流与文件流。
eof
是ios
的成员,ios
是这两个流的基类。 ifstream
上的错误与sstream
上的错误一样多。以上是关于本地数组在循环内重复! C++的主要内容,如果未能解决你的问题,请参考以下文章
如何使用JavaScript将循环内数组中的所有项目相乘[重复]