c - 在函数内输入字符串数组
问题描述
我正在尝试使用 myFgets 输入此字符串数组,但它告诉我这样做时出现访问冲突错误。
我在这里做错了什么?
#include <stdio.h>
#include <string.h>
void myFgets(char str[], int size)
{
fgets(str, size, stdin);
str[strcspn(str, "\n")] = 0;
}
void enterFriends(char** friendsArr)
{
int i = 0;
for (i = 0; i < 3; i++)
{
printf("enter friend %d: ", i + 1);
myFgets(friendsArr[i], 10);
}
}
int main(void)
{
char friendsArr[3][10] = { ' ' };
int i = 0;
enterFriends(friendsArr);
for (i = 0; i < 3; i++)
{
puts(friendsArr[i]);
}
return 0;
}
解决方案
继续评论,您的访问冲突是因为friendsArr
类型char (*)[10]
与char **
. (第一个是指向数组的指针,第二个是指向指针的指针)类型控制指针运算。p
(例如和之间的偏移量p + 1
)。您的 3 x 10 2D 数组是1D 数组的数组。
由于数组被转换为指向其第一个元素1的指针,friendsArr
因此被转换为指向第一个数组的指针(例如 10 个字符的数组),这就是为什么访问时的正式类型是char (*)[10]
(指向 10 个字符的数组的指针)。因此,当您访问下一个指针时,偏移量是到下一个一维数组的开头(10 个字符/字节的偏移量)。
当您尝试将数组传递为char **
时,偏移量不再是 10 个字符,而是sizeof (a_pointer)
. 因此偏移量将是 x86 上的 4 字节或 x86_64 上的 8 字节。那么发生的情况是您将第一个字符串读入第一个 1D 数组,前进 8 个字节(或 4 个字节),然后尝试将下一个字符串读入相同的 1D 数组,从第 8个(或第 4个)元素开始破坏输入的数组。
这带来了您缺少的下一个关键点。您必须始终检查用于确定输入是成功还是失败的每个输入函数的返回。用户按下ctrl + d(或ctrl + z在windows上)生成手动EOF
取消输入是100%有效的。通过检查您的退货,fgets()
您可以妥善处理该案件。此外,您编写的任何接受输入的函数都必须具有有意义的返回类型,可以将输入的成功或失败传达回调用者。
在中,成功(或失败)myFgets()
返回指向字符串的指针将判断该输入是成功还是失败。由于也依赖于该返回,它也需要一个有意义的返回类型来传达有多少输入成功。这带来了额外的一点,您不能简单地在 中循环固定次数,而是需要以成功读取的朋友数量少于您的最大值为条件来调整您的循环。如果用户取消输入,您仍然希望能够返回成功输入的数字。NULL
enterFriends()
enterFriends()
enterFriends()
(注意:您可以调整测试以要求进行所有输入,重新提示取消输入 - 由您决定)
不要在代码中使用MagicNumbers(例如3
, 10
),而是:
#define NFRIENDS 3 /* if you need a constant, #define one (or more) */
#define MAXC 1024
(注意:规则:不要跳过缓冲区大小,10
是用户输入的缩写。除非您在物理内存有限的嵌入式系统上,否则 256 字节到 2048 字节的缓冲区就可以了。例如,GCC定义BUFSIZ
为8192
, VS 定义为512
)
这也会影响您的代码是否将 VLA(可变长度数组)作为参数与您在main()
. 虽然在这种情况下差异主要在于语义,但请注意,从 C11 开始,编译器对 VLA 的支持是可选的。
因此,总而言之,您可以稍微重写代码,调整函数参数和返回以避免 VLA,并提供最小的必要返回来传达输入的成功或失败以及阅读了多少朋友,如下所示:
#include <stdio.h>
#include <string.h>
#define NFRIENDS 3 /* if you need a constant, #define one (or more) */
#define MAXC 1024
/* every function that takes input must provide a meaningful return
* that can indicate success or failure to the caller.
* returns pointer to string on success, NULL otherwise.
*/
char *myFgets (char *str, int size)
{
if (!fgets (str, size, stdin)) { /* validate EVERY input */
puts ("(user canceled input)");
return NULL;
}
str[strcspn(str, "\n")] = 0; /* good job using strcspn() */
return str;
}
/* do not use MagicNumbers, pass a parameter for number of friends */
int enterFriends (char (*friendsArr)[MAXC], size_t size, int nfriends)
{
int i = 0;
while (i < nfriends) { /* can't use a fixed number */
printf ("enter friend %d: ", i + 1);
if (!myFgets (friendsArr[i], size)) /* if user cancels, break loop */
break;
i += 1; /* only increment after good read */
}
return i; /* return number of friends read */
}
int main(void)
{
char friendsArr[NFRIENDS][MAXC] = {""}; /* initialize all elements zero */
int i = 0,
friends = 0;
friends = enterFriends (friendsArr, MAXC, NFRIENDS);
for (i = 0; i < friends; i++) {
puts (friendsArr[i]);
}
}
示例使用/输出
$ ./bin/friendsarray
enter friend 1: Mickey Mouse
enter friend 2: Minnie Mouse
enter friend 3: Goofy (the dog)
Mickey Mouse
Minnie Mouse
Goofy (the dog)
或故意取消最后一个输入:
$ ./bin/friendsarray
enter friend 1: Mickey Mouse
enter friend 2: Minnie Mouse
enter friend 3: (user canceled input)
Mickey Mouse
Minnie Mouse
在取消发生之前正确保留了两个良好的输入。
如果您还有其他问题,请仔细查看并告诉我。
脚注: