Cプログラムは実行後すぐに終了しますが、while(1)ループがあります

Aug 21 2020

マインスイーパプログラムを3KBの制限の下でcに収めようとしているので、qrコードに収めることができます。オプションを追加して-nostdlibコンパイルすると、しばらくの間、プログラムがすぐに終了するまで正常に機能していました(1 )終了する方法なしでメインでループします。

私はリンカに追加したライブラリであったkernel32gccmsvcrt(場合、あなたはそれを再生成します)。技術的には0x0を返しましたが、これは「成功」したことを意味しますが、明らかにそうではありませんでした。またはそれはそれをしなかっただろう。私のコードはここで見ることができます:

#include <windows.h>

#define WIDTH 100
#define HEIGHT 100
#define BOMBS 801

int xorshf96(int x, int y, int z)
{
   unsigned long t;
   x ^= x << 16;
   x ^= x >> 5;
   x ^= x << 1;

   t = x;
   x = y;
   y = z;
   z = t ^ x ^ y;

  return z;
}


void ExpandGrid(int fullGrid[WIDTH][HEIGHT], int knownGrid[WIDTH][HEIGHT], int blankPos[2])
{
    int neighbors[8][2] = {{0,1}, {1,0}, {1,1},
                          {0,-1},        {-1,0},
                          {-1,-1},{-1,1},{1,-1}};
    int curTile[2];

    knownGrid[blankPos[0]][blankPos[1]] = 1;
    if(fullGrid[blankPos[0]][blankPos[1]] != 0) return;

    for(int blck = 0; blck < 8; ++blck)
    {
        curTile[0] = abs(blankPos[0]+neighbors[blck][0]);
        curTile[1] = abs(blankPos[1]+neighbors[blck][1]);
        if(curTile[0] > WIDTH-1 || curTile[1] > HEIGHT-1) continue;

        if(fullGrid[curTile[0]][curTile[1]] == 0 && knownGrid[curTile[0]][curTile[1]] == 0)
        {
            knownGrid[curTile[0]][curTile[1]] = 1;
            ExpandGrid(fullGrid, knownGrid, curTile);
        }
        else if(fullGrid[curTile[0]][curTile[1]] > 0) knownGrid[curTile[0]][curTile[1]] = 1;
    }
}


int main(void)
{
    COORD characterBufferSize = { WIDTH, HEIGHT };
    COORD characterPosition = { 0, 0 };
    SMALL_RECT consoleWriteArea = { 0, 0, WIDTH - 1, HEIGHT - 1 };
    CHAR_INFO consoleBuffer[WIDTH][HEIGHT];

    HANDLE wHnd = GetStdHandle(STD_OUTPUT_HANDLE);
    HANDLE rHnd = GetStdHandle(STD_INPUT_HANDLE);

    int num1, num2, num3 = 0;
    SYSTEMTIME systime;

    DWORD numEventsRead = 0;
    DWORD numEvents = 0;
    INPUT_RECORD *eventBuffer;
    int wait = 0;

    SetConsoleTitle("Minesweeper!");

    int startGrid[WIDTH][HEIGHT] = { 0 };
    int knownGrid[WIDTH][HEIGHT] = { 0 };
    int arrowPos[2] = {0, 0};
    int bomb[2] = {0, 0};

    for (int i = 0; i < BOMBS; i++)
    {
        while (startGrid[bomb[0]][bomb[1]] < -1 || bomb[0] <= 0 || bomb[1] <= 0 || bomb[0] >= WIDTH-1 || bomb[1] >= HEIGHT-1)
        {
            GetLocalTime(&systime);
            num1, num2, num3 = (int) systime.wMilliseconds;

            bomb[1] = (xorshf96(num3, num2, num1) % (HEIGHT-1)) + 1;

            GetSystemTime(&systime);
            num1, num2, num3 = (int) systime.wMilliseconds;

            bomb[0] = (xorshf96(num1, num2, num3) % (WIDTH-1)) + 1;
        }

        startGrid[bomb[0]][bomb[1]] = -9;

        startGrid[bomb[0] + 1][bomb[1] + 1]++;
        startGrid[bomb[0] + 1][bomb[1]]++;
        startGrid[bomb[0]][bomb[1] + 1]++;
        startGrid[bomb[0] - 1][bomb[1] + 1]++;
        startGrid[bomb[0]][bomb[1] - 1]++;
        startGrid[bomb[0] + 1][bomb[1] - 1]++;
        startGrid[bomb[0] - 1][bomb[1] - 1]++;
        startGrid[bomb[0] - 1][bomb[1]]++;
    }

    while(1)
    {
        if (arrowPos[0] > WIDTH-1) arrowPos[0] = WIDTH-1;
        if (arrowPos[0] < 0) arrowPos[0] = 0;
        if (arrowPos[1] > HEIGHT-1) arrowPos[1] = HEIGHT-1;
        if (arrowPos[1] < 0) arrowPos[1] = 0;

        for (int x = 0; x < WIDTH; ++x)
        {
            for (int y = 0; y < HEIGHT; ++y)
            {

                if (knownGrid[x][y] == 1)
                {
                    if (startGrid[x][y] > 0)
                    {
                        consoleBuffer[x][y].Char.AsciiChar = '0' + startGrid[x][y];
                        consoleBuffer[x][y].Attributes = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
                    }
                    else
                    {
                        consoleBuffer[x][y].Char.AsciiChar = 'o';
                        consoleBuffer[x][y].Attributes = (startGrid[x][y] < 0 ? FOREGROUND_RED : FOREGROUND_BLUE) | FOREGROUND_INTENSITY;
                    }
                }
                else
                {
                    consoleBuffer[x][y].Char.AsciiChar = 00;
                    consoleBuffer[x][y].Attributes = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
                }

                if(arrowPos[0] == x && arrowPos[1] == y)
                {
                    consoleBuffer[x][y].Attributes = BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN;
                }
            }
        }

        WriteConsoleOutputA(wHnd, *consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);

        numEvents = 0;
        numEventsRead = 0;
        GetNumberOfConsoleInputEvents(rHnd, &numEvents);

        if (numEvents)
        {
            eventBuffer = malloc(sizeof(INPUT_RECORD) * numEvents);
            ReadConsoleInput(rHnd, eventBuffer, numEvents, &numEventsRead);
        }

        if(numEventsRead && wait <= 0)
        {
            wait = 50;
            switch (eventBuffer[0].Event.KeyEvent.wVirtualKeyCode)
            {
                case VK_UP:
                    arrowPos[0]--;
                    break;
                case VK_DOWN:
                    arrowPos[0]++;
                    break;
                case VK_LEFT:
                    arrowPos[1]--;
                    break;
                case VK_RIGHT:
                    arrowPos[1]++;
                    break;
                case VK_RETURN:
                    ExpandGrid(startGrid, knownGrid, arrowPos);
                    break;
            }
        }

        wait--;
    }

}

回答

2 paxdiablo Aug 22 2020 at 07:35

の使用に-nostdlib2つの効果があります。

  • 標準ライブラリのものが自動的にリンクされるのを防ぎます。そして
  • Cランタイム起動コードcrt0がリンクされないようにします。

最後の1つは重要です。通常、main()呼び出される前に発生することがたくさんあり、リンクしないとcrt0実際のエントリポイント_start(ではないmain)は存在しません。

したがって、解決策は、実際のCプログラムに必要な面倒なC環境のセットアップをすべて行わずに、独自のもの を提供すること_startです。これはmystart.S、次のように提供するのと同じくらい簡単です。

.globl _start
_start:
    call main           # call main without any setup.
    movl %eax, %ebx     # copy return value to pass back.
    movl $1, %eax # function code for exit. int $0x80           # invoke function.

次のように、それを非常に基本的なCプログラムと組み合わせます。

int main(void) {
    return 42;
}

次に、次のトランスクリプトに従って、コンパイルしてテストできます。

pax> gcc -nostdlib -o nolibc nolibc.c mystart.S
pax> ./nolibc ; echo $?
42

これは、「なぜ実行されないのか」という特定の質問に答えます。ただし、他の場所からいくつかの追加の最適化を収集した場合でも実行可能サイズはかなり分厚いものになりました。

gcc -nostdlib -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -o nolibc nolibc.c mystart.S
strip -s -R .comment -R .gnu.version --strip-unneeded nolibc

したがって、それを超えて、実行可能ファイルを実行するために絶対に必要なものだけをロードするように、リンカースクリプトをいじる必要があります。

それはかなり大きな主題であり、あなたの特定の質問とは関係がないので、ここではそれについては触れません。(もちろん検索した後)それを行う方法について別の質問をすることをお勧めします。


そして、それint $80はメモリからのLinuxの考え方であることを忘れないでください。Windowsで同様のことをしたい場合はcrt0、そのプラットフォームでコードがどのように機能するかを調べる必要があります。使用する可能性が高いExitProcess()ため、次のようなものが必要になります(テストされていないため、自分でデバッグする必要があります):

.globl _start
.extern ExitProcess
_start:
    call main           # call main without any setup.
    push %eax           # push main return code for delivery to OS.
                        # win64 equiv is, I think: mov %eax, %ecx
    call ExitProcess    # And off we go.

次に、Windowsのdll / libファイルとリンクする方法を検討します。