,并无函数体,完整的函数好像放在与该头文件同名的一个.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文件和头文件,不需要再提供库的源代码。
你所使用的库函数的定义都放在.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()函数的时候,它里面的参数个数不是一定的,
是和前面的格式字符串有直接关系的,格式字符串中指定了要输出多少变量的值,后面就应该跟
随多少变量作为参数,所以这样的函数在定义的时候是不能将参数写死的。
但是厂商在提供它们开发的库函数等产品的时候,是不能将.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
/*清单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中的...,你说得意思我知道,但我考虑的是如果自已要写
一个不定参数的函数,若只写出出...的话,在函数体中怎么引用这些形参呢?
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
这几个贴子上去,我给你加更多的分!
谢谢你的回答,我已给你加了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.写这种函数,我也没实践过,你再问问高手吧,我也关注。
定义变量就是声明一个变量,包括对该变量类型的指定。对,象厂商所提供的库中的.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 );
}
下面是一个例子
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时找到头文件中的函数入口
但是我对你上面的几点回答有些异议,试解如下
1,声明和定义是不同的,一般声明写载头文件中,定义写在cpp/c中
但是inline函数例外
2,lib文件中放的是函数的入口地址,无论静态还是动态都会有这个文件
是为了在Link时找到头文件中的函数入口
#17
"谢谢你的回答,我已给你加了50分,"
不好意思,我怎么没收到?
不好意思,我怎么没收到?
#18
nothingneed(玄痴) GZCompiler(编译器):
已给你们加分了!
已给你们加分了!
#19
to GZCompiler(编译器):
谢谢你另一贴子上还有你94分
谢谢你另一贴子上还有你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. 用法:通常.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文件和头文件,不需要再提供库的源代码。
你所使用的库函数的定义都放在.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()函数的时候,它里面的参数个数不是一定的,
是和前面的格式字符串有直接关系的,格式字符串中指定了要输出多少变量的值,后面就应该跟
随多少变量作为参数,所以这样的函数在定义的时候是不能将参数写死的。
但是厂商在提供它们开发的库函数等产品的时候,是不能将.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
/*清单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中的...,你说得意思我知道,但我考虑的是如果自已要写
一个不定参数的函数,若只写出出...的话,在函数体中怎么引用这些形参呢?
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
这几个贴子上去,我给你加更多的分!
谢谢你的回答,我已给你加了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.写这种函数,我也没实践过,你再问问高手吧,我也关注。
定义变量就是声明一个变量,包括对该变量类型的指定。对,象厂商所提供的库中的.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 );
}
下面是一个例子
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时找到头文件中的函数入口
但是我对你上面的几点回答有些异议,试解如下
1,声明和定义是不同的,一般声明写载头文件中,定义写在cpp/c中
但是inline函数例外
2,lib文件中放的是函数的入口地址,无论静态还是动态都会有这个文件
是为了在Link时找到头文件中的函数入口
#17
"谢谢你的回答,我已给你加了50分,"
不好意思,我怎么没收到?
不好意思,我怎么没收到?
#18
nothingneed(玄痴) GZCompiler(编译器):
已给你们加分了!
已给你们加分了!
#19
to GZCompiler(编译器):
谢谢你另一贴子上还有你94分
谢谢你另一贴子上还有你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. 用法:通常.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"
格式化字符串完全可以知道后面所有参数的类型。