自動変数が破壊される?
次のようなコードを書きました。(問題がどこにあるかわからないため全部引用します)
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
struct player {
uint_fast8_t card;
uint_fast8_t rank;
};
static uint_fast8_t get_card_power(const char *card_string);
static void show_result(const struct player *player_list);
static uint_fast8_t get_max_card_power(uint_fast8_t *card_left, uint_fast8_t card);
int
main(void)
{
struct player player_list[] = {[51] = {0, 0}};
// struct player *player_list = calloc(52, sizeof(struct player));
// assert(NULL != player_list);
for (uint_fast8_t i = 0; i < 52; ++i) {
char card[2];
int scanf_count = scanf("%2s", card);
assert(1 == scanf_count);
player_list[i].card = get_card_power(card);
}
uint_fast8_t current_card = 0;
uint_fast8_t current_rank = 1;
uint_fast8_t max_card_power = 13;
uint_fast8_t card_left[] = {0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4};
while (true) {
for (uint_fast8_t i = 0; i < 52; ++i) {
if (0 < player_list[i].rank) {
continue;
}
if (current_card < player_list[i].card) {
player_list[i].rank = current_rank++;
if (52 < current_rank) {
show_result(player_list);
exit(EXIT_SUCCESS);
}
current_card = player_list[i].card;
max_card_power = get_max_card_power(card_left, current_card);
if (max_card_power <= current_card) {
current_card = 0;
}
}
}
}
return 0;
}
static uint_fast8_t
get_card_power(const char *card_string)
{
assert(NULL != card_string);
if (0 == strcmp(card_string, "10")) {
return 8;
}
static const uint_fast8_t card_power_map[] = {['3'] = 1, ['4'] = 2, ['5'] = 3, ['6'] = 4, ['7'] = 5, ['8'] = 6,
['9'] = 7, ['J'] = 9, ['Q'] = 10, ['K'] = 11, ['A'] = 12, ['2'] = 13};
assert(0 <= *card_string);
assert(*card_string <= 'Q');
uint_fast8_t ret = card_power_map[(unsigned char) *card_string];
assert(0 < ret);
return ret;
}
static void
show_result(const struct player *player_list)
{
assert(NULL != player_list);
for (uint_fast8_t i = 0; i < 52; ++i) {
assert(0 < player_list[i].rank);
int printf_count = printf("%" PRIuFAST8 "\n", player_list[i].rank);
assert(0 <= printf_count);
}
}
static uint_fast8_t
get_max_card_power(uint_fast8_t *card_left, uint_fast8_t card)
{
assert(NULL != card_left);
assert(0 < card_left[card]);
--card_left[card];
for (uint_fast8_t i = 13; 1 <= i; --i) {
if (0 < card_left[i]) {
return i;
}
}
assert(false);
return 0;
}
このコードはある「オンラインハッカソン」、具体的には https://paiza.jp/poh/hatsukoi/challenge/hatsukoi_clothes5 の回答です。回答はtextareaに貼り付けて提出し、後述しますがこの回答は正解です。
さて、問題なのは、このコードの動作です。
上記サイトでは「提出前動作確認」として1つの入力に対して出力が正しいか確認できます。
私はこのコードを書いて、手元でその入力を用いて正しい出力が得られることを確認しました。
そこでサイトで「提出前動作確認」を行ったところ、不正解となりました。
printf()
による地道なデバッグの結果、どうも同サイトに提出して動作させるとplayer_list[i].card
が意図しない値になっているようなのです。
この変数は最初のfor
ループ内で定義され、以降は参照しかされません。
かつこれを与えるget_card_power()
は内部でassert()
により、0より大きい値しか返さないことが保証されています。
しかしなぜかこの変数が0になることがあるようです。そのタイミングは不定で、デバッグ用printf()
を仕込むタイミングで変わってきます。
この現象に気が付き、コメントアウトされているように配列ではなくcalloc()
にて領域を確保するようにしたところ、正常に動作し正解となりました。
そこで質問ですが、このような動作をする原因は何が考えられるでしょうか。
サイト管理者に聞いたほうがわかるかとも思いますが、「ここで未定義の動作をする可能性がある」といった一般的な理由があれば、サイト管理者に聞くようなことではないと考えています。
なおサイト上の動作環境は
- gcc 4.8.2 (C99)
- メモリサイズ 512 MiB
とのみ示されています。
私の手元の環境は gcc 4.8.4 (ubuntu) です。