我正在学习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()之前刷新缓冲区,或者无法弄清楚发生了什么。有谁知道这是关于什么的?
答案 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;
}
作为最后一点,您应该避免使用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
指向相同的字符串,name
和str
只会打印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;
}
这会产生以下输出(给定john
,jacob
和jingle
作为输入):
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
三次基本相同。
要解决此问题,您需要做两件事。
arr[i].name
实例。这可以使用malloc
中的calloc
或stdlib.h
来实现。stdin
检索到的输入复制到arr[i].name
。这可以使用strcpy
strncpy
或string.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以避免内存泄漏。