局部数组在循环内重复! C ++ [英] A local array repeats inside a loop! C++

查看:164
本文介绍了局部数组在循环内重复! C ++的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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
}


解决方案

code> current_name 。在 cout 之前的内循环之后添加 current_name [i] = 0 。你可能看到这个,如果你阅读 abcdef 然后阅读 jkl ,并可能得到 jkldef 输出






UPDATE

你想知道是否有更好的方法。有 - 我们会得到它。但是,从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-program /



内存地址/指针和Java引用之间的差异:

我们的Java通过引用传递或通过值传递?

http://programmers.stackexchange.com/questions/141834/how-is-a-java-reference-different-from-ac-pointer







与Java程序员不相关的概念




C语言可让您直接访问底层的计算机体系结构 。这将做任何你不明确指定。这里,我提到 C [为了简洁],但我真正谈论的是内存布局和计算机体系结构的组合




  • 如果读取未初始化的内存,您将看到看似随机的数据。

  • 如果从堆中分配内容,则必须显式释放它。它不魔法地被标记为删除由垃圾回收器,当它超出范围。

  • C中的 垃圾回收器

  • C指针比Java引用强大得多。您可以向指针添加和减去值。您可以减去两个指针,并使用差值作为索引值。您可以使用索引变量循环访问数组,而不用 - 您只需参照指针并增加指针。

  • Java中的自动变量的数据存储在堆中。每个变量都需要单独的堆分配。这是缓慢和耗时的。

  • 在C中,自动变量的数据存储在堆栈框架中。堆栈帧是字节的连续区域。要为堆栈帧分配空间,C只需从堆栈指针[硬件寄存器]中减去所需的大小。堆栈帧的大小是给定函数范围内的所有变量的总和,而不管它们是否在函数内的循环中声明。

  • 其初始值取决于该区域使用的先前函数以及存储在其中的字节值。因此,如果 main 调用函数 fnca ,它将用任何数据填充堆栈。如果 main 调用 fncb ,它将看到fnca的值,就fncb而言,它是半随机的。 fnca和fncb必须在使用之前初始化堆栈变量。

  • 没有初始值设定器子句的C变量声明不会初始化变量。对于bss区域,它将为零。

  • 在C [或指向数组或数组元素的指针中有范围检查数组索引]。如果写入超出定义的区域,您将写入下一个映射/链接到内存区域的任何内容。例如,如果你有一个内存区域: int x [10]; int y; 并且你[无意地]写入 x [10] [一个超出结束],将损坏 y

  • 无论数组在哪个内存段(例如数据,bss,堆或堆栈),都是如此。

  • C具有字符串概念。当人们谈论一个c字符串,他们真正的是一个 char 数组,有一个字符串结束 EOS) sentinel 字符。 标准EOS字符几乎被普遍定义为0x00 [since〜1970]

  • 架构支持的唯一内部类型是: char short int long / 指针 long long float / double 。在给定的拱门上可能有一些其他的,但这是通常的列表。其他一切(例如 class struct 是由编译器构建,以方便程序员从arch intrinsic 类型)
  • 和C ++]:

    - C具有预处理器宏。 Java没有宏的概念。预处理器宏可以被认为是元编程的粗略形式。

    - C具有 inline 函数。它们看起来像普通函数,但是编译器会尝试将它们的代码直接插入到任何调用它的函数中。如果函数被清楚地定义但是很小(例如几行),这是方便的。






    示例
    $ b

    以下是原始程序的几个版本:

      // myfnc1  - 原始
    无效
    myfnc1(void)
    {
    istream文件;

    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 - 将定义移动到函数范围
    void
    myfnc2(void)
    {
    istream文件;
    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 - 转换为for循环
    void
    myfnc(void)
    {
    istream文件;
    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 - 转换为使用指针
    void
    myfnc4(void)
    {
    istream文件;
    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 - 更有效地使用指针
    void
    myfnc5(void)
    {
    istream文件;
    const char * line;
    char * name;
    int chr;
    char current_line [LINE];
    char当前名称[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 - 如果行没有分号则修复bug
    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 ==';')

    * name = chr;
    }
    * name = 0;

    cout<< current_name<< endl
    }
    }

    // myfnc7 - 重新编码以使用smart字符串
    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 ==';')

    xstraddchar(name,chr);
    }

    cout<< xstrcstr(name)<< endl
    }

    xstrfree(name);
    }






    string [buffer] class类似于你常用的:

      // xstr  - smartstring for C 

    typedef struct {
    size_t xstr_maxlen; //字符串缓冲区中的最大空间
    char * xstr_lhs; //指向字符串开头的指针
    char * xstr_rhs; //指向字符串开头的指针
    } xstr_t;

    // xstrinit - 重置字符串缓冲区
    void
    xstrinit(xstr_t * xstr)
    {

    memset(xstr, sizeof(xstr));
    }

    // xstragain - 重置字符串缓冲区
    void
    xstragain(xstr_t xstr)
    {

    xstr- > xstr_rhs = xstr-> xstr_lhs;
    }

    // xstrgrow - 增长字符串缓冲区
    void
    xstrgrow(xstr_t * xstr,size_t needlen)
    {
    size_t curlen ;
    size_t newlen;
    char * lhs;

    lhs = xstr-> xstr_lhs;

    //获取我们当前使用的数量
    curlen = xstr-> xstr_rhs - lhs;

    //添加后需要的数量
    newlen = curlen + needlen + 1;

    //如果需要,分配更多
    if((newlen + 1)> = xstr-> xstr_maxlen){
    //分配我们需要的加更多,所以我们不被调用
    //每次添加操作
    xstr-> xstr_maxlen = newlen + 100;

    //获得更多内存
    lhs = realloc(lhs,xstr-> xstr_maxlen);
    xstr-> xstr_lhs = lhs;

    //调整附加指针
    xstr-> xstr_rhs = lhs + curlen;
    }
    }

    // xstraddchar - 将字符添加到字符串
    void
    xstraddchar(xstr_t * xstr,int chr)
    {

    //如果需要,在字符串缓冲区中获得更多的空间
    xstrgrow(xstr,1);

    //添加字符
    * xstr-> xstr_rhs ++ = chr;

    //我们继续保持sentinel / EOS
    * xstr-> xstr_rhs = 0;
    }

    // xstraddstr - 将字符串添加到字符串
    void
    xstraddstr(xstr_t * xstr,const char * str)
    {
    size_t len;

    len = strlen(str);

    //如果需要,在字符串缓冲区中获得更多的空间
    xstrgrow(xstr,len);

    //添加字符串
    memcpy(xstr-> xstr_rhs,str,len);
    * xstr-> xstr_rhs + = len;

    //我们继续保持sentinel / EOS
    * xstr-> xstr_rhs = 0;
    }

    // xstrcstr - 获取c string值
    char *
    xstrcstr(xstr_t * xstr,int chr)
    {

    return xstr-> xstr_lhs;
    }

    // xstrfree - 释放字符串缓冲区数据
    void
    xstrfree(xstr_t * xstr)
    {
    char * lhs;

    lhs = xstr-> xstr_lhs;
    if(lhs!= NULL)
    free(lhs);

    xstrinit(xstr);
    }






    / strong>




    • 在尝试绕过一个c字符串之前,你会在许多地方遇到它。这是不可避免的。

    • 了解如何像索引变量一样轻松操作指针。他们更灵活,[一旦你得到它的悬挂]更容易使用。我看到的程序员编写的代码没有学习这些,他们的代码总是比它需要更复杂[通常充满了我需要修复的错误]。

    • 良好的注释在任何语言中都很重要,但是对于某些事情,在C中比Java更重要。

    • 始终使用 -Wall -Werror 编译并修复 any 警告。
    • 给你。





    现在,关于C ++的一句话...



    上面的大部分是关于架构,内存布局和C.所有这些仍然适用于C ++。



    C ++ 当函数返回并且它们超出范围时回收堆栈变量。



    C ++有许多类来缓解常见函数/惯用法/模板的繁琐。它有 std 标准模板库。它也有 boost 。例如, std :: string 可能会做你想要的。但是,首先将它与我的xstr比较。



    但是,我再次提醒你。在您目前的水平上, 的基本工作不是


    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天全站免登陆