2

私自身は常に関数内の値をチェックしていましたが、大学では先生から関数外の値を常にチェックするように言われています。同じチェック値で関数を数回呼び出す場合は、関数の外側で一度だけ値をチェックすることをお勧めします。ただし、重要な値を確認するのを忘れると、プログラムでセグメンテーション違反などのエラーが発生する可能性があります。

作業中のコードを貼り付けました。学校では、関数の外側の値をチェックする必要がありますが、この場合、これはクレイジーで時間の無駄だと思います。main 関数はコードの最後にあります。malloc の戻り値をチェックするとき (関数の外側の init_unsecure と関数の内側の init_secure)、または削除する前に要素が存在するかどうかをチェックするとき (重複する関数の外側の remove_unsecure) で違いがわかります。存在する関数内の while ループおよび関数内の remove_secure)。

どう思いますか?関数内の値をチェックしているように見えませんか?

#include <stdlib.h>

typedef struct  strens
{
  int       v[100];
  int       n;
}       StrEns, *Ens;

// init struct pointer (need to check if return value is not NULL)
Ens init_unsecure()
{
  Ens   e;

  if ((e = malloc(sizeof(StrEns))) != NULL)
    e->n = 0;
  return e;
}

// init struct pointer and check if malloc is not NULL
Ens init_secure()
{
  Ens   e;

  if ((e = malloc(sizeof(StrEns))) == NULL)
    exit(EXIT_FAILURE);
  e->n = 0;
  return e;
}

// insert element
Ens insert(Ens e, int x)
{
  e->v[e->n] = x;
  ++e->n;
  return e;
}

// return if element exists or not
int exists(Ens e, int x)
{
  int i = 0;
  while (i < e->n && e->v[i] != x)
    ++i;
  return (i != e->n);
}

// remove element (need to check if element exists before)
Ens remove_unsecure(Ens e, int x)
{
  --e->n;
  int i = 0;
  while (e->v[i] != x)
    ++i;
  e->v[i] = e->v[e->n];
}

// remove element if exists
Ens remove_secure(Ens e, int x)
{
  --e->n;
  int i = 0;
  while (i < e->n && e->v[i] != x)
    ++i;
  e->v[i] = e->v[e->n];
}

// comparing init_unsecure vs init_secure && remove_unsecure vs remove_secure
int main()
{
  Ens   e1, e2, e3;

  if ((e1 = init_unsecure()) == NULL)
    return EXIT_FAILURE;
  if ((e2 = init_unsecure()) == NULL)
    return EXIT_FAILURE;
  if ((e3 = init_unsecure()) == NULL)
    return EXIT_FAILURE;

  e1 = init_secure();
  e2 = init_secure();
  e3 = init_secure();

  if (exists(e1, 42))
    remove_unsecure(e1, 42);
  if (exists(e2, 42))
    remove_unsecure(e2, 42);
  if (exists(e3, 42))
    remove_unsecure(e3, 42);

  remove_secure(e1, 42);
  remove_secure(e2, 42);
  remove_secure(e3, 42);

  return EXIT_SUCCESS;
}
4

1 に答える 1

1

C プログラミングでは、関数の呼び出し元は自分が何をしているのかを知っているので、有効な引数を提供する必要があるという考え方が (今でも) 広く行き渡っています。そうしないと、未定義の動作の理由になることが広く受け入れられています。

たとえば、free渡しているポインターが以前に返されたかどうかを検証する必要はありませんmalloc。これは、パフォーマンスに影響を与えるためです。関数を呼び出すコードが正しく、各引数が期待される範囲内で提供されている限り、関数内の防御チェックがなくてもすべて問題ありません。

ただし、コード全体でアサーションを使用して、プログラマーができるだけ早く異常を検出できるようにすることをお勧めします。

于 2013-10-27T18:20:50.150 に答える