| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 6458 人关注过本帖, 1 人收藏
标题:求解有关主函数返回值问题!!
只看楼主 加入收藏
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:15 
Alternatively, the main and wmain functions can be declared as returning void (no return value). If you declare main or wmain as returning void, you cannot return an exit code to the parent process or operating system by using a return statement. To return an exit code when main or wmain is declared as void, you must use the exit function.

翻译:作为可选的选项,main()及wmain()函数可以被声明为返回void(即无返回值)。如果你定义main()或wmain()返回void,那么你就不能通过return语句向父进程或操作系统返回一个退出码。要在声明了返回void的main()或wmain()函数时返回退出码,你必须使用exit()函数。

这个意思就是说,真正给父进程或操作系统返回退出码的机制,其实在exit()函数中,估计即使设置了返回声明的main()和wmain()函数,也是在内部隐式调用了exit()函数,这绝对符合简约原则——程序实现某个功能的代码应该只能有一处而非多处。所谓即使返回void,仍能返回,可见这里的返回设置有替代品,再联系前面的说明(main()并非编译器预设),应可判定给出退出码的机制完全不在main()中。

授人以渔,不授人以鱼。
2012-04-11 22:38
zhengbingyin
Rank: 2
等 级:论坛游民
帖 子:52
专家分:20
注 册:2012-3-27
收藏
得分:0 
返回值用于检测程序的正确性,特别是在一些比较繁琐的编程中,没有返回值来检测的话,往往会造成一些极度难以察觉的错误,因此使用返回值是一个很好的习惯。当然了,在不同的要求里,返回值也会不同。
2012-04-11 22:41
rjvsky
Rank: 3Rank: 3
来 自:陕西西安
等 级:论坛游侠
帖 子:77
专家分:106
注 册:2012-3-6
收藏
得分:0 
看了这么多解释终于,有点明白了,今天又有大收获了,谢谢各位!!!

企业需要的人才,是真正可以给他们带来经济效益的人,而不是一大堆没用的证书,看淡证书,强化自己···
2012-04-11 22:45
你们都要疼我哦
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:火星
等 级:贵宾
威 望:49
帖 子:1296
专家分:2746
注 册:2008-7-13
收藏
得分:0 
纵横论坛百余年  
抢到的地下室可供全宇宙人民人均八百亩居住面积。

小妹,哥哥看你骨骼清奇,绝非凡人,将来必成大业,不如这样,你先把裤裤脱了,待哥哥为你开启灵窍,然后我们一起努力钻研如何
2012-04-11 22:58
rjvsky
Rank: 3Rank: 3
来 自:陕西西安
等 级:论坛游侠
帖 子:77
专家分:106
注 册:2012-3-6
收藏
得分:0 
也就是说程序在开始时,最先不是进入主函数,而是先经过一大堆的编译链接,然后才进入主函数,还有其返回值确实是给了操作系统,以便来传递其运行的正确性
        mainret = _tmain(__argc, _targv, _tenviron);

然后 你的main()的返回值就会在mainret里喽

然后看后面的代码

            if ( !managedapp )
                exit(mainret);

            _cexit();

        }
        __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
        {
            /*
             * Should never reach here
             */

            mainret = GetExceptionCode();

            if ( !managedapp )
                _exit(mainret);

            _c_exit();

        } /* end of try - except */

        return mainret;
这样一说确实理解了

企业需要的人才,是真正可以给他们带来经济效益的人,而不是一大堆没用的证书,看淡证书,强化自己···
2012-04-11 23:10
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
在著名的《C++ Primer》书中,确实是叙述了main()函数必须有返回类型,而且返回值必须是int型。但同时读者也应该注意到书中后面的一段话:

在大多数系统中,main函数的返回值是一个状态指示器。返回值0往往表示main函数成功执行完毕。任何其他非零的返回值都有操作系统定义的含义。通常非零返回值表明有错误出现。每一种操作系统都有自己的方式告诉用户main函数返回什么内容。


这一段话,有如下几个信息:

1.main函数的返回值是一个标志该函数执行结果的状态指示器,正如printf()函数执行完后返回成功输出了多少个字符(因为输出的字符数目实际上未必正好是代码编写者希望输出的数目,在旧式纸带输出设备上因机器及缺纸故障而无法顺利完成任务是常见的事,因此该函数有这样的返回值)一样。事实上,我们自己编写的函数,也可以改编成myPrintf()函数,使用printf()一模一样的参数格式,在内部调用printf(),而把返回值修改为别的退出码,比如返回0标志成功(对应原始printf()的返回值与期望值相符),别的非零值对应各种各样的出错状态,而不仅仅靠字符数目来识别是否出错,事实上靠那个数根本无法判断是什么具体原因导致出错。这就是现代C/C++编程模式使用抛出错误机制和GetError()类函数获取errno_t错误码的原因,它抛弃了旧式C编程靠返回值标志错误的做法,在这种编程模式下,靠返回值来识别错误状态的做法已经过时了。

2.这种返回给操作系统的非零错误码,是操作系统定义的,首先我们得获取这份完整的错误编码表,才能知道具体某个编码表示什么错误信息,而且这种错误编码显然是不同的操作系统有不同的编码体系(在Windows中与Forms GUI有关的错误码不会在Unix/Linux的错误码表中出现,同一个码代表的意思也未必一样)。这样一来,所谓的兼容性其实等于零,除了大家共同认可(仅是认可而已,还不是规定)的0代表成功之外,非零都各自为政,那我们为了兼容性,还不是得针对不同的操作系统设置各自对应的退出码?工作量根本没有减轻,其实还是编写几份对应性的编码。用兼容性来说main应该返回什么,说说就好了,真要你返回非零,其实你自己都搞不清楚应该返回什么,可能还没意识不同的操作系统有不同的返回值意义。

3.这种返回值的使用,也是随着操作系统不同而不同的。比如DOS和Windows,在批处理脚本中使用ERRORLEVEL环境变量(环境变量实际上类似于编程中的全局变量,只不过是在操作系统中的全局变量,可以被所有运行着的程序读写)值来处理,在别的操作系统,未必是用这个了。不过现实中做项目,其实与该应用有关的全部程序,都是同一个团队做出来的,各个模块程序之间的信息协议,早就在方案中拟定了,各程序模块之间传递信息,为了最大的可移植性着想,也并不靠操作系统来使用各程序模块的返回值,而是把返回信息写在某个外部设备(比如文件)上供大家使用,这种信息比单纯的错误码来得更具体,没有人做设计要靠main的返回值来做左右系统的运作(除非要使用操作系统现成的程序套件,但做项目一般的倾向是越少依赖外部越好的,能够自己实现的功能,都尽量不用第三方控件,除了版权、著作权之类法律麻烦,还有安全性隐患,非迫不得已都不用。何况应用系统有信息交流是内部的事,犯不着上人造卫星向全世界广播,要写到操作系统环境变量中让所有程序知晓)。这又表明强调main的返回值效用不大,何况真要用,随时都可以用exit()来终结程序,若出错,此时都没有机会你返回main输出退出码了(此时想起GOTO都没用),仍然是多余。诸如文件无法打开那样的错误,程序中自己就可以处理,把屁股擦干净,直接exit(0)结束程序,操作系统仍然视程序执行成功——它实际上也无法帮助我把不能打开的文件变成能够打开,告诉它错误代码也没鸟用;数组越界崩溃了,你想返回啥都不行。

其实,这书所说的,如果仔细斟酌,跟上面我说过的那些东西,都是没有冲突的。main()尽管写了返回值,但终究应该是调用exit()做各种结束动作(销毁自动变量、退栈等等功夫),正如真正的启动代码不是main()做的一样的,结束代码也同样不是main()做的。

[ 本帖最后由 TonyDeng 于 2012-4-12 01:03 编辑 ]
收到的鲜花
  • 小鱼儿c2012-04-12 07:54 送鲜花  49朵   附言:犀利哥哥

授人以渔,不授人以鱼。
2012-04-12 00:48
小鱼儿c
Rank: 9Rank: 9Rank: 9
等 级:贵宾
威 望:14
帖 子:852
专家分:1317
注 册:2011-4-1
收藏
得分:0 
以下是引用zklhp在2012-4-11 18:39:09的发言:

经常看到有人写代码是int main(),这就表示主函数有返回值,但是此返回值是怎样得到的(也就是大小是怎样确定的。)

这种写法比较规范 因为是需要一个返回值的 这里返回值是由程序写作者返回的 所以怎么得到各人有各人的写法

主函数应该被操作系统看为一个功能模块,所以主函数的返回值应该是给了操作系统,但是问题是操作系统用此返回值干什么了??

判断程序的状态 比如 规定返回0正常 返回其他值不正常 这样操作系统就知道程序的状态了

接第一个回答 因为返回值是认为弄的 所以 程序正常了你也可以返回个9代表不正常 程序不正常了你也可以返回个0代表正常 由作者来定

这个一般用于命令行的脚本 比如windows的bat和*nix的sh

谢谢 前辈的指点啊。。
不懂汇编孩子伤不起啊

用心做一件事情就这么简单
2012-04-12 08:04
你们都要疼我哦
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:火星
等 级:贵宾
威 望:49
帖 子:1296
专家分:2746
注 册:2008-7-13
收藏
得分:0 
win32下面,主函数一般是WinMain(),启动函数由编译器在编译链接时添加进来,
双击EXE运行时,启动函数首先运行进行各种初始化,然后把winmain作为应用程序的入口函数进行调用。
GetStartupInfo (&StartupInfo);
Int nMainRetVal = WinMain(GetModuleHandle(NULL),NULL,pszCommandLineAnsi,( StartupInfo.dwFlags&STARTF_USESHOWWINDOW)?StartupInfo.wShowWindow:SW__SHOWDEFAULT);
返回时,启动函数又调用CRT的exit函数,将返回值nMainRetVal传递给exit,进行一些后期清理工作,最后调用系统ExitProcess函数退出。装vc的时候 如果完全安装的话,应该是有crt的源码的。
TonyDeng版主真有耐心,经常回那么一大大大大大大大大大大大段帖子,无线远程膜拜中。。。。。。

小妹,哥哥看你骨骼清奇,绝非凡人,将来必成大业,不如这样,你先把裤裤脱了,待哥哥为你开启灵窍,然后我们一起努力钻研如何
2012-04-12 08:55
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
再有这个就圆满了 gcc的。。

程序代码:

/**

 * crt1.c

 * This file has no copyright assigned and is placed in the Public Domain.

 * This file is a part of the mingw-runtime package.

 * No warranty is given; refer to the file DISCLAIMER within the package.

 *

 * Source code for the startup proceedures used by all programs. This code

 * is compiled to make crt1.o, which should be located in the library path.

 *

 */

/** Hide the declaration of _fmode with dllimport attribute in stdlib.h to
   avoid problems with older GCC. */
#define __IN_MINGW_RUNTIME
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <process.h>
#include <float.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <signal.h>

/** NOTE: The code for initializing the _argv, _argc, and environ variables

 *       has been moved to a separate .c file which is included in both

 *       crt1.c and dllcrt1.c. This means changes in the code don't have to

 *       be manually synchronized, but it does lead to this not-generally-

 *       a-good-idea use of include. */
#include "init.c"
#include "cpu_features.h"

extern void __main ();
extern void _pei386_runtime_relocator (void);

extern int main (int, char **, char **);

/** TLS initialization hook.  */
extern const PIMAGE_TLS_CALLBACK __dyn_tls_init_callback;

/**

 * Must have the correct app type for MSVCRT.

 */

#ifdef __MSVCRT__
#define __UNKNOWN_APP    0
#define __CONSOLE_APP    1
#define __GUI_APP        2
__MINGW_IMPORT void __set_app_type(int);
#endif /** __MSVCRT__ */

/**  Global _fmode for this .exe, not the one in msvcrt.dll,
    The default is set in txtmode.o in libmingw32.a */
/** Override the dllimport'd declarations in stdlib.h */
#undef _fmode
extern int _fmode;
#ifdef __MSVCRT__
extern int* __p__fmode(void); /** To access the dll _fmode */
#endif

/**

 * Setup the default file handles to have the _CRT_fmode mode, as well as

 * any new files created by the user.

 */
extern int _CRT_fmode;

static void
_mingw32_init_fmode (void)
{
  /** Don't set the std file mode if the user hasn't set any value for it. */
  if (_CRT_fmode)
    {
      _fmode = _CRT_fmode;

      /**
       * This overrides the default file mode settings for stdin,
       * stdout and stderr. At first I thought you would have to
       * test with isatty, but it seems that the DOS console at
       * least is smart enough to handle _O_BINARY stdout and
       * still display correctly.
       */
      if (stdin)

 {
   _setmode (_fileno (stdin), _CRT_fmode);

 }
      if (stdout)

 {
   _setmode (_fileno (stdout), _CRT_fmode);

 }
      if (stderr)

 {
   _setmode (_fileno (stderr), _CRT_fmode);

 }
    }

    /**  Now sync  the dll _fmode to the  one for this .exe.  */
#ifdef __MSVCRT__
    *__p__fmode() = _fmode;
#else
    *_imp___fmode_dll = _fmode;
#endif
}

/** This function will be called when a trap occurs. Thanks to Jacob
   Navia for his contribution. */
static CALLBACK long
_gnu_exception_handler (EXCEPTION_POINTERS * exception_data)
{
  void (*old_handler) (int);
  long action = EXCEPTION_CONTINUE_SEARCH;
  int reset_fpu = 0;

  switch (exception_data->ExceptionRecord->ExceptionCode)
    {
    case EXCEPTION_ACCESS_VIOLATION:
      /** test if the user has set SIGSEGV */
      old_handler = signal (SIGSEGV, SIG_DFL);
      if (old_handler == SIG_IGN)

 {
   /** this is undefined if the signal was raised by anything other
      than raise ().  */
   signal (SIGSEGV, SIG_IGN);
   action = EXCEPTION_CONTINUE_EXECUTION;

 }
      else if (old_handler != SIG_DFL)

 {
   /** This means 'old' is a user defined function. Call it */
   (*old_handler) (SIGSEGV);
   action = EXCEPTION_CONTINUE_EXECUTION;

 }
      break;

    case EXCEPTION_ILLEGAL_INSTRUCTION:
    case EXCEPTION_PRIV_INSTRUCTION:
      /** test if the user has set SIGILL */
      old_handler = signal (SIGILL, SIG_DFL);
      if (old_handler == SIG_IGN)

 {
   /** this is undefined if the signal was raised by anything other
      than raise ().  */
   signal (SIGILL, SIG_IGN);
   action = EXCEPTION_CONTINUE_EXECUTION;

 }
      else if (old_handler != SIG_DFL)

 {
   /** This means 'old' is a user defined function. Call it */
   (*old_handler) (SIGILL);
   action = EXCEPTION_CONTINUE_EXECUTION;

 }
      break;

    case EXCEPTION_FLT_INVALID_OPERATION:
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
    case EXCEPTION_FLT_DENORMAL_OPERAND:
    case EXCEPTION_FLT_OVERFLOW:
    case EXCEPTION_FLT_UNDERFLOW:
    case EXCEPTION_FLT_INEXACT_RESULT:
      reset_fpu = 1;
      /** fall through. */

    case EXCEPTION_INT_DIVIDE_BY_ZERO:
      /** test if the user has set SIGFPE */
      old_handler = signal (SIGFPE, SIG_DFL);
      if (old_handler == SIG_IGN)

 {
   signal (SIGFPE, SIG_IGN);
   if (reset_fpu)
     _fpreset ();
   action = EXCEPTION_CONTINUE_EXECUTION;

 }
      else if (old_handler != SIG_DFL)

 {
   /** This means 'old' is a user defined function. Call it */
   (*old_handler) (SIGFPE);
   action = EXCEPTION_CONTINUE_EXECUTION;

 }
      break;

    default:
      break;
    }
  return action;
}

/**

 * The function mainCRTStartup is the entry point for all console programs.

 */
static void  __MINGW_ATTRIB_NORETURN
__mingw_CRTStartup (void)
{
  int nRet;

  /** Initialize TLS callback.  */
  if (__dyn_tls_init_callback != NULL)
    __dyn_tls_init_callback (NULL, DLL_THREAD_ATTACH, NULL);

  /**
   * Set up the top-level exception handler so that signal handling
   * works as expected. The mapping between ANSI/POSIX signals and
   * Win32 SE is not 1-to-1, so caveat emptore.
   *
   */
  SetUnhandledExceptionFilter (_gnu_exception_handler);

  /**
   * Initialize floating point unit.
   */
  __cpu_features_init (); /** Do we have SSE, etc.*/
  _fpreset ();   /** Supplied by the runtime library. */

  /**
   * Set up __argc, __argv and _environ.
   */
  _mingw32_init_mainargs ();

  /**
   * Sets the default file mode.
   * If _CRT_fmode is set, also set mode for stdin, stdout
   * and stderr, as well
   * NOTE: DLLs don't do this because that would be rude!
   */
  _mingw32_init_fmode ();

 
   /** Adust references to dllimported data that have non-zero offsets.  */
  _pei386_runtime_relocator ();

  /** Align the stack to 16 bytes for the sake of SSE ops in main
     or in functions inlined into main.  */
  asm  __volatile__  ("andl $-16, %%esp" : : : "%esp");

   /** From libgcc.a, __main calls global class constructors via
      __do_global_ctors, This in turn  registers  __do_global_dtors
      as the first entry of the app's atexit table.  We do this
      explicitly at app startup rather than rely on gcc to generate
      the call in main's  prologue, since main may be imported from a dll
      which has its own __do_global_ctors.  */
    __main ();

  /**
   * Call the main function. If the user does not supply one
   * the one in the 'libmingw32.a' library will be linked in, and
   * that one calls WinMain. See main.c in the 'lib' dir
   * for more details.
   */
  nRet = main (_argc, _argv, environ);

  /**
   * Perform exit processing for the C library. This means
   * flushing output and calling 'atexit' registered functions.
   */
  _cexit ();

  ExitProcess (nRet);
}

/**

 * The function mainCRTStartup is the entry point for all console programs.

 */
void
mainCRTStartup (void)
{
#ifdef __MSVCRT__
  __set_app_type (__CONSOLE_APP);
#endif
  __mingw_CRTStartup ();
}

/**

 * For now the GUI startup function is the same as the console one.

 * This simply gets rid of the annoying warning about not being able

 * to find WinMainCRTStartup when linking GUI applications.

 */
void
WinMainCRTStartup (void)
{
#ifdef __MSVCRT__
  __set_app_type (__GUI_APP);
#endif
  __mingw_CRTStartup ();
}

/**

 *  We force use of library version of atexit, which is only

 *  visible in import lib as _imp__atexit

 */
extern int (*_imp__atexit)(void (*)(void));
int atexit (void (* pfn )(void) )
{
  return ( (*_imp__atexit)(pfn));
}

/** Likewise for non-ANSI _onexit */
extern _onexit_t (*_imp___onexit)(_onexit_t);
_onexit_t
_onexit (_onexit_t pfn )
{
  return (*_imp___onexit)(pfn);
}


2012-04-12 12:10
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
以下是引用小鱼儿c在2012-4-12 08:04:13的发言:

 
谢谢 前辈的指点啊。。
不懂汇编孩子伤不起啊

汇编只是工具 如果学过windows编程的话 不懂汇编也能理解这些 呵呵
2012-04-12 12:10
快速回复:求解有关主函数返回值问题!!
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.021664 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved