绝对没人能完整得回答出这个问题!

时间:2022-08-27 14:38:13
1、请问我如何能做出自已的头文件?我看到别人的程序中头文件.H只是一些函数的说明
,并无函数体,完整的函数好像放在与该头文件同名的一个.C中,请问如何制作自已
的头文件?另外能否详细地讲解一下自已定义的头文件的工作原理!
2、我看到TC 2。0的INCLUDE目录中有一些标谁预定义的头文件,如stdio.h等。但在其中
只能找到一些函数的定义,没有具体的函数实现,请问它的工作原理,另外我还在另一目
录LIB中找到一些OBJ和LIB文件,是编译过的二进制文件,不知它和预置的头文件有何关系?
能否讲解一下OBJ与LIB文件的作用?

3、看到STDIO.H中有PRINTF和SCANF函数的说明,其定义如下:
int      _Cdecl printf  (const char *format, ...);

不知...的作用是什么?

21 个解决方案

#1


paying attention!

#2


既然都“绝对没人”,你还要问

#3


。H就是存盘的时候写成*。H不就行了吗(我的看法)。

#4


开发商都是*呀?还能让你看见具体的实现嘛?
你所使用的库函数的定义都放在.h文件中,而这些函数的实现都已经编译在Lib文件里面了。

创建静态库:

现在以一个简单的数学函数库为例介绍静态库的创建和使用。

要创建静态库,选择File->New菜单,弹出New对话框。选择Projects标签,在项目类型列表框中选择Win32 Static Library,在Name中输入mymath,表明要创建一个mymath.lib的静态库文件。

然后用Project->Add to Project->Files菜单往mymath工程中加入以下两个文件:

1.头文件(见清单1):定义了Summary和Factorial两个函数,分别用于完成求和与阶乘。注意这里使用C风格的函数,需要加入extern “C”关键字,表明它是C风格的外部函数。

清单1 头文件

#ifndef _MYMATH_H

#define _MYMATH_H

extern “C”

{

int Summary(int n);

int Factorial(int n);

}

#endif

2.源文件:包含了Summary和Factorial函数的定义,见清单2。

清单2 源文件

int Summary(int n)

{

int sum=0;

int i;

for(i=1;i<=n;i++)

{

sum+=i;

}

return sum;

}

int Factorial(int n)

{

int Fact=1;

int i;

for(i=1;i<=n;i++)

{

Fact=Fact*i;

}

return Fact;

}

 

在Build菜单下,选择Build菜单下的Build mymath.lib。Visual C++编译链接工程,在mymath\debug目录下生成mymath.lib文件。至此,静态连接库生成的工作就做完了。下面用一个小程序来测试这个静态库。

提示:用户在交付最终静态连接库时,只需要提供.lib文件和头文件,不需要再提供库的源代码。 

 

 

#5




看来你很白C啊!
学点C基础吧!别在这里丢中国人脸了!
[你是哪个学校的啊?没开C语言吗?]

#6


兄弟,到底关不关心这个问题,怎么扔在这不管了?

#7


关注,详细点回答可以么?

#8


    我上面的贴子不是解释了.h文件和.lib文件了吗,还有个教你自己制作静态库的例子(用VC)。好,我再说一遍,先说.h和.c,在我们进行编程的时候,为了是程序的结构清晰,常将对于函数的定义、变量的说明等语句写在.h文件中,而函数的实现代码写在.c文件中,然后在.h文件的头部#include "*.h"文件,这样就算是一个工程了。
    但是厂商在提供它们开发的库函数等产品的时候,是不能将.h和.c文件一起提交的(如果他们愿意,其实也是可以的),它们要保护自己的知识产权,不想让别人看见它们对于某些问题的
具体实现方法,所以通过我上面所讲的开发静态库的方法,将工程遍译成一个.h文件和一个.lib
文件,用户可以从.h文件中看到厂商都提供了哪些函数,也就是可以实现哪些功能,然后直接进
行调用就行了,但是这些函数是怎么工作的用户就看不到了,对于只想使用函数的人来说也没有
必要看到具体的实现,这种方式实现了程序的封装。另外,开发商某一天如果发现对于某个函数
的实现方法不太好,比如说绘图函数的效率不高,他们就可以重新编写函数的实现,而对应的.h
文件不用发生改变,用户在使用新版本的静态函数库时,不用改变原来的使用习惯,只需用新版
的.lib文件覆盖旧版的就可以了。
    我想对于前两点问题我已经说的够清楚了,如果你还不清楚就也请把不清楚的地方详细的指
出来。
    第三点问题是这样的:你想,你使用printf()函数的时候,它里面的参数个数不是一定的,
是和前面的格式字符串有直接关系的,格式字符串中指定了要输出多少变量的值,后面就应该跟
随多少变量作为参数,所以这样的函数在定义的时候是不能将参数写死的。

#9


上面有一处打错了,应该是“在.c文件中#include ".h"”。

#10


/* 补充一点 */
/*清单1 头文件*/

#ifndef _MYMATH_H

#define _MYMATH_H

#ifdef __cplusplus  /* 如果 使用 C++ ,使用  C 连接 */
extern “C”{
#endif

int Summary(int n);

int Factorial(int n);

#ifdef __cplusplus
}
#endif

#endif

#11


to GZCompiler(编译器) :
1、.h中定义的变量是全局变量么?是定义变量还是定义类型呢?我看到
头文件中好像只有类型定义和函数声明呀,没见到变量定义的?
2、要自已定义一个头文件.h让别人引用的话,是要把函数及类型的声明
写在.h中(假设为example.h),函数的具体实现写在(example.c)中么?
.h与.c要同名是么?
3、你提到了现在咱们用的LIB及自定义的C文件和它们对应的。H文件构成
了静态函数库,那么还有动态函数库么?倘若有的话(是TC还是V C++有)
,能介绍一下么?
4、至于printf中的...,你说得意思我知道,但我考虑的是如果自已要写
一个不定参数的函数,若只写出出...的话,在函数体中怎么引用这些形参呢?

#12


to GZCompiler(编译器) :
谢谢你的回答,我已给你加了50分,
这几个问题你回答后出来后,请到
http://www.csdn.net/expert/topic/410/410427.shtm
http://www.csdn.net/expert/topic/410/410433.shtm
http://www.csdn.net/expert/topic/410/410493.shtm
这几个贴子上去,我给你加更多的分!

#13


1.应该是全局变量,对于这个工程都是有效的。我不明白你说的定义类型是什么意思,
定义变量就是声明一个变量,包括对该变量类型的指定。对,象厂商所提供的库中的.h文件
一般不会有变量定义,因为它不需要提供给你什么变量来使用。
2.当然,在你自己写的头文件(.h)中,可以包括变量的定义,如果你是出于给别人用的目的,
如果这些变量对别人没有用处(如只是帮助你自己来实现函数的功能),就不要写在头文件中。
总之,为了程序的结构清晰,就是要把函数的声明(定义)写在头文件(.h)中,函数的实现
写在源文件(.c)中,至于两个文件的文件名是不一定要相同的,至于取为相同的名称是明确
它们之间存在很密切的关系。
3.动态库,有啊,难道你没听说过传说中的DLL(动态连接库)吗?DLL就不这么简单可讲了,建议你先弄本基础的C语言书,里面一般都有讲头文件和源文件关系的。然后再看Windows编程的
书,里面有讲DLL的。
4.写这种函数,我也没实践过,你再问问高手吧,我也关注。

#14


...使用va_arg, va_end, va_start宏调用
下面是一个例子

int average( int first, ... )
{
   int count = 0, sum = 0, i = first;
   va_list marker;

   va_start( marker, first );     /* Initialize variable arguments. */
   while( i != -1 )
   {
      sum += i;
      count++;
      i = va_arg( marker, int);
   }
   va_end( marker );              /* Reset variable arguments.      */
   return( sum ? (sum / count) : 0 );
}

#15


nothingneed——高手!WinterSeo应该给他分

#16


呵呵,我不是什么高手
但是我对你上面的几点回答有些异议,试解如下
1,声明和定义是不同的,一般声明写载头文件中,定义写在cpp/c中
   但是inline函数例外
2,lib文件中放的是函数的入口地址,无论静态还是动态都会有这个文件
   是为了在Link时找到头文件中的函数入口

#17


"谢谢你的回答,我已给你加了50分,"
不好意思,我怎么没收到?

#18


nothingneed(玄痴) GZCompiler(编译器):
已给你们加分了!

#19


to GZCompiler(编译器):
谢谢你另一贴子上还有你94分

#20


好帖子啊

#21


说的简单点:.h文件大致可以看作接口说明文件;而.c文件是实现具体功能的代码文件。解
释如下:
1. 用法:通常.h文件中只放全局变量(C++的全局对象)、函数、宏定义和数据类型(C++的
类class)的声明,而变量的定义和函数的实现则放在.c或.cpp文件中。假设要编写一个动态数组模块,以供整个项目的其它模块使用:

/* darr.h: interface declaration file for the dynamic array module */
#ifndef _DYNAMIC_ARRY_H
#define _DYNAMIC_ARRY_H /*宏定义,避免多次include*/

/*定义一个数据类型:数组元素类型        */
/*假如用C++,可以用template实现类型通用性*/
typedef int dynarr_element_t;

/*动态数租类型*/
typedef struct _tagdynarr 
{
  int               m_nSize;
  dynarr_element_t  *m_pItem;  
} dynarr_t;

/* 声明一个全局变量,必须加extern,否则是定义了一个变量 */
extern int g_nDebugLevel; /* for test purpose only */

/*函数声明,extern 可省略*/
extern int dynarr_init(dynarr_t *pArray);
extern int dynarr_setsize(arr_t *pArray,int nSize)
 
#endif /* _DYNAMIC_ARRY_H*

/* darr_init.c: implenmentation file for the dynamic array module */
#include <stdio.h>

#include "darr.h"

/* g_nDebugLevel在.h文件中声明,
   在.c文件中定义!
   注意这里不要加extern   
*/
int g_nDebugLevel; 

int dynarr_init(dynarr_t *pArray)
{
  if(pArray == NULL) return -1;
  pArray->m_nSize = 0;
  pArray->m_pItem = NULL;
  return 0;
}

/* darr_size.c: implenmentation file for the dynamic array module */
#include <stdio.h>

#include "darr.h"

int dynarr_setsize(arr_t *pArray,int nSize)
{
  if(g_nDebugLevel>1) {
    printf("array set size to %d\n", nSize);
  }
  /* implenmentation code goes here */
  return nSize;
}

好,动态数组模块可以交付使用了!如果编写主模块的人要使用这个模块,那么
只要把接口文件和编译好的目标文件(.obj或库文件)提供给他,他就可以这样
使用之:

/*main.c: main module*/

#include "darr.h"

int main(int argc,char *argv[])
{
  dynarr_t darr;
  dynarr_init(&darr);
  dynarr_setsize(100);
  ... ...
}

2. 优点:便于模块化软件开发。如上例所示,模块开发者只要
将.h文件作为接口发布出来,其它模块就可以使用了。这样不会
造成源代码的多份拷贝,便于维护,并且只要保持接口不变,可
以继续对实现代码进行完善,增加新功能。当然还可以保护商业
秘密,防止源代码泄露。

3. 以上仅仅是建议的使用方式,不是强制的。include语句的作用
就是把所包含文件的内容“搬”到当前位置,就像直接写上去的一
样!include可以包含任何文本文件(当然其内容要符合C/C++语法),
如.c和.cpp文件,如果喜欢,你完全可以使用任何别的扩展名。你也完
全可以在头文件中定义变量,写函数的是现代码。当然,这些做法都
不是好的风格,是软件工程所不提倡的,但却体现了C/C++的灵活性。 

4. 在上面的例子中,可以把darr_init.c和darr_size.c编译成
darr_obj.c和darr_size.obj后连同接口darr.h一起交给用户使用。
但这样有一个问题,即darr_obj.c和darr_size.obj联系紧密,但
却是2个文件,不便于维护、管理和使用。这时最好考虑使用库文
件,将darr_obj.c和darr_size.obj打包成darr.lib.如c语言的标
准库函数象printf,scanf等就只能看到声明,而找不到实现文件,
原因就是是实现代码已经编译成二进制形式并生成了库文件。

5. 对于int _Cdecl printf(const char *format, ...),
nothingneed(玄痴)给出了很好的例子,这里再补充几点:
printf函数声明中的_Cdecl是很重要的,它表明采用C语言的
函数调用方式,其它调用方式如pascal是不能有不确定数目的
参数的;va_arg()的作用是根据前面的参数找到下一个参数,
前提是要知道下一个参数的类型。如printf("%d %f\n",i,f),
我们可以想象printf函数内部是根据类型首先取出整型的i打
印出来,然后是双精度型浮点型的f;那么它又是怎样知道我们
传进去的是一个int型和一个double型的参数呢?这要归功于
printf函数的第一个固定参数char *fmt,本例中,根据"%d %f"
格式化字符串完全可以知道后面所有参数的类型。

#1


paying attention!

#2


既然都“绝对没人”,你还要问

#3


。H就是存盘的时候写成*。H不就行了吗(我的看法)。

#4


开发商都是*呀?还能让你看见具体的实现嘛?
你所使用的库函数的定义都放在.h文件中,而这些函数的实现都已经编译在Lib文件里面了。

创建静态库:

现在以一个简单的数学函数库为例介绍静态库的创建和使用。

要创建静态库,选择File->New菜单,弹出New对话框。选择Projects标签,在项目类型列表框中选择Win32 Static Library,在Name中输入mymath,表明要创建一个mymath.lib的静态库文件。

然后用Project->Add to Project->Files菜单往mymath工程中加入以下两个文件:

1.头文件(见清单1):定义了Summary和Factorial两个函数,分别用于完成求和与阶乘。注意这里使用C风格的函数,需要加入extern “C”关键字,表明它是C风格的外部函数。

清单1 头文件

#ifndef _MYMATH_H

#define _MYMATH_H

extern “C”

{

int Summary(int n);

int Factorial(int n);

}

#endif

2.源文件:包含了Summary和Factorial函数的定义,见清单2。

清单2 源文件

int Summary(int n)

{

int sum=0;

int i;

for(i=1;i<=n;i++)

{

sum+=i;

}

return sum;

}

int Factorial(int n)

{

int Fact=1;

int i;

for(i=1;i<=n;i++)

{

Fact=Fact*i;

}

return Fact;

}

 

在Build菜单下,选择Build菜单下的Build mymath.lib。Visual C++编译链接工程,在mymath\debug目录下生成mymath.lib文件。至此,静态连接库生成的工作就做完了。下面用一个小程序来测试这个静态库。

提示:用户在交付最终静态连接库时,只需要提供.lib文件和头文件,不需要再提供库的源代码。 

 

 

#5




看来你很白C啊!
学点C基础吧!别在这里丢中国人脸了!
[你是哪个学校的啊?没开C语言吗?]

#6


兄弟,到底关不关心这个问题,怎么扔在这不管了?

#7


关注,详细点回答可以么?

#8


    我上面的贴子不是解释了.h文件和.lib文件了吗,还有个教你自己制作静态库的例子(用VC)。好,我再说一遍,先说.h和.c,在我们进行编程的时候,为了是程序的结构清晰,常将对于函数的定义、变量的说明等语句写在.h文件中,而函数的实现代码写在.c文件中,然后在.h文件的头部#include "*.h"文件,这样就算是一个工程了。
    但是厂商在提供它们开发的库函数等产品的时候,是不能将.h和.c文件一起提交的(如果他们愿意,其实也是可以的),它们要保护自己的知识产权,不想让别人看见它们对于某些问题的
具体实现方法,所以通过我上面所讲的开发静态库的方法,将工程遍译成一个.h文件和一个.lib
文件,用户可以从.h文件中看到厂商都提供了哪些函数,也就是可以实现哪些功能,然后直接进
行调用就行了,但是这些函数是怎么工作的用户就看不到了,对于只想使用函数的人来说也没有
必要看到具体的实现,这种方式实现了程序的封装。另外,开发商某一天如果发现对于某个函数
的实现方法不太好,比如说绘图函数的效率不高,他们就可以重新编写函数的实现,而对应的.h
文件不用发生改变,用户在使用新版本的静态函数库时,不用改变原来的使用习惯,只需用新版
的.lib文件覆盖旧版的就可以了。
    我想对于前两点问题我已经说的够清楚了,如果你还不清楚就也请把不清楚的地方详细的指
出来。
    第三点问题是这样的:你想,你使用printf()函数的时候,它里面的参数个数不是一定的,
是和前面的格式字符串有直接关系的,格式字符串中指定了要输出多少变量的值,后面就应该跟
随多少变量作为参数,所以这样的函数在定义的时候是不能将参数写死的。

#9


上面有一处打错了,应该是“在.c文件中#include ".h"”。

#10


/* 补充一点 */
/*清单1 头文件*/

#ifndef _MYMATH_H

#define _MYMATH_H

#ifdef __cplusplus  /* 如果 使用 C++ ,使用  C 连接 */
extern “C”{
#endif

int Summary(int n);

int Factorial(int n);

#ifdef __cplusplus
}
#endif

#endif

#11


to GZCompiler(编译器) :
1、.h中定义的变量是全局变量么?是定义变量还是定义类型呢?我看到
头文件中好像只有类型定义和函数声明呀,没见到变量定义的?
2、要自已定义一个头文件.h让别人引用的话,是要把函数及类型的声明
写在.h中(假设为example.h),函数的具体实现写在(example.c)中么?
.h与.c要同名是么?
3、你提到了现在咱们用的LIB及自定义的C文件和它们对应的。H文件构成
了静态函数库,那么还有动态函数库么?倘若有的话(是TC还是V C++有)
,能介绍一下么?
4、至于printf中的...,你说得意思我知道,但我考虑的是如果自已要写
一个不定参数的函数,若只写出出...的话,在函数体中怎么引用这些形参呢?

#12


to GZCompiler(编译器) :
谢谢你的回答,我已给你加了50分,
这几个问题你回答后出来后,请到
http://www.csdn.net/expert/topic/410/410427.shtm
http://www.csdn.net/expert/topic/410/410433.shtm
http://www.csdn.net/expert/topic/410/410493.shtm
这几个贴子上去,我给你加更多的分!

#13


1.应该是全局变量,对于这个工程都是有效的。我不明白你说的定义类型是什么意思,
定义变量就是声明一个变量,包括对该变量类型的指定。对,象厂商所提供的库中的.h文件
一般不会有变量定义,因为它不需要提供给你什么变量来使用。
2.当然,在你自己写的头文件(.h)中,可以包括变量的定义,如果你是出于给别人用的目的,
如果这些变量对别人没有用处(如只是帮助你自己来实现函数的功能),就不要写在头文件中。
总之,为了程序的结构清晰,就是要把函数的声明(定义)写在头文件(.h)中,函数的实现
写在源文件(.c)中,至于两个文件的文件名是不一定要相同的,至于取为相同的名称是明确
它们之间存在很密切的关系。
3.动态库,有啊,难道你没听说过传说中的DLL(动态连接库)吗?DLL就不这么简单可讲了,建议你先弄本基础的C语言书,里面一般都有讲头文件和源文件关系的。然后再看Windows编程的
书,里面有讲DLL的。
4.写这种函数,我也没实践过,你再问问高手吧,我也关注。

#14


...使用va_arg, va_end, va_start宏调用
下面是一个例子

int average( int first, ... )
{
   int count = 0, sum = 0, i = first;
   va_list marker;

   va_start( marker, first );     /* Initialize variable arguments. */
   while( i != -1 )
   {
      sum += i;
      count++;
      i = va_arg( marker, int);
   }
   va_end( marker );              /* Reset variable arguments.      */
   return( sum ? (sum / count) : 0 );
}

#15


nothingneed——高手!WinterSeo应该给他分

#16


呵呵,我不是什么高手
但是我对你上面的几点回答有些异议,试解如下
1,声明和定义是不同的,一般声明写载头文件中,定义写在cpp/c中
   但是inline函数例外
2,lib文件中放的是函数的入口地址,无论静态还是动态都会有这个文件
   是为了在Link时找到头文件中的函数入口

#17


"谢谢你的回答,我已给你加了50分,"
不好意思,我怎么没收到?

#18


nothingneed(玄痴) GZCompiler(编译器):
已给你们加分了!

#19


to GZCompiler(编译器):
谢谢你另一贴子上还有你94分

#20


好帖子啊

#21


说的简单点:.h文件大致可以看作接口说明文件;而.c文件是实现具体功能的代码文件。解
释如下:
1. 用法:通常.h文件中只放全局变量(C++的全局对象)、函数、宏定义和数据类型(C++的
类class)的声明,而变量的定义和函数的实现则放在.c或.cpp文件中。假设要编写一个动态数组模块,以供整个项目的其它模块使用:

/* darr.h: interface declaration file for the dynamic array module */
#ifndef _DYNAMIC_ARRY_H
#define _DYNAMIC_ARRY_H /*宏定义,避免多次include*/

/*定义一个数据类型:数组元素类型        */
/*假如用C++,可以用template实现类型通用性*/
typedef int dynarr_element_t;

/*动态数租类型*/
typedef struct _tagdynarr 
{
  int               m_nSize;
  dynarr_element_t  *m_pItem;  
} dynarr_t;

/* 声明一个全局变量,必须加extern,否则是定义了一个变量 */
extern int g_nDebugLevel; /* for test purpose only */

/*函数声明,extern 可省略*/
extern int dynarr_init(dynarr_t *pArray);
extern int dynarr_setsize(arr_t *pArray,int nSize)
 
#endif /* _DYNAMIC_ARRY_H*

/* darr_init.c: implenmentation file for the dynamic array module */
#include <stdio.h>

#include "darr.h"

/* g_nDebugLevel在.h文件中声明,
   在.c文件中定义!
   注意这里不要加extern   
*/
int g_nDebugLevel; 

int dynarr_init(dynarr_t *pArray)
{
  if(pArray == NULL) return -1;
  pArray->m_nSize = 0;
  pArray->m_pItem = NULL;
  return 0;
}

/* darr_size.c: implenmentation file for the dynamic array module */
#include <stdio.h>

#include "darr.h"

int dynarr_setsize(arr_t *pArray,int nSize)
{
  if(g_nDebugLevel>1) {
    printf("array set size to %d\n", nSize);
  }
  /* implenmentation code goes here */
  return nSize;
}

好,动态数组模块可以交付使用了!如果编写主模块的人要使用这个模块,那么
只要把接口文件和编译好的目标文件(.obj或库文件)提供给他,他就可以这样
使用之:

/*main.c: main module*/

#include "darr.h"

int main(int argc,char *argv[])
{
  dynarr_t darr;
  dynarr_init(&darr);
  dynarr_setsize(100);
  ... ...
}

2. 优点:便于模块化软件开发。如上例所示,模块开发者只要
将.h文件作为接口发布出来,其它模块就可以使用了。这样不会
造成源代码的多份拷贝,便于维护,并且只要保持接口不变,可
以继续对实现代码进行完善,增加新功能。当然还可以保护商业
秘密,防止源代码泄露。

3. 以上仅仅是建议的使用方式,不是强制的。include语句的作用
就是把所包含文件的内容“搬”到当前位置,就像直接写上去的一
样!include可以包含任何文本文件(当然其内容要符合C/C++语法),
如.c和.cpp文件,如果喜欢,你完全可以使用任何别的扩展名。你也完
全可以在头文件中定义变量,写函数的是现代码。当然,这些做法都
不是好的风格,是软件工程所不提倡的,但却体现了C/C++的灵活性。 

4. 在上面的例子中,可以把darr_init.c和darr_size.c编译成
darr_obj.c和darr_size.obj后连同接口darr.h一起交给用户使用。
但这样有一个问题,即darr_obj.c和darr_size.obj联系紧密,但
却是2个文件,不便于维护、管理和使用。这时最好考虑使用库文
件,将darr_obj.c和darr_size.obj打包成darr.lib.如c语言的标
准库函数象printf,scanf等就只能看到声明,而找不到实现文件,
原因就是是实现代码已经编译成二进制形式并生成了库文件。

5. 对于int _Cdecl printf(const char *format, ...),
nothingneed(玄痴)给出了很好的例子,这里再补充几点:
printf函数声明中的_Cdecl是很重要的,它表明采用C语言的
函数调用方式,其它调用方式如pascal是不能有不确定数目的
参数的;va_arg()的作用是根据前面的参数找到下一个参数,
前提是要知道下一个参数的类型。如printf("%d %f\n",i,f),
我们可以想象printf函数内部是根据类型首先取出整型的i打
印出来,然后是双精度型浮点型的f;那么它又是怎样知道我们
传进去的是一个int型和一个double型的参数呢?这要归功于
printf函数的第一个固定参数char *fmt,本例中,根据"%d %f"
格式化字符串完全可以知道后面所有参数的类型。