0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

paiza問題集の「特殊な2項間漸化式」のリファクタリング、入力チェック、テストコードを考えてみた

0
Posted at

paizaラーニングレベルアップ問題集の特殊な2項間漸化式2をやってみました。


問題


提出コード

#include <stdio.h>

int main(void) {
	int x, d1, d2;
	scanf("%d %d %d", &x, &d1, &d2);
	int A[1001];
	A[0] = x;
	for (size_t i = 1; i <= 1000; i++) {
		if (i % 2 == 0) {
			A[i] = A[i - 1] + d1;
		} else {
			A[i] = A[i - 1] + d2;
		}
	}
	size_t Q;
	scanf("%zu", &Q);
	while (Q--) {
		size_t k;
		scanf("%zu", &k);
		printf("%d\n", A[--k]);
	}
	return 0;
}

この提出コードをリファクタリングしていきたいと思います。


ロジック部

提出コードを

  • 入力部(入力チェック)
  • ロジック部(処理本体)

に分けたいと思います。


プロトタイプ

主に

  • 数列クラスのコンストラクタ
  • 数列の要素を取得する
  • 数列インスタンスの解放

の関数を作成します。

Sequence.h
#ifndef SEQUENCE_H_
#define SEQUENCE_H_

#include <stddef.h>
#include <stdbool.h>

typedef struct __Sequence _Sequence;

typedef struct Sequence {
	_Sequence *_seq;
	bool (*get)(const struct Sequence*, size_t, int*);
} Sequence;

Sequence* newSequence(int, int, int);

void freeSequence(Sequence**);

#endif /* SEQUENCE_H_ */

テストコード

入出力例を元にテストコードを書きます。
また、

  • 0を入力した時の挙動
  • 数列の1001番目の要素を取得しようとした時の挙動

も確認します。

数列の添字については、0を入力した時に1番目の要素を取得する仕様にします。

SequenceTest.c
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "minunit.h"
#include "Sequence.h"

int tests_run = 0;

static char* message_d(int expected, int actual) {
	static char msg[72];
	snprintf(msg, sizeof(msg), "Error: expected: <%d> but was: <%d>", expected, actual);
	return msg;
}

static char* test_get(int x, int d1, int d2, size_t k, int expected) {
	Sequence *seq = newSequence(x, d1, d2);
	int actual;
	bool result = seq->get(seq, k, &actual);
	freeSequence(&seq);
	mu_assert("Error: expected: <true> but was: <false>", result);
	mu_assert(message_d(expected, actual), actual == expected);
	mu_assert(message_d(0, errno), errno == 0);
	return 0;
}

static char* test_get_out_of_range(int x, int d1, int d2, size_t k) {
	Sequence *seq = newSequence(x, d1, d2);
	int actual;
	bool result = seq->get(seq, k, &actual);
	freeSequence(&seq);
	mu_assert("Error: expected: <false> but was: <true>", !result);
	mu_assert(message_d(ERANGE, errno), errno == ERANGE);
	return 0;
}

static char* test_get_0() {return test_get(0, 0, 0, 0, 0);}
static char* test_get_1() {return test_get(3, 7, -4, 0, 3);}
static char* test_get_2() {return test_get(3, 7, -4, 1, -1);}
static char* test_get_3() {return test_get(3, 7, -4, 2, 6);}
static char* test_get_4() {return test_get(3, 7, -4, 3, 2);}
static char* test_get_5() {return test_get(1000, 1000, -1000, 999, 0);}
static char* test_get_6() {return test_get_out_of_range(0, 0, 0, 1000);}

static char* all_tests() {
	mu_run_test(test_get_0);
	mu_run_test(test_get_1);
	mu_run_test(test_get_2);
	mu_run_test(test_get_3);
	mu_run_test(test_get_4);
	mu_run_test(test_get_5);
	mu_run_test(test_get_6);
	return 0;
}

int main() {
	char *result = all_tests();
	if (result != 0) {
		fprintf(stderr, "%s\n", result);
		fprintf(stderr, "Tests run: %d\n", tests_run);
	} else {
		fprintf(stdout, "ALL TESTS PASSED\n");
		fprintf(stdout, "Tests run: %d\n", tests_run);
	}
	return result != 0;
}

コンストラクタで要素数1000の数列を予め生成しておく仕様にした場合、一般項が

a_i=x+\left\lfloor\frac{i-1}{2}\right\rfloor d_1+\left\lfloor\frac{i}{2}\right\rfloor d_2

と書き表されることを利用すれば、以下の様なテストコードを書くこともできます。

test_constructor
static char* message_zu(size_t expected, size_t actual) {
	static char msg[72];
	snprintf(msg, sizeof(msg), "Error: expected: <%zu> but was: <%zu>", expected, actual);
	return msg;
}

static char* test_constructor(int x, int d1, int d2) {
	Sequence *seq = newSequence(x, d1, d2);
	mu_assert("Error: expected: not <null> but was: <null>", seq);
	void *p = (void*) seq->_seq;
	size_t size = *(size_t*)p;
	p += sizeof(size_t);
	int actual[size];
	memcpy(actual, p, sizeof(actual));
	freeSequence(&seq);
	mu_assert("Error: expected: <null> but was: not <null>", !seq);
	mu_assert(message_zu(1000, size), size == 1000);
	for (size_t i = 0; i < size; i++) {
		int expected = x + (int) (i / 2) * d1 + (int) ((i + 1) / 2) * d2;
		mu_assert(message_d(expected, actual[i]), actual[i] == expected);
	}
	return 0;
}

static char* test_constructor_0() {return test_constructor(0, 0 ,0);}
static char* test_constructor_1() {return test_constructor(3, 7, -4);}

ソースコード

処理本体を実装していきます。

Sequence.c
#include <stdlib.h>
#include <errno.h>

#include "Sequence.h"

const size_t K = 1000;

struct __Sequence {
	size_t size;
	int sequence[];
};

static bool Sequence_get(const struct Sequence *self, size_t k, int *a) {
	errno = 0;
	if (k >= K) {
		errno = ERANGE;
		return false;
	}
	*a = self->_seq->sequence[k];
	return true;
}

Sequence* newSequence(int x, int d1, int d2) {
	errno = 0;
	Sequence *seq = malloc(sizeof(Sequence));
	if (!seq)
		return NULL;
	seq->_seq = malloc(sizeof(_Sequence) + sizeof(seq->_seq->sequence[0]) * K);
	if (!seq->_seq) {
		free(seq);
		return NULL;
	}
	seq->_seq->size = K;
	seq->_seq->sequence[0] = x;
	for (size_t i = 1; i < K; i++) {
		switch (i % 2) {
		case 0:
			seq->_seq->sequence[i] = seq->_seq->sequence[i - 1] + d1;
			break;
		case 1:
			seq->_seq->sequence[i] = seq->_seq->sequence[i - 1] + d2;
			break;
		default:
			free(seq->_seq);
			seq->_seq = NULL;
			free(seq);
			seq = NULL;
			return NULL;
		}
	}

	seq->get = Sequence_get;
	return seq;
}

void freeSequence(Sequence** self) {
	free((*self)->_seq);
	(*self)->_seq = NULL;
	free(*self);
	*self = NULL;
}

以下、Windows環境におけるテスト実行コマンドです。

gcc -Wall -Wextra -Werror -std=c99 SequenceTest.c Sequence.c -o SequenceTest
.\SequenceTest

入力チェック

入力値が、制約条件

  • $-1000\le x\le 1000$
  • $-1000\le d_1\le 1000$
  • $-1000\le d_2\le 1000$
  • $1\le Q\le 1000$
  • $1\le k_i\le 1000$ ($1\le Q\le 1000$)

を満たしているかどうかをチェックします。
(そもそも整数値が入力されているかどうかも含めて)


プロトタイプ
  • 1行目の入力
  • 2行目の入力
  • 3行目以降の入力

に分けます。その際、以下のutilを使うことにします。


util.h
util.h
#ifndef UTIL_H_
#define UTIL_H_
#include <stdint.h>
#include <stdbool.h>

char* chomp(char*);

bool parse_ulong(const char*, size_t*);

bool parse_int(const char*, int*);

#endif /* UTIL_H_ */

util.c
util.c
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <inttypes.h>
#include <errno.h>
#include "util.h"

char* chomp(char *s) {
	if (!s)
		return NULL;
	size_t n = strlen(s);
	while (n--)
		if (isspace(s[n]))
			s[n] = '\0';
		else
			break;
	return s;
}

static bool is_positive_number(const char *s) {
	if (!(s && *s))
		return false;
	if (*s == '0')
		return s[1] == 0;
	for (const char *c = s; *c; c++)
		if (!isdigit(*c))
			return false;
	return true;
}

static bool is_number(const char *s) {
	if (!(s && *s))
		return false;
	if (*s == '-')
		return s[1] != 0 && s[1] != '0' && is_positive_number(s + 1);
	else
		return is_positive_number(s);
}

static bool parse_unsigned(const char *str, uintmax_t *u, uintmax_t max) {
	errno = 0;
	if (!is_positive_number(str)) {
		errno = EINVAL;
		return false;
	}
	char *s = NULL;
	*u = strtoumax(str, &s, 10);
	if (errno) {
		return false;
	} else if (s && *s) {
		errno = EINVAL;
		return false;
	} else if (*u > max) {
		errno = ERANGE;
		return false;
	}
	return true;
}

static bool parse_signed(const char *str, intmax_t *i, intmax_t max) {
	errno = 0;
	if (!is_number(str)) {
		errno = EINVAL;
		return false;
	}
	char *s = NULL;
	*i = strtoimax(str, &s, 10);
	if (errno) {
		return false;
	} else if (s && *s) {
		errno = EINVAL;
		return false;
	} else if (*i < ~max || max < *i) {
		errno = ERANGE;
		return false;
	}
	return true;
}

bool parse_ulong(const char *str, size_t *n) {
	uintmax_t m = 0;
	if (!parse_unsigned(str, &m, SIZE_MAX))
		return false;
	*n = (size_t) m;
	return true;
}

bool parse_int(const char *str, int *n) {
	intmax_t m = 0;
	if (!parse_signed(str, &m, INT_MAX))
		return false;
	*n = (int) m;
	return true;
}

input.h
#ifndef INPUT_H_
#define INPUT_H_

#include <stdbool.h>

bool read_init(const char*, int*, int*, int*);

bool read_query_num(const char*, size_t*);

bool read_index(const char*, size_t*);

#endif /* INPUT_H_ */

テストコード
  • null
  • 空文字
  • 最小値-1
  • 最小値
  • 最大値
  • 最大値+1

を特に意識してテストケースを起こし、テストコードを記述します。

以下のテストコードでは、数字以外の文字を入力したテストケースを省略しております。

inputTest.c
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "minunit.h"
#include "input.h"

int tests_run = 0;

static char* message_zu(size_t expected, size_t actual) {
	static char msg[72];
	snprintf(msg, sizeof(msg), "Error: expected: <%zu> but was: <%zu>", expected, actual);
	return msg;
}

static char* message_d(int expected, int actual) {
	static char msg[72];
	snprintf(msg, sizeof(msg), "Error: expected: <%d> but was: <%d>", expected, actual);
	return msg;
}

static char* test_read_init(const char *str, int ex, int ed1, int ed2) {
	int x, d1, d2;
	mu_assert("Error: expected: <true> but was: <false>", read_init(str, &x, &d1, &d2));
	mu_assert(message_d(ex, x), x == ex);
	mu_assert(message_d(ed1, d1), d1 == ed1);
	mu_assert(message_d(ed2, d2), d2 == ed2);
	mu_assert(message_d(0, errno), errno == 0);
	return 0;
}

static char* test_read_init_invalid(const char *str) {
	int x, d1, d2;
	mu_assert("Error: expected: <false> but was: <true>", !read_init(str, &x, &d1, &d2));
	mu_assert(message_d(EINVAL, errno), errno == EINVAL);
	return 0;
}

static char* test_read_init_out_of_range(const char *str) {
	int x, d1, d2;
	mu_assert("Error: expected: <false> but was: <true>", !read_init(str, &x, &d1, &d2));
	mu_assert(message_d(ERANGE, errno), errno == ERANGE);
	return 0;
}

static char* test_read_init_0() {return test_read_init_invalid(NULL);}
static char* test_read_init_1() {return test_read_init_invalid("");}
static char* test_read_init_2() {return test_read_init_invalid("0");}
static char* test_read_init_3() {return test_read_init_invalid("0 0");}
static char* test_read_init_4() {return test_read_init_invalid("0 0 0 0");}
static char* test_read_init_5() {return test_read_init("0 0 0", 0, 0, 0);}
static char* test_read_init_6() {return test_read_init_out_of_range("-1001 -1000 -1000");}
static char* test_read_init_7() {return test_read_init_out_of_range("-1000 -1001 -1000");}
static char* test_read_init_8() {return test_read_init_out_of_range("-1000 -1000 -1001");}
static char* test_read_init_9() {return test_read_init("-1000 -1000 -1000", -1000, -1000, -1000);}
static char* test_read_init_10() {return test_read_init("1000 1000 1000", 1000, 1000, 1000);}
static char* test_read_init_11() {return test_read_init_out_of_range("1001 1000 1000");}
static char* test_read_init_12() {return test_read_init_out_of_range("1000 1001 1000");}
static char* test_read_init_13() {return test_read_init_out_of_range("1000 1000 1001");}

static char* test_read_query_num(const char *str, size_t expected) {
	size_t actual;
	mu_assert("Error: expected: <true> but was: <false>", read_query_num(str, &actual));
	mu_assert(message_zu(expected, actual), actual == expected);
	mu_assert(message_d(0, errno), errno == 0);
	return 0;
}

static char* test_read_query_num_invalid(const char *str) {
	size_t actual;
	mu_assert("Error: expected: <false> but was: <true>", !read_query_num(str, &actual));
	mu_assert(message_d(EINVAL, errno), errno == EINVAL);
	return 0;
}

static char* test_read_query_num_out_of_range(const char *str) {
	size_t actual;
	mu_assert("Error: expected: <false> but was: <true>", !read_query_num(str, &actual));
	mu_assert(message_d(ERANGE, errno), errno == ERANGE);
	return 0;
}

static char* test_read_query_num_0() {return test_read_query_num_invalid(NULL);}
static char* test_read_query_num_1() {return test_read_query_num_invalid("");}
static char* test_read_query_num_2() {return test_read_query_num_out_of_range("0");}
static char* test_read_query_num_3() {return test_read_query_num("1", 1);}
static char* test_read_query_num_4() {return test_read_query_num("1000", 1000);}
static char* test_read_query_num_5() {return test_read_query_num_out_of_range("1001");}

static char* test_read_index(const char *str, size_t expected) {
	size_t actual;
	mu_assert("Error: expected: <true> but was: <false>", read_index(str, &actual));
	mu_assert(message_zu(expected, actual), actual == expected);
	mu_assert(message_d(0, errno), errno == 0);
	return 0;
}

static char* test_read_index_invalid(const char *str) {
	size_t actual;
	mu_assert("Error: expected: <false> but was: <true>", !read_index(str, &actual));
	mu_assert(message_d(EINVAL, errno), errno == EINVAL);
	return 0;
}

static char* test_read_index_out_of_range(const char *str) {
	size_t actual;
	mu_assert("Error: expected: <false> but was: <true>", !read_index(str, &actual));
	mu_assert(message_d(ERANGE, errno), errno == ERANGE);
	return 0;
}

static char* test_read_index_0() {return test_read_index_invalid(NULL);}
static char* test_read_index_1() {return test_read_index_invalid("");}
static char* test_read_index_2() {return test_read_index_out_of_range("0");}
static char* test_read_index_3() {return test_read_index("1", 1);}
static char* test_read_index_4() {return test_read_index("1000", 1000);}
static char* test_read_index_5() {return test_read_index_out_of_range("1001");}

static char* all_tests() {
	mu_run_test(test_read_init_0);
	mu_run_test(test_read_init_1);
	mu_run_test(test_read_init_2);
	mu_run_test(test_read_init_3);
	mu_run_test(test_read_init_4);
	mu_run_test(test_read_init_5);
	mu_run_test(test_read_init_6);
	mu_run_test(test_read_init_7);
	mu_run_test(test_read_init_8);
	mu_run_test(test_read_init_9);
	mu_run_test(test_read_init_10);
	mu_run_test(test_read_init_11);
	mu_run_test(test_read_init_12);
	mu_run_test(test_read_init_13);
	mu_run_test(test_read_query_num_0);
	mu_run_test(test_read_query_num_1);
	mu_run_test(test_read_query_num_2);
	mu_run_test(test_read_query_num_3);
	mu_run_test(test_read_query_num_4);
	mu_run_test(test_read_query_num_5);
	mu_run_test(test_read_index_0);
	mu_run_test(test_read_index_1);
	mu_run_test(test_read_index_2);
	mu_run_test(test_read_index_3);
	mu_run_test(test_read_index_4);
	mu_run_test(test_read_index_5);
	return 0;
}

int main() {
	char *result = all_tests();
	if (result != 0) {
		fprintf(stderr, "%s\n", result);
		fprintf(stderr, "Tests run: %d\n", tests_run);
	} else {
		fprintf(stdout, "ALL TESTS PASSED\n");
		fprintf(stdout, "Tests run: %d\n", tests_run);
	}
	return result != 0;
}

ソースコード

入力チェック処理を実装していきます。
正しく実装すれば上述のテストを全てパスするはずです。

input.c
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "input.h"
#include "util.h"

bool read_init(const char *str, int *x, int *d1, int *d2) {
	errno = 0;
	if (!(str && *str)) {
		errno = EINVAL;
		return false;
	}
	char s[] = "-1000 -1000 -1000\n";
	if (strlen(str) > strlen(s)) {
		errno = EINVAL;
		return false;
	}
	if (!parse_int(strtok(chomp(strncpy(s, str, sizeof(s) - 1)), " "), x)) {
		return false;
	} else if (*x < -1000 || 1000 < *x) {
		errno = ERANGE;
		return false;
	}
	if (!parse_int(strtok(NULL, " "), d1)) {
		return false;
	} else if (*d1 < -1000 || 1000 < *d1) {
		errno = ERANGE;
		return false;
	}
	if (!parse_int(strtok(NULL, " "), d2)) {
		return false;
	} else if (*d2 < -1000 || 1000 < *d2) {
		errno = ERANGE;
		return false;
	}
	if (strtok(NULL, " ")) {
		errno = EINVAL;
		return false;
	}
	return true;
}

bool read_query_num(const char *str, size_t *q) {
	errno = 0;
	if (!(str && *str)) {
		errno = EINVAL;
		return false;
	}
	char s[] = "1000\n";
	if (strlen(str) > strlen(s)) {
		errno = EINVAL;
		return false;
	}
	if (!parse_ulong(chomp(strncpy(s, str, sizeof(s) - 1)), q)) {
		return false;
	} else if (*q < 1 || 1000 < *q) {
		errno = ERANGE;
		return false;
	}
	return true;
}

bool read_index(const char *str, size_t *k) {
	errno = 0;
	if (!(str && *str)) {
		errno = EINVAL;
		return false;
	}
	char s[] = "1000\n";
	if (strlen(str) > strlen(s)) {
		errno = EINVAL;
		return false;
	}
	if (!parse_ulong(chomp(strncpy(s, str, sizeof(s) - 1)), k)) {
		return false;
	} else if (*k < 1 || 1000 < *k) {
		errno = ERANGE;
		return false;
	}
	return true;
}

Windows環境下でのテスト実行コマンドは以下の様になります。

gcc -Wall -Wextra -Werror -std=c99 inputTest.c input.c util.c -o inputTest
.\inputTest

エントリポイント

リファクタリングしたコードを使って、冒頭の提出コードを書き換えていきます。


テストコード

最早「単体」テストではありませんが、後述のmain.cをコンパイルして実行する作りになっています。

test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include "minunit.h"
#define SOURCE "main"
#define IN_FILE "in.txt"
#define OUT_FILE "out.txt"
#define ERR_FILE "err.txt"

int tests_run = 0;
char result[1024][16];

static char* message_zu(size_t expected, size_t actual) {
	static char msg[72];
	snprintf(msg, sizeof(msg), "Error: expected: <%zu> but was: <%zu>", expected, actual);
	return msg;
}

static char* message_s(const char *expected, const char *actual) {
	static char msg[256];
	snprintf(msg, sizeof(msg), "Error: expected: <%s> but was: <%s>", expected, actual);
	return msg;
}

static char* message_d(int expected, int actual) {
	static char msg[72];
	snprintf(msg, sizeof(msg), "Error: expected: <%d> but was: <%d>", expected, actual);
	return msg;
}

static size_t test(size_t n, const char *lines[]) {
	FILE *file;
	if (!(file = fopen(IN_FILE, "w"))) {
		perror(NULL);
		exit(errno);
	}
	for (size_t i = 0; i < n; i++) {
		fputs(lines[i], file);
		fputc('\n', file);
	}
	fclose(file);
	clock_t clockt = clock();
	if (!!(errno = system(".\\" SOURCE " <" IN_FILE " 1>" OUT_FILE " 2>" ERR_FILE))) {
		perror(NULL);
		exit(errno);
	}
	fprintf(stderr, "#%d %f sec\n", tests_run, (float) (clock() - clockt) / CLOCKS_PER_SEC);
	fflush(stderr);
	if (!(file = fopen(OUT_FILE, "r"))) {
		perror(NULL);
		exit(errno);
	}
	size_t m = 0;
	while (fgets(result[m], sizeof(result[m]), file)) m++;
	fclose(file);
	return m;
}

static int test_err(size_t n, const char *lines[]) {
	FILE *file;
	if (!(file = fopen(IN_FILE, "w"))) {
		perror(NULL);
		exit(errno);
	}
	for (size_t i = 0; i < n; i++) {
		fputs(lines[i], file);
		fputc('\n', file);
	}
	fclose(file);
	clock_t clockt = clock();
	int e = system(".\\" SOURCE " <" IN_FILE " 1>" OUT_FILE " 2>" ERR_FILE);
	fprintf(stderr, "#%d %f sec\n", tests_run, (float) (clock() - clockt) / CLOCKS_PER_SEC);
	fflush(stderr);
	return e;
}

static char* test0() {
	int e = test_err(0, NULL);
	mu_assert(message_d(EXIT_FAILURE, e), e == EXIT_FAILURE);
	return 0;
}

static char* test1() {
	const char *lines[] = { "0 0 0", "1", "1" };
	size_t n = test(sizeof(lines) / sizeof(lines[0]), lines);
	mu_assert(message_zu(1, n), n == 1);
	mu_assert(message_s("0\n", result[0]), !strcmp(result[0], "0\n"));
	return 0;
}

static char* test2() {
	const char *lines[] = {
		"0", "0 0", "0 0 0 0", "1001 1000 1000", "1000 1001 1000", "1000 1000 1001", "-1001 -1000 -1000", "-1000 -1001 -1000", "-1000 -1000 -1001", "0 0 0",
		"0", "1001", "1",
		"0", "1001", "1"
	};
	size_t n = test(sizeof(lines) / sizeof(lines[0]), lines);
	mu_assert(message_zu(1, n), n == 1);
	mu_assert(message_s("0\n", result[0]), !strcmp(result[0], "0\n"));
	return 0;
}

static char* test3() {
	char *lines[1002] = { "1000 1000 -1000", "1000" };
	char str[1000][8] = { };
	for (size_t i = 0; i < 1000; i++) {
		snprintf(str[i], sizeof(str[i]), "%zu", i + 1);
		lines[i + 2] = str[i];
	}
	size_t n = test(sizeof(lines) / sizeof(lines[0]), (const char**) lines);
	mu_assert(message_zu(1000, n), n == 1000);
	for (size_t i = 0; i < 1000; i++) {
		char *s = i % 2 ? "0\n" : "1000\n";
		mu_assert(message_s(s, result[i]), !strcmp(result[i], s));
	}
	return 0;
}

static char* all_tests() {
	mu_run_test(test0);
	mu_run_test(test1);
	mu_run_test(test2);
	mu_run_test(test3);
	return 0;
}

int main() {
	if (!!(errno = system("gcc -Wall -Wextra -Werror -DNDEBUG -std=c99 " SOURCE ".c Sequence.c input.c util.c -o " SOURCE " 1>" SOURCE ".txt 2>&1"))) {
		perror(NULL);
		FILE *file = fopen(SOURCE ".txt", "r");
		if (file) {
			int c = 0;
			while ((c = fgetc(file)) != EOF)
				putchar(c);
			fclose(file);
		}
		exit(errno);
	}
	char *result = all_tests();
	if (result != 0) {
		fprintf(stderr, "%s\n", result);
		fprintf(stderr, "Tests run: %d\n", tests_run);
	} else {
		fprintf(stdout, "ALL TESTS PASSED\n");
		fprintf(stdout, "Tests run: %d\n", tests_run);
	}
	remove(ERR_FILE);
	remove(OUT_FILE);
	remove(IN_FILE);
	remove(SOURCE ".exe");
	remove(SOURCE ".txt");
	if (errno) {
		return errno;
	} else {
		return result != 0;
	}
}

ソースコード

入力に不備があった場合はシステムエラーメッセージを吐き出して再入力させるような作りにしてあります。

読者諸賢におかれましては、perror(NULL)NULLの部分を適切なエラーメッセージ文字列に変更いただきたく。

main.c
#include <stdio.h>
#include <stdlib.h>

#include "Sequence.h"
#ifdef NDEBUG
#include <string.h>
#include <errno.h>
#include "input.h"
const char END[] = { EOF, '\n', 0 };
#else
#include <time.h>
#define PRINT(format, ...)                      \
	do {                                        \
		fprintf(stderr, format, ##__VA_ARGS__); \
		fflush(stderr);                         \
	} while (0)
#endif

int main(void) {
#ifndef NDEBUG
	clock_t clockt = clock();
#endif
	int x, d1, d2;
#ifdef NDEBUG
	do {
		errno = 0;
		char s[] = "-1000 -1000 -1000\n";
		if (!fgets(s, sizeof(s), stdin)) {
			return EXIT_FAILURE;
		}
		if (!strchr(s, '\n')) {
			for (int c = getchar(); !strchr(END, c); c = getchar());
			errno = EINVAL;
		} else if (read_init(s, &x, &d1, &d2)) {
			break;
		}
		perror(NULL);
	} while (true);
#else
	x = -1000;
	d1 = -1000;
	d2 = -1000;
#endif
	Sequence *seq = newSequence(x, d1, d2);
	size_t Q;
#ifdef NDEBUG
	do {
		errno = 0;
		char s[] = "1000\n";
		if (!fgets(s, sizeof(s), stdin)) {
			return EXIT_FAILURE;
		}
		if (!strchr(s, '\n')) {
			for (int c = getchar(); !strchr(END, c); c = getchar());
			errno = EINVAL;
		} else if (read_query_num(s, &Q)) {
			break;
		}
		perror(NULL);
	} while (true);
#else
	Q = 1000;
#endif
	while (Q--) {
		size_t k;
#ifdef NDEBUG
		do {
			errno = 0;
			char s[] = "1000\n";
			if (!fgets(s, sizeof(s), stdin)) {
				return EXIT_FAILURE;
			}
			if (!strchr(s, '\n')) {
				for (int c = getchar(); !strchr(END, c); c = getchar());
				errno = EINVAL;
			} else if (read_index(s, &k)) {
				break;
			}
			perror(NULL);
		} while (true);
#else
		k = 1000 - Q;
#endif
		int a;
		if (!seq->get(seq, --k, &a)) {
			freeSequence(&seq);
			return EXIT_FAILURE;
		}
		printf("%d\n", a);
	}
	freeSequence(&seq);
#ifndef NDEBUG
	PRINT("%f sec.\n", (float) (clock() - clockt) / CLOCKS_PER_SEC);
#endif
	return EXIT_SUCCESS;
}

以下、Windows環境下での実行コマンドです。

gcc -Wall -Wextra -Werror -DNDEBUG -std=c99 main.c Sequence.c input.c util.c -o main
.\main

二行目に不用意に大きな数字を入力すると終わらなくなってしまいます💦

テストを実行する場合は以下のコマンドです。

gcc -Wall -Wextra -Werror -std=c99 test.c -o test
.\test
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?