循环内的结构数组

时间:2016-09-01 21:33:30

标签: c arrays loops struct

我正在学习C和我在玩结构,但我发现了一些我无法解释的行为,我想知道它为什么会发生。

这是我的代码:

struct my_struct{
  char *name;
};

int main()
{

  struct my_struct arr[3];
  int i = 0;
  char str[10];

  while (i<3)
  {
    fgets(str, 10, stdin);
    arr[i].name = str;
    printf("Array number %d: %s", i, arr[i].name); 
    i++;
  }

  printf("1 - %s\n2 - %s\n3 - %s", arr[0].name, arr[1].name, arr[2].name);

  return 0;
}

我的意见:

test1
test2
test3

预期产出:

Array number 0: test1

Array number 1: test2

Array number 2: test3
1 - test1

2 - test2

3 - test3

结果输出:

Array number 0: test1

Array number 1: test2

Array number 2: test3
1 - test3

2 - test3

3 - test3

结果输出:

问题是,只要while循环继续运行,它似乎没问题;然而,当它退出时,它似乎设置了每个&#34; name&#34;数组中结构的值到最后一个。

如果,一旦离开循环并且在最后一个printf()之前,我手动设置数组中最后一个结构的名称,这是唯一一个被更新的结构,但前面的结构是&#39;名称仍设置为循环内输入的最后一个名称。

我想我错过了关于内存管理的事情,比如在再次调用fgets()之前刷新缓冲区,或者无法弄清楚发生了什么。有谁知道这是关于什么的?

4 个答案:

答案 0 :(得分:5)

这就是你所期望的,以这种方式思考,你有char str[10],这是你的字符串存储的内存。当您使用arr[i].name = str设置每个数组名称时,您将char * name指向此内存。所以这就是你的for循环正在做的事情:

0. str = [];         arr[0].name = NULL; arr[1].name = NULL; arr[1].name = NULL;
1. str = [string1];  arr[0].name = str;  arr[1].name = NULL; arr[1].name = NULL;
2. str = [string2];  arr[0].name = str;  arr[1].name = str;  arr[1].name = NULL;
3. str = [string3];  arr[0].name = str;  arr[1].name = str;  arr[1].name = str;

因此,在循环结束时,所有arr.name指针都指向字符串,并且每次都编辑了字符串。如果您希望各个arr元素存储自己的字符串,那么最好这样做:

struct my_struct{
  char name[10]; // Note how each instance of `my_struct` now stores its own string.
};

int main() {
  struct my_struct arr[3];
  int i = 0;

  while (i<3) {
    fgets(arr[i].name, 10, stdin);
    printf("Array number %d: %s", i, arr[i].name); 
    i++;
  }

  printf("1 - %s\n2 - %s\n3 - %s", arr[0].name, arr[1].name, arr[2].name);

  return 0;
}

https://blogs.msdn.microsoft.com/visualstudioalm/2012/07/09/javascript-unit-tests-on-team-foundation-service-with-chutzpah/

作为最后一点,您应该避免使用fgets Live example。更喜欢(see here)

答案 1 :(得分:2)

您不能复制像<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/S14273" android:orderInCategory="100" android:title="High School" app:showAsAction="never" /> <item android:id="@+id/S14276" android:orderInCategory="100" android:title="Middle School" app:showAsAction="never" /> <item android:id="@+id/S14274" android:orderInCategory="100" android:title="Hillside" app:showAsAction="never" /> <item android:id="@+id/S14271" android:orderInCategory="100" android:title="Eisenhower" app:showAsAction="never" /> <item android:id="@+id/S14278" android:orderInCategory="100" android:title="Van Holten" app:showAsAction="never" /> <item android:id="@+id/S14277" android:orderInCategory="100" android:title="Milltown" app:showAsAction="never" /> <item android:id="@+id/S14275" android:orderInCategory="100" android:title="J.F.K. Primary" app:showAsAction="never" /> <item android:id="@+id/S14272" android:orderInCategory="100" android:title="Hamilton" app:showAsAction="never" /> <item android:id="@+id/S14269" android:orderInCategory="100" android:title="Crim Primary" app:showAsAction="never" /> <item android:id="@+id/S14268" android:orderInCategory="100" android:title="Bradely Gardens" app:showAsAction="never" /> <item android:id="@+id/S14264" android:orderInCategory="100" android:title="Adamsville" app:showAsAction="never" /> </menu> 那样的C字符串。

您正在做的是将每个arr[i].name = str指针设置为同一地址,由name表示。因此,当您致电str时,每个printf指向相同的字符串,namestr只会打印printf三次。

如果要复制字符串,请使用str。此外,您需要为strcpy变量分配内存。

答案 2 :(得分:2)

因为这是C,所以您需要自己管理所有内存构建/销毁/管理。

如果你只是学习C,那么你现在最好坚持使用char数组,而不是直接指向指针,除非你知道自己在做什么,或者至少不做一点研究/学习预先。

数组与指针不同,并且有一个帖子here详细说明了差异。

针对您的问题,您可以通过向代码添加更多输出来更清楚地了解正在发生的事情。使用%p格式说明符,可以打印出变量的指针位置。

有几种方法可以调试C程序以找出这些内容,例如在Linux上使用gdb或在Windows上使用Visual Studio,但这是最简单的显示方式。

在你的程序中添加一些调试输出,你得到这个:

#include<stdio.h>

struct my_struct{
  char *name;
};

int main()
{

  struct my_struct arr[3];
  int i = 0;
  char str[10];

  while (i<3)
  {
    fgets(str, 10, stdin);
    arr[i].name = str;
    printf("Array number %d: %s", i, arr[i].name); 
    printf("  %p\n", arr[i].name);
    i++;
  }

  printf("\n1 - %s (%p)\n2 - %s (%p)\n3 - %s (%p)", 
    arr[0].name, arr[0].name, 
    arr[1].name, arr[1].name,
    arr[2].name, arr[2].name);

  return 0;
}

这会产生以下输出(给定johnjacobjingle作为输入):

Array number 0: john
  0xfff8d96a
Array number 1: jacob
  0xfff8d96a
Array number 2: jingle
  0xfff8d96a

1 - jingle
 (0xfff8d96a)
2 - jingle
 (0xfff8d96a)
3 - jingle
 (0xfff8d96a)

从这里,我们可以看到你每次都明显覆盖相同的内存地址。这是因为name已分配给str,而且更为谦逊,name被设置为char* str所在的位置记忆,这可能不会改变。这与在循环中执行x = 3三次基本相同。

要解决此问题,您需要做两件事。

  1. 在使用之前分配每个arr[i].name实例。这可以使用malloc中的callocstdlib.h来实现。
  2. 将从stdin检索到的输入复制到arr[i].name。这可以使用strcpy
  3. 中的strncpystring.h(首选)来实现

    应用修复程序(两行代码)后,循环将如下所示:

    while (i<3)
    {
      fgets(str, 10, stdin);
    
      // Allocate 10 (most likely) bytes of memory to arr[i].name
      // And also clear out that memory space      
      arr[i].name = (char*)calloc(10, sizeof(char));
    
      // Safely copy the data (max 10 chars) from 'str' into 'arr[i].name'
      strncpy(arr[i].name, str, 10);
    
      printf("Array number %d: %s", i, arr[i].name); 
      printf("  %p\n", arr[i].name);
    
      i++;
    }
    

    应用修复后,最终结果如下:

    Array number 0: john
      0x89a0008
    Array number 1: jacob
      0x89a0018
    Array number 2: jingle
      0x89a0028
    
    1 - john
     (0x89a0008)
    2 - jacob
     (0x89a0018)
    3 - jingle
     (0x89a0028)
    

答案 3 :(得分:1)

您必须为每个结构的char *名称分配内存:

while (i<3)
  {
    fgets(str, 10, stdin);
    arr[i].name=(char *)malloc(10*sizeof(char));
    strcpy(arr[i].name,str);

    printf("Array number %d: %s", i, arr[i].name); 
    i++;
  }

请注意,通过这种方式,当您使用数组[i]完成后,您应该释放(array [i] .name),对于每个i&lt; 3以避免内存泄漏。