编辑器,编译器,调试器的选择:

  在WINDOWS平台下编译C程序有很多种编译器可以选择,比如gcc,cl,intel c++等等。编辑器呢!那就更多了,但是有2个编辑器做为主要的推荐,1个是vim,1个是emacs.任选其1就好了。调试器的话,你可以选择gdb,还有vc集成开发环境的调试器。gdb别看它黑乎乎的哦!很是强大的,不过你不习惯黑乎乎的命令行界面,也可以选择一些gdb的图形前端。vc集成开发环境的调试器呢!也非常的强大,很直观。不过这2款调试器,我比较偏爱gdb.

用vim编写C语言helloworld代码:

  vim的上手可以参考其帮助文档,启动vim后输入命令:help 如图:

通过这一帮助文件就可以让你很快的上手vim.当然要更好的在vim写C程序你可以参考网络上相关的windows下vim c++开发环境的搭建文献。

  呵呵!下面开始在vim中写代码了!

要理解好这个程序呢!必须先要明白一个概念,就是一个程序到底是如何被编写,编译,链接和执行的,这一过程可以参考下面的图:

首先,我们刚才在VIM中编写了helloworld的源代码了,下一步就是编译了。如图:

可以看到一个cl命令后,编译器自动完成了编译链接的过程。下面结合代码一步一步的去理解这一整个过程。
  第一行!很多人做事情都有自己的一个工具包!比如修电路的大叔!一般都挎着一个电工包!木匠呢,也有一个工具包,里面放着一些常用的工具。嘿嘿!那么,现在我是想在屏幕上输出helloworld!这个时候呢,我就需要1个输出包啦!所以#include<stdio.h>就表示把stdio.h文件包拿来用,<>是表示从哪里去拿。#include就是表示这里这个位置开始插入包(其实本质是复制粘帖)。嘿嘿!我明白了!stdio.h里啦就定义实现如何输出的了!其实这个stdio.h里了不是只有输出工具的,还有输入等工具!所以这里的代码其实是把所有stdio.h里面的工具包全拿过来了!(看起来有点浪费啊!唉!)。那么如何查看这一预处理后的文件呢,这里我们加入1个参数/P.就会出现1个.i的文件了,我们来看看:

嘿嘿!出来了吧!我们再打开这个.i文件看看:
View Code
#line 1 "helloworld.c"
#line 1 "E:\\vc6.0\\VC98\\Include\\stdio.h"















#pragma once
#line 18 "E:\\vc6.0\\VC98\\Include\\stdio.h"






#line 25 "E:\\vc6.0\\VC98\\Include\\stdio.h"







#pragma pack(push,8)
#line 34 "E:\\vc6.0\\VC98\\Include\\stdio.h"














#line 49 "E:\\vc6.0\\VC98\\Include\\stdio.h"
#line 50 "E:\\vc6.0\\VC98\\Include\\stdio.h"






#line 57 "E:\\vc6.0\\VC98\\Include\\stdio.h"








#line 66 "E:\\vc6.0\\VC98\\Include\\stdio.h"
#line 67 "E:\\vc6.0\\VC98\\Include\\stdio.h"



typedef unsigned
int size_t;

#line 73 "E:\\vc6.0\\VC98\\Include\\stdio.h"




typedef unsigned
short wchar_t;

#line 80 "E:\\vc6.0\\VC98\\Include\\stdio.h"



typedef wchar_t wint_t;
typedef wchar_t wctype_t;

#line 87 "E:\\vc6.0\\VC98\\Include\\stdio.h"
#line 88 "E:\\vc6.0\\VC98\\Include\\stdio.h"









typedef
char * va_list;
#line 99 "E:\\vc6.0\\VC98\\Include\\stdio.h"

#line 101 "E:\\vc6.0\\VC98\\Include\\stdio.h"






#line 108 "E:\\vc6.0\\VC98\\Include\\stdio.h"

#line 110 "E:\\vc6.0\\VC98\\Include\\stdio.h"






























#line 141 "E:\\vc6.0\\VC98\\Include\\stdio.h"





struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef
struct _iobuf FILE;

#line 159 "E:\\vc6.0\\VC98\\Include\\stdio.h"












#line 172 "E:\\vc6.0\\VC98\\Include\\stdio.h"












#line 185 "E:\\vc6.0\\VC98\\Include\\stdio.h"




























#line 214 "E:\\vc6.0\\VC98\\Include\\stdio.h"
#line 215 "E:\\vc6.0\\VC98\\Include\\stdio.h"





extern FILE _iob[];
#line 222 "E:\\vc6.0\\VC98\\Include\\stdio.h"









#line 232 "E:\\vc6.0\\VC98\\Include\\stdio.h"


typedef __int64 fpos_t;







#line 243 "E:\\vc6.0\\VC98\\Include\\stdio.h"
#line 244 "E:\\vc6.0\\VC98\\Include\\stdio.h"


#line 247 "E:\\vc6.0\\VC98\\Include\\stdio.h"




























int __cdecl _filbuf(FILE *);
int __cdecl _flsbuf(int, FILE *);




FILE
* __cdecl _fsopen(const char *, const char *, int);
#line 283 "E:\\vc6.0\\VC98\\Include\\stdio.h"

void __cdecl clearerr(FILE *);
int __cdecl fclose(FILE *);
int __cdecl _fcloseall(void);




FILE
* __cdecl _fdopen(int, const char *);
#line 293 "E:\\vc6.0\\VC98\\Include\\stdio.h"

int __cdecl feof(FILE *);
int __cdecl ferror(FILE *);
int __cdecl fflush(FILE *);
int __cdecl fgetc(FILE *);
int __cdecl _fgetchar(void);
int __cdecl fgetpos(FILE *, fpos_t *);
char * __cdecl fgets(char *, int, FILE *);




int __cdecl _fileno(FILE *);
#line 307 "E:\\vc6.0\\VC98\\Include\\stdio.h"

int __cdecl _flushall(void);
FILE
* __cdecl fopen(const char *, const char *);
int __cdecl fprintf(FILE *, const char *, ...);
int __cdecl fputc(int, FILE *);
int __cdecl _fputchar(int);
int __cdecl fputs(const char *, FILE *);
size_t __cdecl fread(
void *, size_t, size_t, FILE *);
FILE
* __cdecl freopen(const char *, const char *, FILE *);
int __cdecl fscanf(FILE *, const char *, ...);
int __cdecl fsetpos(FILE *, const fpos_t *);
int __cdecl fseek(FILE *, long, int);
long __cdecl ftell(FILE *);
size_t __cdecl fwrite(
const void *, size_t, size_t, FILE *);
int __cdecl getc(FILE *);
int __cdecl getchar(void);
int __cdecl _getmaxstdio(void);
char * __cdecl gets(char *);
int __cdecl _getw(FILE *);
void __cdecl perror(const char *);
int __cdecl _pclose(FILE *);
FILE
* __cdecl _popen(const char *, const char *);
int __cdecl printf(const char *, ...);
int __cdecl putc(int, FILE *);
int __cdecl putchar(int);
int __cdecl puts(const char *);
int __cdecl _putw(int, FILE *);
int __cdecl remove(const char *);
int __cdecl rename(const char *, const char *);
void __cdecl rewind(FILE *);
int __cdecl _rmtmp(void);
int __cdecl scanf(const char *, ...);
void __cdecl setbuf(FILE *, char *);
int __cdecl _setmaxstdio(int);
int __cdecl setvbuf(FILE *, char *, int, size_t);
int __cdecl _snprintf(char *, size_t, const char *, ...);
int __cdecl sprintf(char *, const char *, ...);
int __cdecl sscanf(const char *, const char *, ...);
char * __cdecl _tempnam(const char *, const char *);
FILE
* __cdecl tmpfile(void);
char * __cdecl tmpnam(char *);
int __cdecl ungetc(int, FILE *);
int __cdecl _unlink(const char *);
int __cdecl vfprintf(FILE *, const char *, va_list);
int __cdecl vprintf(const char *, va_list);
int __cdecl _vsnprintf(char *, size_t, const char *, va_list);
int __cdecl vsprintf(char *, const char *, va_list);








#line 363 "E:\\vc6.0\\VC98\\Include\\stdio.h"




FILE
* __cdecl _wfsopen(const wchar_t *, const wchar_t *, int);
#line 369 "E:\\vc6.0\\VC98\\Include\\stdio.h"

wint_t __cdecl fgetwc(FILE
*);
wint_t __cdecl _fgetwchar(
void);
wint_t __cdecl fputwc(wint_t, FILE
*);
wint_t __cdecl _fputwchar(wint_t);
wint_t __cdecl getwc(FILE
*);
wint_t __cdecl getwchar(
void);
wint_t __cdecl putwc(wint_t, FILE
*);
wint_t __cdecl putwchar(wint_t);
wint_t __cdecl ungetwc(wint_t, FILE
*);

wchar_t
* __cdecl fgetws(wchar_t *, int, FILE *);
int __cdecl fputws(const wchar_t *, FILE *);
wchar_t
* __cdecl _getws(wchar_t *);
int __cdecl _putws(const wchar_t *);

int __cdecl fwprintf(FILE *, const wchar_t *, ...);
int __cdecl wprintf(const wchar_t *, ...);
int __cdecl _snwprintf(wchar_t *, size_t, const wchar_t *, ...);
int __cdecl swprintf(wchar_t *, const wchar_t *, ...);
int __cdecl vfwprintf(FILE *, const wchar_t *, va_list);
int __cdecl vwprintf(const wchar_t *, va_list);
int __cdecl _vsnwprintf(wchar_t *, size_t, const wchar_t *, va_list);
int __cdecl vswprintf(wchar_t *, const wchar_t *, va_list);
int __cdecl fwscanf(FILE *, const wchar_t *, ...);
int __cdecl swscanf(const wchar_t *, const wchar_t *, ...);
int __cdecl wscanf(const wchar_t *, ...);






FILE
* __cdecl _wfdopen(int, const wchar_t *);
FILE
* __cdecl _wfopen(const wchar_t *, const wchar_t *);
FILE
* __cdecl _wfreopen(const wchar_t *, const wchar_t *, FILE *);
void __cdecl _wperror(const wchar_t *);
FILE
* __cdecl _wpopen(const wchar_t *, const wchar_t *);
int __cdecl _wremove(const wchar_t *);
wchar_t
* __cdecl _wtempnam(const wchar_t *, const wchar_t *);
wchar_t
* __cdecl _wtmpnam(wchar_t *);



#line 414 "E:\\vc6.0\\VC98\\Include\\stdio.h"
#line 415 "E:\\vc6.0\\VC98\\Include\\stdio.h"


#line 418 "E:\\vc6.0\\VC98\\Include\\stdio.h"
































int __cdecl fcloseall(void);
FILE
* __cdecl fdopen(int, const char *);
int __cdecl fgetchar(void);
int __cdecl fileno(FILE *);
int __cdecl flushall(void);
int __cdecl fputchar(int);
int __cdecl getw(FILE *);
int __cdecl putw(int, FILE *);
int __cdecl rmtmp(void);
char * __cdecl tempnam(const char *, const char *);
int __cdecl unlink(const char *);

#line 463 "E:\\vc6.0\\VC98\\Include\\stdio.h"






#pragma pack(pop)
#line 471 "E:\\vc6.0\\VC98\\Include\\stdio.h"

#line 473 "E:\\vc6.0\\VC98\\Include\\stdio.h"
#line 2 "helloworld.c"

void main()
{
printf(
"Helloworld!\n");
}

  第三行!void main(),这行呢,先要说明下了!一个很特别的单词main后面还跟着个()。其实这样的形式在C语言就是表示函数,前面的int就是函数的返回值了!其实函数也就像1个工具包里的一个有生命的小工具!你给它喂点东西,比如这里喂void,而且还要让它吐出1个int类型的东西出来!嘿嘿!而且这个有生命的小工具是可以反复使用的!但是这里有点特殊!用main命名的小工具一般只用1次,因为它有个特殊的使命啊!!!它是用户的入门函数!其实呢在VC上面真正的入门函数是mainCRTStartup(),在用户入门函数main()之前到mainCRTStartup()中间呢其实已经做了好多工作了!比如有准备命令行,获得操作系统版本,IO的初始化,获得环境选项以及其字符串,全局变量的初始化,还有做了条件编译!如果是Win程序的话入口就是WinMain(),如果不是入口就是main()啦!然后main()执行完了程序才exit啦!其实main()函数是倒数第二条函数了!不过呢一般来说,程序员有操作权限的就是到了main()函数这里了!这一过程你可以打开crt0.c文件看看具体代码如下:

View Code
/***
*crt0.c - C runtime initialization routine
*
* Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
* This the actual startup routine for apps. It calls the user's main
* routine [w]main() or [w]WinMain after performing C Run-Time Library
* initialization.
*
* (With ifdef's, this source file also provides the source code for
* wcrt0.c, the startup routine for console apps with wide characters,
* wincrt0.c, the startup routine for Windows apps, and wwincrt0.c,
* the startup routine for Windows apps with wide characters.)
*
******************************************************************************
*/

#ifdef _WIN32

#ifndef CRTDLL

#include
<cruntime.h>
#include
<dos.h>
#include
<internal.h>
#include
<stdlib.h>
#include
<string.h>
#include
<rterr.h>
#include
<windows.h>
#include
<awint.h>
#include
<tchar.h>
#include
<dbgint.h>

/*
* wWinMain is not yet defined in winbase.h. When it is, this should be
* removed.
*/

int
WINAPI
wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd
);

#ifdef WPRFLAG
_TUCHAR
* __cdecl _wwincmdln(void);
#else /* WPRFLAG */
_TUCHAR
* __cdecl _wincmdln(void);
#endif /* WPRFLAG */

/*
* command line, environment, and a few other globals
*/

#ifdef WPRFLAG
wchar_t
*_wcmdln; /* points to wide command line */
#else /* WPRFLAG */
char *_acmdln; /* points to command line */
#endif /* WPRFLAG */

char *_aenvptr = NULL; /* points to environment block */
wchar_t
*_wenvptr = NULL; /* points to wide environment block */


void (__cdecl * _aexit_rtn)(int) = _exit; /* RT message return procedure */

static void __cdecl fast_error_exit(int); /* Error exit via ExitProcess */

/*
* _error_mode and _apptype, together, determine how error messages are
* written out.
*/
int __error_mode = _OUT_TO_DEFAULT;
#ifdef _WINMAIN_
int __app_type = _GUI_APP;
#else /* _WINMAIN_ */
int __app_type = _CONSOLE_APP;
#endif /* _WINMAIN_ */


/***
*BaseProcessStartup(PVOID Peb)
*
*Purpose:
* This routine does the C runtime initialization, calls main(), and
* then exits. It never returns.
*
*Entry:
* PVOID Peb - pointer to Win32 Process Environment Block (not used)
*
*Exit:
* This function never returns.
*
******************************************************************************
*/

#ifdef _WINMAIN_

#ifdef WPRFLAG
void wWinMainCRTStartup(
#else /* WPRFLAG */
void WinMainCRTStartup(
#endif /* WPRFLAG */

#else /* _WINMAIN_ */

#ifdef WPRFLAG
void wmainCRTStartup(
#else /* WPRFLAG */
void mainCRTStartup(
#endif /* WPRFLAG */

#endif /* _WINMAIN_ */
void
)

{
int mainret;

#ifdef _WINMAIN_
_TUCHAR
*lpszCommandLine;
STARTUPINFO StartupInfo;
#endif /* _WINMAIN_ */

/*
* Get the full Win32 version
*/
_osver
= GetVersion();

_winminor
= (_osver >> 8) & 0x00FF ;
_winmajor
= _osver & 0x00FF ;
_winver
= (_winmajor << 8) + _winminor;
_osver
= (_osver >> 16) & 0x00FFFF ;

#ifdef _MT
if ( !_heap_init(1) ) /* initialize heap */
#else /* _MT */
if ( !_heap_init(0) ) /* initialize heap */
#endif /* _MT */
fast_error_exit(_RT_HEAPINIT);
/* write message and die */

#ifdef _MT
if( !_mtinit() ) /* initialize multi-thread */
fast_error_exit(_RT_THREAD);
/* write message and die */
#endif /* _MT */

/*
* Guard the remainder of the initialization code and the call
* to user's main, or WinMain, function in a __try/__except
* statement.
*/

__try {

_ioinit();
/* initialize lowio */

#ifdef WPRFLAG
/* get wide cmd line info */
_wcmdln
= (wchar_t *)__crtGetCommandLineW();

/* get wide environ info */
_wenvptr
= (wchar_t *)__crtGetEnvironmentStringsW();

_wsetargv();
_wsetenvp();
#else /* WPRFLAG */
/* get cmd line info */
_acmdln
= (char *)GetCommandLineA();

/* get environ info */
_aenvptr
= (char *)__crtGetEnvironmentStringsA();

_setargv();
_setenvp();
#endif /* WPRFLAG */

_cinit();
/* do C data initialize */

#ifdef _WINMAIN_

StartupInfo.dwFlags
= 0;
GetStartupInfo(
&StartupInfo );

#ifdef WPRFLAG
lpszCommandLine
= _wwincmdln();
mainret
= wWinMain(
#else /* WPRFLAG */
lpszCommandLine
= _wincmdln();
mainret
= WinMain(
#endif /* WPRFLAG */
GetModuleHandleA(NULL),
NULL,
lpszCommandLine,
StartupInfo.dwFlags
& STARTF_USESHOWWINDOW
? StartupInfo.wShowWindow
: SW_SHOWDEFAULT
);
#else /* _WINMAIN_ */

#ifdef WPRFLAG
__winitenv
= _wenviron;
mainret
= wmain(__argc, __wargv, _wenviron);
#else /* WPRFLAG */
__initenv
= _environ;
mainret
= main(__argc, __argv, _environ);
#endif /* WPRFLAG */

#endif /* _WINMAIN_ */
exit(mainret);
}
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
{
/*
* Should never reach here
*/
_exit( GetExceptionCode() );

}
/* end of try - except */

}



/***
*_amsg_exit(rterrnum) - Fast exit fatal errors
*
*Purpose:
* Exit the program with error code of 255 and appropriate error
* message.
*
*Entry:
* int rterrnum - error message number (amsg_exit only).
*
*Exit:
* Calls exit() (for integer divide-by-0) or _exit() indirectly
* through _aexit_rtn [amsg_exit].
* For multi-thread: calls _exit() function
*
*Exceptions:
*
******************************************************************************
*/

void __cdecl _amsg_exit (
int rterrnum
)
{
#ifdef _WINMAIN_
if ( __error_mode == _OUT_TO_STDERR )
#else /* _WINMAIN_ */
if ( __error_mode != _OUT_TO_MSGBOX )
#endif /* _WINMAIN_ */
_FF_MSGBANNER();
/* write run-time error banner */

_NMSG_WRITE(rterrnum);
/* write message */
_aexit_rtn(
255); /* normally _exit(255) */
}

/***
*fast_error_exit(rterrnum) - Faster exit fatal errors
*
*Purpose:
* Exit the process with error code of 255 and appropriate error
* message.
*
*Entry:
* int rterrnum - error message number (amsg_exit only).
*
*Exit:
* Calls ExitProcess.
*
*Exceptions:
*
******************************************************************************
*/

static void __cdecl fast_error_exit (
int rterrnum
)
{
#ifdef _WINMAIN_
if ( __error_mode == _OUT_TO_STDERR )
#else /* _WINMAIN_ */
if ( __error_mode != _OUT_TO_MSGBOX )
#endif /* _WINMAIN_ */
_FF_MSGBANNER();
/* write run-time error banner */

_NMSG_WRITE(rterrnum);
/* write message */
ExitProcess(
255); /* normally _exit(255) */
}

#ifndef WPRFLAG


#endif /* WPRFLAG */

#endif /* CRTDLL */

#else /* _WIN32 */

#include
<cruntime.h>
#include
<internal.h>
#include
<stdlib.h>
#include
<msdos.h>
#include
<string.h>
#include
<setjmp.h>
#include
<dbgint.h>
#include
<macos\types.h>
#include
<macos\segload.h>
#include
<macos\gestalte.h>
#include
<macos\osutils.h>
#include
<macos\traps.h>
#include
<mpw.h>

static void __cdecl Inherit(void); /* local function */

int __cdecl main(int, char **, char **); /*generated by compiler*/

unsigned
long _GetShellStack(void);

static char * __cdecl _p2cstr_internal ( unsigned char * str );

extern MPWBLOCK * _pMPWBlock;
extern int __argc;
extern char **__argv;

/***
*__crt0()
*
*Purpose:
* This routine does the C runtime initialization, calls main(), and
* then exits. It never returns.
*
*Entry:
*
*Exit:
* This function never returns.
*
******************************************************************************
*/

void __cdecl __crt0 (
)
{
int mainret;
char szPgmName[32];
char *pArg;
char *argv[2];

#ifndef _M_MPPC
void *pv;

/* This is the magic stuff that MPW tools do to get info from MPW*/

pv
= (void *)*(int *)0x316;
if (pv != NULL && !((int)pv & 1) && *(int *)pv == 'MPGM') {
pv
= (void *)*++(int *)pv;
if (pv != NULL && *(short *)pv == 'SH') {
_pMPWBlock
= (MPWBLOCK *)pv;
}
}

#endif /* _M_MPPC */

_environ
= NULL;
if (_pMPWBlock == NULL) {
__argc
= 1;
memcpy(szPgmName, (
char *)0x910, sizeof(szPgmName));
pArg
= _p2cstr_internal(szPgmName);
argv[
0] = pArg;
argv[
1] = NULL;
__argv
= argv;

#ifndef _M_MPPC
_shellStack
= 0; /* force ExitToShell */
#endif /* _M_MPPC */
}
#ifndef _M_MPPC
else {
_shellStack
= _GetShellStack(); //return current a6, or first a6
_shellStack += 4; //a6 + 4 is the stack pointer we want
__argc = _pMPWBlock->argc;
__argv
= _pMPWBlock->argv;

Inherit();
/* Inherit file handles - env is set up by _envinit if needed */
}
#endif /* _M_MPPC */

/*
* call run time initializer
*/
__cinit();

mainret
= main(__argc, __argv, _environ);
exit(mainret);
}


#ifndef _M_MPPC
/***
*Inherit() - obtain and process info on inherited file handles.
*
*Purpose:
*
* Locates and interprets MPW std files. For files we just save the
* file handles. For the console we save the device table address so
* we can do console I/O. In the latter case, FDEV is set in the _osfile
* array.
*
*Entry:
* Address of MPW param table
*
*Exit:
* No return value.
*
*Exceptions:
*
******************************************************************************
*/

static void __cdecl Inherit (
void
)
{
MPWFILE
*pFile;
int i;
pFile
= _pMPWBlock->pFile;
if (pFile == NULL) {
return;
}
for (i = 0; i < 3; i++) {
switch ((pFile->pDevice)->name) {
case 'ECON':
_osfile[i]
|= FDEV | FOPEN;
_osfhnd[i]
= (int)pFile;
break;

case 'FSYS':
_osfile[i]
|= FOPEN;
_osfhnd[i]
= (*(pFile->ppFInfo))->ioRefNum;
break;
}
pFile
++;
}
}

#endif /* _M_MPPC */



static char * __cdecl _p2cstr_internal (
unsigned
char * str
)
{
unsigned
char *pchSrc;
unsigned
char *pchDst;
int cch;

if ( str && *str ) {
pchDst
= str;
pchSrc
= str + 1;

for ( cch=*pchDst; cch; --cch ) {
*pchDst++ = *pchSrc++;
}

*pchDst = '\0';
}

return( str );
}

#endif /* _WIN32 */

  第四行和第六行!诶!为什么这两行要一起学啊!因为这两行只有2个括号!哈哈!在C语言中呢1对括号就代表1个代码块了!就像python的缩进了!(应该是这样吧!以后学多了或许有新感悟的!暂时就这样理解吧!)

  第五行!这一行呢其实就是从stdio.h中取出printf输出这个小工具(函数)了,然后用这个工具在屏幕上打印helloworld!字符串了!哦!很神奇!这个工具好啊!但是目前鉴于自己的水平,还不能很好的解释printf的实现,以后再解释了!

  关于cl的命令行参数可以参考msdn,上面有详细的参数帮助目录。

摇滚经典曲子:  Paramore - Warped Tour 2011 - Carefu