What can you do in C without “std” includes? Are they part of “C,” or just libraries?

后端 未结 11 953
孤独总比滥情好
孤独总比滥情好 2021-02-02 06:11

I apologize if this is a subjective or repeated question. It\'s sort of awkward to search for, so I wasn\'t sure what terms to include.

What I\'d like to know is what th

11条回答
  •  有刺的猬
    2021-02-02 06:45

    What could I do if there's no printf(), fopen(), etc?

    As long as you know how to interface the system you are using you can live without the standard C library. In embedded systems where you only have several kilobytes of memory, you probably don't want to use the standard library at all.

    Here is a Hello World! example on Linux and Windows without using any standard C functions:

    For example on Linux you can invoke the Linux system calls directly in inline assembly:

    /* 64 bit linux. */
    
    #define SYSCALL_EXIT 60
    #define SYSCALL_WRITE 1
    
    void sys_exit(int error_code)
    {
        asm volatile
        (
            "syscall"
            : 
            : "a"(SYSCALL_EXIT), "D"(error_code)
            : "rcx", "r11", "memory"
        );
    }
    
    int sys_write(unsigned fd, const char *buf, unsigned count)
    {
        unsigned ret;
    
        asm volatile
        (
            "syscall"
            : "=a"(ret)
            : "a"(SYSCALL_WRITE), "D"(fd), "S"(buf), "d"(count)
            : "rcx", "r11", "memory"
        );
        
        return ret;
    }
    
    void _start(void)
    {
        const char hwText[] = "Hello world!\n";
    
        sys_write(1, hwText, sizeof(hwText));
        sys_exit(12);
    }
    

    You can look up the manual page for "syscall" which you can find how can you make system calls. On Intel x86_64 you put the system call id into RAX, and then return value will be stored in RAX. The arguments must be put into RDI, RSI, RDX, R10, R9 and R8 in this order (when the argument is used).

    Once you have this you should look up how to write inline assembly in gcc. The syscall instruction changes the RCX, R11 registers and memory so we add this to the clobber list make GCC aware of it.

    The default entry point for the GNU linker is _start. Normally the standard library provides it, but without it you need to provide it. It isn't really a function as there is no caller function to return to. So we must make another system call to exit our process.

    Compile this with:

    gcc -nostdlib nostd.c 
    

    And it outputs Hello world!, and exits.

    On Windows the system calls are not published, instead it's hidden behind another layer of abstraction, the kernel32.dll. Which is always loaded when your program starts whether you want it or not. So you can simply include windows.h from the Windows SDK and use the Win32 API as usual:

    #include 
    
    void _start(void)
    {
        const char str[] = "Hello world!\n";
        HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
        DWORD written;
    
        WriteFile(stdout, str, sizeof(str), &written, NULL);
        ExitProcess(12);
    }
    

    The windows.h has nothing to do with the standard C library, as you should be able to write Windows programs in any other language too.

    You can compile it using the MinGW tools like this:

    gcc -nostdlib C:\Windows\System32\kernel32.dll nostdlib.c
    

    Then the compiler is smart enough to resolve the import dependencies and compile your program.

    If you disassemble the program, you can see only your code is there, there is no standard library bloat in it.

    So you can use C without the standard library.

提交回复
热议问题