如何解决C程序立即在执行后退出,但它有一个while1循环
我正试图在3KB的限制下将c的扫雷程序安装到c中,以便它可以安装在qr代码中,并且在我添加选项-nostdlib
并对其进行编译之前,该程序一直运行良好已退出,尽管main中有while(1)循环而无法退出。
我在链接器中添加的库为kernel32
,gcc
和msvcrt
(以防您想重新生成它们)。尽管从技术上讲它确实返回了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},{0,-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,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,num3 = (int) systime.wMilliseconds;
bomb[1] = (xorshf96(num3,num1) % (HEIGHT-1)) + 1;
GetSystemTime(&systime);
num1,num3 = (int) systime.wMilliseconds;
bomb[0] = (xorshf96(num1,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,arrowPos);
break;
}
}
wait--;
}
}
解决方法
使用-nostdlib
具有两个效果:
- 阻止标准库的内容自动链接;和
- 它阻止了C运行时启动代码
crt0
的链接。
最后一个很重要,在调用main()
之前通常会发生很多事情,如果不链接crt0
,则 real 入口点_start
(不是main
)不存在。
因此解决方案是提供您自己的自己 _start
,但不提供真正的C程序所需的所有凌乱的C环境设置。就像提供一个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文件链接。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。