本地阵列重复一个循环中! C ++ [英] A local array repeats inside a loop! C++
问题描述
的 current_name
是本地字符
下面的循环中的数组。我宣布它的循环中,因此改变我每次从文件中读取一个新行的时间。但是,由于某些原因,previous数据不会从 current_name
删除!它打印的旧数据,如果它没有被新的人物,从下一行覆盖。
什么想法?
而(isOpen会和放大器;&安培;!file.eof()){
烧焦current_line [LINE];
焦炭current_name [NAME]; file.getline(current_line,LINE); INT I = 0;
而(current_line [I] =';'){
current_name [I] = current_line [I]
我++;
} COUT<< current_name<< ENDL;
}
你不灌装后终止 current_name
。添加 current_name [I] = 0
内部循环后之前的 COUT
。你可能会看到这一点,如果你读 ABCDEF
然后读 JKL
和可能得到 jkldef
输出
更新
您想知道是否有更好的方法。还有 - 我们会得到它。但是,从Java来了,你的问题和后续鉴定,我相信你应该知道的一些较大的问题。小心你希望的东西 - 你实际上可能得到它[和更多的] :-)。下面所有的都是基于爱...
注意所有的Java程序员!欢迎来到美丽新世界!
块引用>基本概念
之前,我们甚至到
C
在语言,我们需要谈谈几个概念第一。计算机体系结构:结果
https://en.wikipedia.org/wiki/Computer_architecture 结果
https://en.wikipedia.org/wiki/Instruction_set计算机程序的内存布局:结果
http://www.geeksforgeeks.org/memory-layout-of-c-程序/内存地址/指针和Java引用的区别:结果
就是Java"传递按引用"或QUOT;通过按值"?结果
<一href=\"http://programmers.stackexchange.com/questions/141834/how-is-a-java-reference-different-from-a-c-pointer\">http://programmers.stackexchange.com/questions/141834/how-is-a-java-reference-different-from-a-c-pointer
概念格格不入的Java程序员
块引用>C语言为您提供了直接访问底层的的计算机体系结构的。它会的不的做任何事情,你不这样做的明确的规定。在此,我提
C
[为了简洁],但我真正谈论的是组合的内存布局和计算机体系结构的
- 如果你读的内存,你没有初始化,你会看到看似随机的数据。
- 如果您从堆中分配的东西,你必须明确地释放它。它不会奇迹般地得到标志着由垃圾收集器删除时,它超出范围。
- 还有的没有的垃圾收集器使用C
- C指针更强大了Java引用。您可以加减值指针。你可以减去两个指针,并使用差作为指标值。你可以通过数组循环的没有的使用指标变量 - 你只是一个尊重指针和递增指针。
- 自动变量的Java中的数据被存储在堆上。每个变量都需要一个单独的堆分配。这是缓慢而费时的。
- 在C,自动变量的数据存储在的堆栈帧的。堆栈帧是的字节的连续区域。要分配的栈帧空间,C简单地减去堆栈指针[硬件寄存器]所需的大小。堆栈帧的大小的总和的所有的给定函数的范围内的变量,不管他们是否在函数内部循环中声明所。
- 其初始值取决于什么previous功能使用的区域,什么字节的值它存储在那里。因此,如果
主
调用函数fnca
,它将填充任何数据堆栈。如果那么主
通话fncb
它会看到fnca的价值观,这是半随机尽可能fncb而言。在使用之前,他们都fnca和fncb必须初始化堆栈变量。- 没有C变量的声明的一个初始化的条款做的不的初始化变量。为bss区,这将是零。对于一个堆栈变量,你必须做明确。
- 还有的没有的C中的数组索引的范围检查[或指向数组的指针或为此事数组元素。如果你写超出规定区域,你会写成任何已被映射/挂入内存区域旁边。例如,如果你有一个存储区:
INT X [10]; INTŸ;
和你[无意中]写入X [10]
[一举超越结束]你会损坏ÿ
- 这无论哪个内存部分(例如数据,BSS,堆或堆栈)阵列中也是如此。
- C具有的一个的字符串没有的概念的。当人们谈论一个C字符串他们是什么样的真正的谈论是有一个字符串的末尾(又名一个
字符
阵列EOS)的定点的角色在有效数据的末尾。 标准EOS焦炭几乎普遍定义为0x00 [自〜1970]- 唯一的内在的由架构支持的类型有:
字符
,短
,INT
,长
/指针
,长长
和浮点/双精度
。有可能是在给定的拱一些人,但是这是通常的名单。其他的一切(如类
或结构
是建立由编译器为方便从程序员拱形的内在的类型)下面有一些事情是的有关C [和C ++]:结果
- C具有的 preprocessor 的宏。 Java没有宏的概念。 preprocessor宏可以被认为是的元编程的原始形式。结果
- C有在线
功能。它们看起来就像普通的功能,但是编译器将尝试直接将其code到调用之一的任何功能。这是非常方便函数是否干净定义,但小(例如几行)。它节省的实际调用该函数的开销。例子
下面有几个版本的原程序为例:
// myfnc1 - 原创
空虚
myfnc1(无效)
{
istream的文件; 而(isOpen会&放大器;&放大器;!file.eof()){
烧焦current_line [LINE];
焦炭current_name [NAME]; file.getline(current_line,LINE); INT I = 0; 而(current_line [I] =';'){
current_name [I] = current_line [I]
我++;
} current_name [I] = 0; COUT&LT;&LT; current_name&LT;&LT; ENDL;
}
}// myfnc2 - 移动定义的功能范围
空虚
myfnc2(无效)
{
istream的文件;
INT I;
烧焦current_line [LINE];
焦炭current_name [NAME]; 而(isOpen会&放大器;&放大器;!file.eof()){
file.getline(current_line,LINE); I = 0; 而(current_line [I] =';'){
current_name [I] = current_line [I]
我++;
} current_name [I] = 0; COUT&LT;&LT; current_name&LT;&LT; ENDL;
}
}// myfnc3 - 转化为循环
空虚
myfnc(无效)
{
istream的文件;
INT I;
烧焦current_line [LINE];
焦炭current_name [NAME]; 而(isOpen会&放大器;&放大器;!file.eof()){
file.getline(current_line,LINE); 对于(i = 0; current_line [I] =';'; ++ I)
current_name [I] = current_line [I]
current_name [I] = 0; COUT&LT;&LT; current_name&LT;&LT; ENDL;
}
}// myfnc4 - 转换为使用指针
空虚
myfnc4(无效)
{
istream的文件;
为const char *线;
字符*名称;
烧焦current_line [LINE];
焦炭current_name [NAME]; 而(isOpen会&放大器;&放大器;!file.eof()){
file.getline(current_line,LINE); 名称= current_name;
为(行= current_line; *行=';'; ++行++名称)
*名称= *线;
*名称= 0; COUT&LT;&LT; current_name&LT;&LT; ENDL;
}
}// myfnc5 - 更有效地利用指针
空虚
myfnc5(无效)
{
istream的文件;
为const char *线;
字符*名称;
INT CHR;
烧焦current_line [LINE];
焦炭current_name [NAME]; 而(isOpen会&放大器;&放大器;!file.eof()){
file.getline(current_line,LINE); 名称= current_name;
行= current_line;
对于(CHR = *行++; CHR =';'; CHR = *行++,++名)
*名称= CHR;
*名称= 0; COUT&LT;&LT; current_name&LT;&LT; ENDL;
}
}// myfnc6 - 修复的bug,如果行有没有分号
空虚
myfnc6(无效)
{
istream的文件;
为const char *线;
字符*名称;
INT CHR;
烧焦current_line [LINE];
焦炭current_name [NAME]; 而(isOpen会&放大器;&放大器;!file.eof()){
file.getline(current_line,LINE); 名称= current_name;
行= current_line;
对于(CHR = *行++; CHR = 0;!CHR = *行++,++名字){
如果(CHR ==';')
打破;
*名称= CHR;
}
*名称= 0; COUT&LT;&LT; current_name&LT;&LT; ENDL;
}
}// myfnc7 - 再codeD使用的智能的字符串
空虚
myfnc7(无效)
{
istream的文件;
为const char *线;
字符*名称;
INT CHR;
烧焦current_line [LINE];
xstr_t current_name;
xstr_t *名称; 名称=安培; current_name;
xstrinit(名); 而(isOpen会&放大器;&放大器;!file.eof()){
file.getline(current_line,LINE); xstragain(名); 行= current_line;
对于(CHR = *行++; CHR = 0;!CHR = *行++){
如果(CHR ==';')
打破;
xstraddchar(姓名,CHR);
} COUT&LT;&LT; xstrcstr(名)LT;&LT; ENDL;
} xstrfree(名);
}下面是一个聪明的字符串[缓冲区]类类似于您已经习惯了什么:
// XSTR - 智能字符串阶级对于Ctypedef结构{
为size_t xstr_maxlen; //在字符串缓冲区最大空间
字符* xstr_lhs; //指针开始串
字符* xstr_rhs; //指针开始串
} xstr_t;// xstrinit - 复位字符串缓冲区
空虚
xstrinit(xstr_t * XSTR)
{ memset的(XSTR,0,sizeof的(XSTR));
}// xstragain - 复位字符串缓冲区
空虚
xstragain(xstr_t XSTR)
{ xstr-&GT; xstr_rhs = xstr-&GT; xstr_lhs;
}// xstrgrow - 长字符串缓冲区
空虚
xstrgrow(xstr_t * XSTR,为size_t needlen)
{
为size_t curlen;
为size_t newlen;
字符* LHS; LHS = xstr-&GT; xstr_lhs; //获取量,我们正在使用
curlen = xstr-&GT; xstr_rhs - LHS; //获取添加后,我们需要什么量
newlen = curlen + needlen + 1; //分配更多的,如果我们需要它
如果((newlen + 1)GT = xstr-&GT; xstr_maxlen){
//分配,我们需要什么样再加上多一点,所以我们不叫上
//每个加载操作
xstr-&GT; xstr_maxlen = newlen + 100; //获取更多的内存
LHS = realloc的(左,xstr-&GT; xstr_maxlen);
xstr-&GT; xstr_lhs = LHS; //调整追加指针
xstr-&GT; xstr_rhs = LHS + curlen;
}
}// xstraddchar - 添加字符的字符串
空虚
xstraddchar(xstr_t * XSTR,INT CHR)
{ //在字符串缓冲区得到更多的空间,如果我们需要它
xstrgrow(XSTR,1); //添加字符
* xstr-&GT; xstr_rhs ++ = CHR; //维持定点/ EOS因为我们走
* xstr-&GT; xstr_rhs = 0;
}// xstraddstr - 添加串来串
空虚
xstraddstr(xstr_t * XSTR,为const char *海峡)
{
为size_t LEN; LEN = strlen的(STR); //在字符串缓冲区得到更多的空间,如果我们需要它
xstrgrow(XSTR,LEN); //添加字符串
的memcpy(xstr-&GT; xstr_rhs,STR,LEN);
* xstr-&GT; xstr_rhs + = LEN; //维持定点/ EOS因为我们走
* xstr-&GT; xstr_rhs = 0;
}// xstrcstr - 获得C字符串的价值
字符*
xstrcstr(xstr_t * XSTR,INT CHR)
{ 返回xstr-&GT; xstr_lhs;
}// xstrfree - 释放字符串缓冲区数据
空虚
xstrfree(xstr_t * XSTR)
{
字符* LHS; LHS = xstr-&GT; xstr_lhs;
如果(左!= NULL)
免费(左图); xstrinit(XSTR);
}建议
- 在尝试解决一个C字符串,接受它。你会遇到它在很多地方。这是不可避免的。
- 了解如何为轻松地操纵指针作为指标变量。他们更灵活,[一旦你得到他们的窍门]更容易使用。我见过谁没有学会这一点,他们的code程序员编写code比它需要[通常充满了我需要修复的bug]更加复杂。
- 好评论是在任何语言很重要,但是,也许,更何况用C比Java的某些事情。
- 与
-Wall
-Werror
总是编译和修复的任何的警告。你一直的警告的: - )- 我玩了一下周围用我给你的
myfnc
的例子。这可以帮助。- 您之前获取基础知识牢固掌握...
现在,关于C ++的字...
最上面的是关于架构,内存布局和C所有这一切的还是的适用于C ++。
C ++的确实的做堆栈变量比较有限填海当函数返回时,他们走出去的范围。这有它的长处和短处。
C ++有很多类,以缓解常用功能/成语/样板的枯燥。它具有
STD
标准模板库。它还具有升压
。例如,的std ::字符串
可能会做你想要什么。但是,它比较对我的第一XSTR但是,再一次,我要提醒你。在你的present程度,工作的从的基本面,而不是的围绕的他们。
The
current_name
is a localchar
array inside the following loop. I declared it inside the loop so it changes every time I read a new line from a file. But, for some reason the previous data is not removed from thecurrent_name
! It prints old data out if it wasn't overridden by new characters from the next line.ANY IDEAS?
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; }
解决方案You're not terminating
current_name
after filling it. Addcurrent_name[i] = 0
after the inner loop just before yourcout
. You're probably seeing this if you readabcdef
then readjkl
and probably getjkldef
for output
UPDATE
You wanted to know if there is a better way. There is--and we'll get to it. But, coming from Java, your question and followup identified some larger issues that I believe you should be aware of. Be careful what you wish for--you may actually get it [and more] :-). All of the following is based on love ...
Attention All Java Programmers! Welcome to "A Brave New World"!
Basic Concepts
Before we even get to
C
the language, we need to talk about a few concepts first.Computer Architecture:
https://en.wikipedia.org/wiki/Computer_architecture
https://en.wikipedia.org/wiki/Instruction_setMemory Layout of Computer Programs:
http://www.geeksforgeeks.org/memory-layout-of-c-program/Differences between Memory Addresses/Pointers and Java References:
Is Java "pass-by-reference" or "pass-by-value"?
http://programmers.stackexchange.com/questions/141834/how-is-a-java-reference-different-from-a-c-pointer
Concepts Alien to Java Programmers
The C language gives you direct access the underlying computer architecture. It will not do anything that you don't explicitly specify. Herein, I'm mentioning
C
[for brevity] but what I'm really talking about is a combination of the memory layout and the computer architecture.
- If you read memory that you didn't initialize, you will see seemingly random data.
- If you allocate something from the heap, you must explicitly free it. It doesn't magically get marked for deletion by a garbage collector when it "goes out of scope".
- There is no garbage collector in C
- C pointers are far more powerful that Java references. You can add and subtract values to pointers. You can subtract two pointers and use the difference as an index value. You can loop through an array without using index variables--you just deference a pointer and increment the pointer.
- The data of automatic variables in Java are stored in the heap. Each variable requires a separate heap allocation. This is slow and time consuming.
- In C, the data of automatic variables in stored in the stack frame. The stack frame is a contiguous area of bytes. To allocate space for the stack frame, C simply subtracts the desired size from the stack pointer [hardware register]. The size of the stack frame is the sum of all variables within a given function's scope, regardless of whether they're declared inside a loop inside the function.
- Its initial value depends upon what previous function used that area for and what byte values it stored there. Thus, if
main
calls functionfnca
, it will fill the stack with whatever data. If thenmain
callsfncb
it will see fnca's values, which are semi-random as far as fncb is concerned. Both fnca and fncb must initialize stack variables before they are used.- Declaration of a C variable without an initializer clause does not initialize the variable. For the bss area, it will be zero. For a stack variable, you must do that explicitly.
- There is no range checking of array indexes in C [or pointers to arrays or array elements for that matter]. If you write beyond the defined area, you will write into whatever has been mapped/linked into the memory region next. For example, if you have a memory area:
int x[10]; int y;
and you [inadvertently] write tox[10]
[one beyond the end] you will corrupty
- This is true regardless of which memory section (e.g. data, bss, heap, or stack) your array is in.
- C has no concept of a string. When people talk about a "c string" what they're really talking about is a
char
array that has an "end of string" (aka EOS) sentinel character at the end of the useful data. The "standard" EOS char is almost universally defined as 0x00 [since ~1970]- The only intrinsic types supported by an architecture are:
char
,short
,int
,long
/pointer
,long long
, andfloat/double
. There may be some others on a given arch, but that's the usual list. Everything else (e.g. aclass
orstruct
is "built up" by the compiler as a convenience to the programmer from the arch intrinsic types)Here are some things that are about C [and C++]:
- C has preprocessor macros. Java has no concept of macros. Preprocessor macros can be thought of as a crude form of metaprogramming.
- C hasinline
functions. They look just like regular functions, but the compiler will attempt to insert their code directly into any function that calls one. This is handy if the function is cleanly defined but small (e.g. a few lines). It saves the overhead of actually calling the function.
Examples
Here are several versions of your original program as an example:
// 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); }
Here is a "smart" string [buffer] class similar to what you're used to:
// 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); }
Recommendations
- Before you try to "get around" a "c string", embrace it. You'll encounter it in many places. It's unavoidable.
- Learn how to manipulate pointers as easily as index variables. They're more flexible and [once you get the hang of them] easier to use. I've seen code written by programmers who didn't learn this and their code is always more complex than it needs to be [and usually full of bugs that I've needed to fix].
- Good commenting is important in any language but, perhaps, more so in C than Java for certain things.
- Always compile with
-Wall
-Werror
and fix any warnings. You have been warned :-)- I'd play around a bit with the
myfnc
examples I gave you. This can help.- Get a firm grasp of the basics before you ...
And now, a word about C++ ...
Most of the above was about architecture, memory layout, and C. All of that still applies to C++.
C++ does do a more limited reclamation of stack variables when the function returns and they go out of scope. This has its pluses and minuses.
C++ has many classes to alleviate the tedium of common functions/idioms/boilerplate. It has the
std
standard template library. It also hasboost
. For example,std::string
will probably do what you want. But, compare it against my xstr first.But, once again, I wish to caution you. At your present level, work from the fundamentals, not around them.
这篇关于本地阵列重复一个循环中! C ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!