【问题标题】:Exception Being Thrown When Accessing Struct Array访问结构体数组时抛出异常
【发布时间】:2018-05-15 04:19:10
【问题描述】:

当我调用 test() 函数时,它会提示我为结构数组中的第一个玩家输入赌注。它接受我的输入。在我的for循环的第二轮中,在我的struct数组中要求第二人下注时,输入值后抛出异常:

  Exception thrown at 0x00577F81 (ucrtbased.dll) in lottery.exe: 
  0xC0000005: Access violation writing location 0x7B7CC9FC.

  If there is a handler for this exception, the program may be safely continued. 

这是我的代码:

void initNames(struct player *p, int size) {
  int i;
  for (i = 0; i < size; i++) {
      printf("Enter player %d's name...\n", (i + 1));
      scanf("%s", p[i].name);
  }
  return;
}

void initScore(struct player *p, int size) {
   int i;
   for (i = 0; i < size; i++) {
      p[i].wins = 0;
      p[i].losses = 0;
      p[i].funds = 100.00;
   }
   return;
}

void test(struct player *p, int size) {
  int i;
  for (i = 0; i < size; i++) {
    printf("%s, you have $%.2lf. Place your bet!\n", p[i].name, p[i].funds);
    scanf("%lf", p[i].bet);
  }
}

void main() {

  int size;
  struct player *playerPtr;

  printf("How many players?");
  scanf("%d", &size);
  playerPtr = malloc(sizeof(struct player)*size);

  initScore(&playerPtr, size);

  initNames(&playerPtr, size);

  test(&playerPtr, size);

  free(playerPtr);
}

感谢您的帮助或解释!

【问题讨论】:

  • initScore(&amp;playerPtr, size); --&gt; initScore(playerPtr, size);initNamestest 类似
  • p[i].bet 是正确的语法。因为pstruct player *p[i]struct player,你可以使用.bet
  • 写下来@AjayBrahmakshatriya - 你发现了主要问题。由于playerPtr 的地址在你的函数中没有改变——没有必要将它的地址传递给函数。只需传递指针。该函数将使用指针的副本,但该副本将指向(保存)与原始地址相同的地址——因此函数内的任何修改都将反映在调用者中(main() 此处)
  • 现在如果playerPtr 没有在main() 中分配,并且您希望将指针传递给函数进行分配,那么您需要将&amp;playerPtr 作为player ** 传递,这样您就可以将来自malloc 的返回分配给相同的指针,例如*playerPtr = malloc (...) 以便分配在main() 中可见。在这种情况下,如果你只是传递一个指针,你将malloc返回的地址分配给原始的副本,并且副本的生命周期只延伸到函数的生命周期——导致内存泄漏并且没有分配指针在main().
  • 如果您删除了&amp;,那么在您的函数中分配给数组的值应该没有问题(这很幸运,您选择了格式说明符 -- 忽略前导空格是出于巧合而非设计,但在 匹配 失败时将导致无限循环)唯一挥之不去的问题是您在下注时使用 p[i].bet 而不是 &amp;p[i].bet . 总是,总是检查scanfreturn,每次必须处理3个案例:(1)EOF; (2) 匹配输入失败;最后 (3) 次成功转换。

标签: c pointers struct


【解决方案1】:

在您声明函数 initNames 的方式与在 main 中调用它的方式之间存在差异。

它被声明为 -

void initNames(struct player *p, int size);

这意味着它需要一个指向struct player 的指针作为第一个参数,而int 作为第二个参数。

在 main 中,您将其称为 -

struct player *playerPtr;
initNames(&playerPtr, size);

现在playerPtr 的类型是struct player*,因此&amp;playerPtr 的类型将是struct player**。因此,参数的类型不匹配。你的编译器应该已经警告你了。始终使用-Wall 编译以查看所有警告,并使用-Werror 将警告视为错误。

解决问题 -

您不会在任何函数中修改playerPtr。所以你不需要通过struct player**。所以将调用更改为 -

initNames(playerPtr, size);

函数内部无需更改任何内容,因为函数的原型尚未更改。

函数initScoretest 也存在完全相同的问题。您可以通过将第一个参数更改为它们的调用以类似的方式修复它们。

现在也解决了这个问题,程序将不正确。 看线-

scanf("%lf", p[i].bet);

我假设bet 声明为double 类型。您现在正在将 double 传递给 scanf,其中预计会出现 double*。您需要传递投注地址。所以将行改为 -

scanf("%lf", &p[i].bet);

我已修复所有错误,工作版本为 Ideone.

我假设了struct player 的定义。

为了更加确定正在读取的值,您应该始终检查 scanf 的返回值 -

int ret = scanf("%lf", p[i].bet);

if(ret == 0) {
    // Print appropriate error message that the value entered in not a double and ask the user to retry
} else if (ret == EOF) {
    // The input stream has been closed by the user without providing appropriate input, print appropriate error message and abort. 
}
// If execution reaches this point, p[i].bet is safe to use.

【讨论】:

  • 这对我来说很有意义。我现在遇到的问题是在输入第一个玩家下注后抛出异常。它引发了与开始时发生的相同的异常。我不确定它为什么这样做,因为数据被安全地写入 initScores() 函数中。
  • 查看我的最后评论。
  • 我通过在我的 scanf 语句中使用 &p[i].bet 而不是 p[i].bet 解决了这个问题
  • @JohnBillingham 我已将修复添加到我的答案中。不过我看你已经修好了。
猜你喜欢
  • 2019-09-05
  • 1970-01-01
  • 1970-01-01
  • 2019-07-29
  • 1970-01-01
  • 1970-01-01
  • 2015-05-07
  • 2013-05-24
  • 2017-09-30
相关资源
最近更新 更多