学习了程序员的自我修养这本书,对着示例CRT代码敲了一遍,收获还是颇丰的。
源码
- string.c
#include <windows.h>
char* itoa(int n, char* str, int radix)
{
char digit[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char* p = str;
char* head = str;
if (!p || radix < 2 || radix > 36)
return p;
if (radix != 10 && n < 0)
return p;
if (n == 0)
{
*p++ = '0';
*p = 0;
return p;
}
if (radix == 10 && n < 0)
{
*p++ = '-';
n = -n;
}
while (n)
{
*p++ = digit[n%radix];
n /= radix;
}
*p = 0;
for (--p; head < p; ++head, --p)
{
char temp = *head;
*head = *p;
*p = temp;
}
return str;
}
int strcmp(const char* src, const char* dst)
{
int ret = 0;
unsigned char* p1 = (unsigned char*)src;
unsigned char* p2 = (unsigned char*)dst;
while (!(ret = *p1 - *p2) && *p2)
++p1, ++p2;
if (ret < 0)
ret = -1;
else if (ret > 0)
ret = 1;
return ret;
}
char* strcpy(char* dest, const char* src)
{
char* ret = dest;
while (*src)
*dest++ = *src++;
*dest = '\0';
return ret;
}
int strlen(const char* str)
{
int cnt = 0;
if (!str)
return 0;
for (; *str != '\0'; ++str)
++cnt;
return cnt;
}
- stdio.c
#include "minicrt.h"
#include <windows.h>
int mini_crt_io_init()
{
return 1;
}
FILE* fopen(const char* filename, const char* mode)
{
HANDLE hFile = 0;
int access = 0;
int creation = 0;
if (strcmp(mode, "w") == 0)
{
access |= GENERIC_WRITE;
creation |= CREATE_ALWAYS;
}
if (strcmp(mode, "w+") == 0)
{
access |= GENERIC_WRITE | GENERIC_READ;
creation |= CREATE_ALWAYS;
}
if (strcmp(mode, "r") == 0)
{
access |= GENERIC_READ;
creation |= OPEN_EXISTING;
}
if (strcmp(mode, "r+") == 0)
{
access |= GENERIC_WRITE | GENERIC_READ;
creation |= TRUNCATE_EXISTING;
}
hFile = CreateFileA(filename, access, 0, 0, creation, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
return (FILE*)hFile;
}
int fread(void* buffer, int size, int count, FILE* stream)
{
int read = 0;
if (!ReadFile((HANDLE)stream, buffer, size * count, &read, 0))
return 0;
return read;
}
int fwrite(const void* buffer, int size, int count, FILE* stream)
{
int written = 0;
if (!WriteFile((HANDLE)stream, buffer, size * count, &written, 0))
return 0;
return written;
}
int fclose(FILE* fp)
{
return CloseHandle((HANDLE)fp);
}
int fseek(FILE* fp, int offset, int set)
{
return SetFilePointer((HANDLE)fp, offset, 0, set);
}
- entry.c
#include "minicrt.h"
#include <windows.h>
extern int main(int argc, char* argv[]);
void exit(int);
static void crt_fatal_error(const char* msg)
{
//printf("fatal error: %s",msg);
exit(1);
}
void mini_crt_entry(void)
{
int ret;
//Init part
int flag = 0;
int argc = 0;
char* argv[16];//maximum 16 parameter
char* cl = GetCommandLineA();
//analyse command line
argv[0] = cl;
argc++;
while (*cl)
{
if (*cl == '\"')
if (flag == 0) flag = 1;
else flag = 0;
else if (*cl == ' '&&flag == 0)
{
if (*(cl + 1))
{
argv[argc] = cl + 1;
argc++;
}
*cl = '\0';
}
cl++;
}
if (!mini_crt_heap_init())
crt_fatal_error("heap initialize failed\n");
if (!mini_crt_io_init())
crt_fatal_error("IO initialize failed\n");
ret = main(argc,argv);
//exit part
exit(ret);
}
void exit(int exitCode)
{
ExitProcess(exitCode);
}
- malloc.c
#include "minicrt.h"
#include <windows.h>
typedef struct _heap_header
{
enum {
HEAP_BLOCK_FREE = 0xABABABAB,
HEAP_BLOCK_USED = 0xCDCDCDCD
}type;
unsigned size;
struct _heap_header* next;
struct _heap_header* prev;
}heap_header;
#define ADDR_ADD(a,o)(((char*)(a)) + o)
#define HEADER_SIZE (sizeof(heap_header))
static heap_header* list_head = NULL;
int mini_crt_heap_init()
{
void* base = NULL;
heap_header* header = NULL;
//32 MB size HEAP
unsigned int heap_size = 1024 * 1024 * 32;
base = VirtualAlloc(0, heap_size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
if (!base) return 0;
header = (heap_header*)base;
header->size = heap_size;
header->type = HEAP_BLOCK_FREE;
header->next = NULL;
header->prev = NULL;
list_head = header;
return 1;
}
void* malloc(unsigned size)
{
heap_header* header;
if (!size)return NULL;
header = list_head;
while (header)
{
if (header->type == HEAP_BLOCK_USED)
{
header = header->next;
continue;
}
if (header->size > size + HEADER_SIZE && header->size <= size + HEADER_SIZE * 2)
{
header->type = HEAP_BLOCK_USED;
/*
I do think this code have bug. When the last time you mark this block to USED (size is big)
but when the size comes little this time,it actually can contains.
Oh,I am wrong,the next time call malloc it can also get in next if QAQ.
*/
}
if (header->size > size + HEADER_SIZE * 2)
{
// split
heap_header* next = (heap_header*)ADDR_ADD(header, size + HEADER_SIZE);
next->prev = header;
next->next = header->next;
next->type = HEAP_BLOCK_FREE;
next->size = header->size - (size - HEADER_SIZE);
header->next = next;
header->size = size + HEADER_SIZE;
header->type = HEAP_BLOCK_USED;
return ADDR_ADD(header, HEADER_SIZE);
}
header = header->next;
}
return NULL;
}
void free(void* ptr)
{
heap_header* header = (heap_header*)ADDR_ADD(ptr, -HEADER_SIZE);
if (header->type != HEAP_BLOCK_USED)
return;
header->type = HEAP_BLOCK_FREE;
// merge behind
if (header->prev != NULL && header->prev->type == HEAP_BLOCK_FREE)
{
header->prev->next = header->next;
if (header->next != NULL)
header->next->prev = header->prev;
header->prev->size += header->size;
header = header->prev;
}
// merge after
if (header->next != NULL && header->next->type == HEAP_BLOCK_FREE)
{
header->size += header->next->size;
header->next = header->next->next;
}
}
- minicrt.h
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
//malloc
#ifndef NULL
#define NULL (0)
#endif
void free(void* ptr);
void* malloc(int size);
int mini_crt_init_heap();
//string
char* itoa(int n,char* str,int radix);
int strcmp(const char* src,const char* dst);
char* strcpy(char* dest,const char* src);
int strlen(const char* str);
//file & IO
typedef int FILE;
#define EOF (-1)
#define stdin ((FILE*)(GetStdHandle(STD_INPUT_HANDLE)))
#define stdout ((FILE*)(GetStdHandle(STD_OUTPUT_HANDLE)))
#define stderr ((FILE*)(GetStdHandle(STD_ERROR_HANDLE)))
int mini_crt_init_io();
FILE* fopen(const char* filename,const char* mode);
int fread(void* buffer,int size,int count,FILE* stream);
int fwrite(const void* buffer, int size, int count, FILE* stream);
int fclose(FILE* fp);
int fseek(FILE* fp, int offset, int set);
//printf
int fputc(int c, FILE* stream);
int fputs(const char* str, FILE* stream);
int printf(const char* format, ...);
int fprintf(FILE* stream,const char* format,...);
//internal
void do_global_ctors();
void mini_crt_call_exit_routine();
//atexit
typedef void (*atexit_func_t)(void);
int atexit(atexit_func_t func);
#ifdef __cplusplus
}
#endif
- formatstr.c
#include "minicrt.h"
#include <windows.h>
int fputc(int c, FILE* stream)
{
if (fwrite(&c, 1, 1, stream) != 1)
return EOF;
else
return c;
}
int fputs(const char* str, FILE* stream)
{
int len = strlen(str);
if (fwrite(str, 1, len, stream) != len)
return EOF;
else
return len;
}
int vfprintf(FILE* stream, const char* format, va_list arglist)
{
// define a translate-normal pattern
int translating = 0;
int ret = 0;
const char* p = 0;
for (p = format; *p; p++)
{
switch (*p)
{
case '%':
if (!translating)
translating = 1;
else
{
if (fputc('%', stream) < 0)
return EOF;
ret++;
translating = 0;
}
break;
case 'd':
if (translating)
{
char buf[16];
translating = 0;
itoa(va_arg(arglist, int), buf, 10);
if (fputs(buf, stream) < 0)
return EOF;
ret += strlen(buf);
}
else if (fputc('d', stream) < 0)
return EOF;
else
ret++;
break;
case 's':
if (translating)
{
const char* str = va_arg(arglist, const char*);
translating = 0;
if (fputs(str, stream) < 0)
return EOF;
ret += strlen(str);
}
else if (fputc('s', stream) < 0)
return EOF;
else
ret++;
break;
default:
if (!translating)
translating = 0;
if (fputc(*p, stream) < 0)
return EOF;
else
ret++;
break;
}
}
return ret;
}
int printf(const char* format, ...)
{
va_list(arglist);
va_start(arglist, format);
return vfprintf(stdout, format, arglist);
}
int fprintf(FILE* stream,const char* format,...)
{
va_list(arglist);
va_start(arglist, format);
return vfprintf(stream, format, arglist);
}
测试代码
#include "minicrt.h"
int main(int argc,char* argv[])
{
int i;
FILE* fp;
char** v = malloc(argc*sizeof(char*));
for (i = 0; i < argc; i++)
{
v[i] = malloc(strlen(argv[i]) + 1);
strcpy(v[i], argv[i]);
}
fp = fopen("test.txt", "w");
for (i = 0; i < argc; i++)
{
int len = strlen(v[i]);
fwrite(&len, 1, sizeof(int), fp);
fwrite(v[i], 1, len, fp);
}
fclose(fp);
fp = fopen("test.txt", "r");
for (i = 0; i < argc; i++)
{
int len;
char* buf;
fread(&len, 1, sizeof(int), fp);
buf = malloc(len + 1);
fread(buf, 1, len, fp);
buf[len] = '\0';
printf("%d %s\n", len, buf);
free(buf);
free(v[i]);
}
fclose(fp);
return 0;
}
- 已测试通过
编译方法
cl /c /DWIN32 /GS- entry.c malloc.c formatstr.c stdio.c string.c
lib entry.obj malloc.obj formatstr.obj stdio.obj string.obj /OUT:minicrt.lib
使用方法
cl /c /DWIN32 main.c
link main.obj minicrt.lib kernel32.lib /NODEFAULTLIB /entry:mini_crt_entry
dir main.exe