本地阵列重复一个循环中! C ++ [英] A local array repeats inside a loop! C++

查看:178
本文介绍了本地阵列重复一个循环中! 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 local char 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 the current_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. Add current_name[i] = 0 after the inner loop just before your cout. You're probably seeing this if you read abcdef then read jkl and probably get jkldef 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_set

Memory 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 function fnca, it will fill the stack with whatever data. If then main calls fncb 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 to x[10] [one beyond the end] you will corrupt y
  • 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, and float/double. There may be some others on a given arch, but that's the usual list. Everything else (e.g. a class or struct 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 has inline 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 = &current_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 has boost. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆