Я пишу клон ncurses Scrabble на C.

Я написал код для представления мешка, который является источником перемешанных символов, из которого вы можете вытаскивать персонажей, так же, как вытаскивание случайных плиток из мешка в реальной жизни.

Мой код работает в OS X, но когда я пытаюсь скомпилировать на машине Debian, он дает мне такое грубое сообщение:

~/nscrabble $ ./scrabble
Segmentation fault

Я использовал gdb, чтобы попытаться диагностировать проблему. Похоже, что segfault был вызван оператором return:

(gdb) run
Starting program: /home/jay/nscrabble/scrabble 

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400ca8 in bag () at bag.c:53
53          return b;
(gdb) 

Вот так. Вот код (bag.c и bag.h) Вот bag.h -

#ifndef BAG_H_INCLUDED
#define BAG_H_INCLUDED

/*
 * This array states how many times each
 * letter should show up in the bag.
 *
 * distributions[0]     Number of As
 * distributions[1]     Number of Bs
 * ...
 * distributions[25]    Number of Zs
 * distributions[26]    Number of blank tiles
 */
const short distributions[27];

/*
 * The bag is the repository of tiles in Scrabble.
 * I didn't want to deal with storing pointers
 * and freeing them constantly, so I decided to
 * store chars.
 */
typedef struct bag {
        char tiles[100];
        size_t top;

        /*
         * Get a tile letter out of the bag.
         * It removes the letter from the bag.
         */
        char (*pop)( struct bag* );
} bag_t;

/*
 * The constructor.
 * Get a new bag using this.
 */
struct bag bag();



char bg_pop( struct bag* );

#endif

А вот bag.c -

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "bag.h"

const short distributions[27] = {
        9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 
        6, 4, 6, 4, 2, 2, 1, 2, 1, 2
};

char bg_pop( struct bag* b )
{
        return b->tiles[b->top--];
}

struct bag bag()
{
        struct bag b;
        for( char letter = 'A', count = 0; letter <= ']'; letter++ ) {
                for(int i = 0; i < distributions[letter - 65]; i++, count++)
                        b.tiles[count] = letter;
        }
        srand( time(NULL) );
        for( int i = 0; i < 3; i++, rand() )
                ;
        for( int i = 0; i < 98; i++ ) {
                int n = (rand() % (100 - i)) + i;
                char temp = b.tiles[i];
                b.tiles[i] = b.tiles[n];
                b.tiles[n] = temp;
        }
        b.top = 100;
        b.pop = bg_pop;
        return b;
}

Scrabble.c:

#include <stdio.h>
#include "bag.h"

int main( int argc, const char** argv )
{
        struct bag b = bag();

        for( int i = 0; i < 100; i++ )
                printf("%3c", b.tiles[i]);
}

Я знаю, что перетасовка выполняется правильно (по крайней мере, на OS X).

Не мог бы кто-нибудь помочь пролить свет на то, что здесь происходит?

1
birdoftheday 4 Янв 2016 в 03:38

2 ответа

Лучший ответ

Вот:

for( char letter = 'A', count = 0; letter <= ']'; letter++ ) {
    for( int i = 0; i < distributions[ letter - 65 ]; i++, count++ ) {
        b.tiles[count] = letter;
    }
}

Если вы распечатаете значение count, вы увидите, что оно достигает 152, что намного больше, чем 100 элементов в вашем массиве. Вы, кажется, нарушаете правила, потому что это приведет к отрицательному результату count в вашей системе, поскольку вы определили его как char, который подписан в вашей системе.

Вы можете определить его как int, и segfault у меня исчезнет, ​​когда я это сделаю, но вы все еще выходите за пределы вашего массива, поэтому явно что-то не так с вашей логикой при использовании {{X1} }, Вот. Или, что более полезно, у вас есть пара петель, которые выводят 152 буквы, но вы выделили память только для 100 из них, так что вы делаете больше букв, чем у вас есть место.

Если вы измените letter <= ']' на letter <= '[' (поскольку '[' стоит сразу после 'Z' в таблице ASCII), ваш цикл правильно завершится, когда count достигнет 100, и вы должен быть установлен.

1
Crowman 4 Янв 2016 в 01:21

Вы выходите за пределы своего массива в строке 21:

b.tiles[count] = letter;

Чтобы увидеть это с помощью gdb:

gdb blah
b 21
cond 1 count >= 100
run

Если вы подниметесь достаточно высоко с помощью count выше, вы выбросите обратный адрес в стек, и поэтому вы выполните segfault на return

0
Craig Estey 4 Янв 2016 в 01:06