大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
如果你安装的Visual Studio,以及它的Visual C++的话,
创新互联是网站建设技术企业,为成都企业提供专业的成都网站设计、成都做网站,网站设计,网站制作,网站改版等技术服务。拥有10余年丰富建站经验和众多成功案例,为您定制适合企业的网站。10余年品质,值得信赖!
那么在安装目录下的VC/crt/src下有所有标准C库的源代码
另外,h后缀的头文件包含函数的声明,具体的实现都在c后缀的源码文件中
有安装vs2008或2010吗,在安装目录下面的VC/src中自带有源代码。比如我的就在
D:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src中。没有的话发给你
很遗憾,标准库中的函数结合了系统,硬件等的综合能力,是比较近机器的功能实现,所以大部分是用汇编完成的,而且已经导入到了lib和dll里了,就是说,他们已经被编译好了,似乎没有代码的存在了.
能看到的也只有dll中有多少函数被共享.
第三方可能都是dll,因为上面也说了,dll是编译好的,只能看到成品,就可以隐藏代码,保护自己的知识产权,同时也是病毒的归宿...... 当然,除了DLL的确还存在一种东西,插件程序~~~
void __fileDECL qsort (
void *base,
size_t num,
size_t width,
int (__fileDECL *comp)(const void *, const void *)
)
#endif /* __USE_CONTEXT */
{
char *lo, *hi; /* ends of sub-array currently sorting */
char *mid; /* points to middle of subarray */
char *loguy, *higuy; /* traveling pointers for partition step */
size_t size; /* size of the sub-array */
char *lostk[STKSIZ], *histk[STKSIZ];
int stkptr; /* stack for saving sub-array to be processed */
/* validation section */
_VALIDATE_RETURN_VOID(base != NULL || num == 0, EINVAL);
_VALIDATE_RETURN_VOID(width 0, EINVAL);
_VALIDATE_RETURN_VOID(comp != NULL, EINVAL);
if (num 2)
return; /* nothing to do */
stkptr = 0; /* initialize stack */
lo = (char *)base;
hi = (char *)base + width * (num-1); /* initialize limits */
/* this entry point is for pseudo-recursion calling: setting
lo and hi and jumping to here is like recursion, but stkptr is
preserved, locals aren't, so we preserve stuff on the stack */
recurse:
size = (hi - lo) / width + 1; /* number of el's to sort */
/* below a certain size, it is faster to use a O(n^2) sorting method */
if (size = CUTOFF) {
__SHORTSORT(lo, hi, width, comp, context);
}
else {
/* First we pick a partitioning element. The efficiency of the
algorithm demands that we find one that is approximately the median
of the values, but also that we select one fast. We choose the
median of the first, middle, and last elements, to avoid bad
performance in the face of already sorted data, or data that is made
up of multiple sorted runs appended together. Testing shows that a
median-of-three algorithm provides better performance than simply
picking the middle element for the latter case. */
mid = lo + (size / 2) * width; /* find middle element */
/* Sort the first, middle, last elements into order */
if (__COMPARE(context, lo, mid) 0) {
swap(lo, mid, width);
}
if (__COMPARE(context, lo, hi) 0) {
swap(lo, hi, width);
}
if (__COMPARE(context, mid, hi) 0) {
swap(mid, hi, width);
}
/* We now wish to partition the array into three pieces, one consisting
of elements = partition element, one of elements equal to the
partition element, and one of elements than it. This is done
below; comments indicate conditions established at every step. */
loguy = lo;
higuy = hi;
/* Note that higuy decreases and loguy increases on every iteration,
so loop must terminate. */
for (;;) {
/* lo = loguy hi, lo higuy = hi,
A[i] = A[mid] for lo = i = loguy,
A[i] A[mid] for higuy = i hi,
A[hi] = A[mid] */
/* The doubled loop is to avoid calling comp(mid,mid), since some
existing comparison funcs don't work when passed the same
value for both pointers. */
if (mid loguy) {
do {
loguy += width;
} while (loguy mid __COMPARE(context, loguy, mid) = 0);
}
if (mid = loguy) {
do {
loguy += width;
} while (loguy = hi __COMPARE(context, loguy, mid) = 0);
}
/* lo loguy = hi+1, A[i] = A[mid] for lo = i loguy,
either loguy hi or A[loguy] A[mid] */
do {
higuy -= width;
} while (higuy mid __COMPARE(context, higuy, mid) 0);
/* lo = higuy hi, A[i] A[mid] for higuy i hi,
either higuy == lo or A[higuy] = A[mid] */
if (higuy loguy)
break;
/* if loguy hi or higuy == lo, then we would have exited, so
A[loguy] A[mid], A[higuy] = A[mid],
loguy = hi, higuy lo */
swap(loguy, higuy, width);
/* If the partition element was moved, follow it. Only need
to check for mid == higuy, since before the swap,
A[loguy] A[mid] implies loguy != mid. */
if (mid == higuy)
mid = loguy;
/* A[loguy] = A[mid], A[higuy] A[mid]; so condition at top
of loop is re-established */
}
/* A[i] = A[mid] for lo = i loguy,
A[i] A[mid] for higuy i hi,
A[hi] = A[mid]
higuy loguy
implying:
higuy == loguy-1
or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */
/* Find adjacent elements equal to the partition element. The
doubled loop is to avoid calling comp(mid,mid), since some
existing comparison funcs don't work when passed the same value
for both pointers. */
higuy += width;
if (mid higuy) {
do {
higuy -= width;
} while (higuy mid __COMPARE(context, higuy, mid) == 0);
}
if (mid = higuy) {
do {
higuy -= width;
} while (higuy lo __COMPARE(context, higuy, mid) == 0);
}
/* OK, now we have the following:
higuy loguy
lo = higuy = hi
A[i] = A[mid] for lo = i = higuy
A[i] == A[mid] for higuy i loguy
A[i] A[mid] for loguy = i hi
A[hi] = A[mid] */
/* We've finished the partition, now we want to sort the subarrays
[lo, higuy] and [loguy, hi].
We do the smaller one first to minimize stack usage.
We only sort arrays of length 2 or more.*/
if ( higuy - lo = hi - loguy ) {
if (lo higuy) {
lostk[stkptr] = lo;
histk[stkptr] = higuy;
++stkptr;
} /* save big recursion for later */
if (loguy hi) {
lo = loguy;
goto recurse; /* do small recursion */
}
}
else {
if (loguy hi) {
lostk[stkptr] = loguy;
histk[stkptr] = hi;
++stkptr; /* save big recursion for later */
}
if (lo higuy) {
hi = higuy;
goto recurse; /* do small recursion */
}
}
}
/* We have sorted the array, except for any pending sorts on the stack.
Check if there are any, and do them. */
--stkptr;
if (stkptr = 0) {
lo = lostk[stkptr];
hi = histk[stkptr];
goto recurse; /* pop subarray from stack */
}
else
return; /* all subarrays done */
}
深入printf
/***
*printf.c - print formatted
*
* Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
* defines printf() - print formatted data
*
*******************************************************************************/
#include
#include
#include
#include
#include
#include
#include
/***
*int printf(format, ...) - print formatted data
*
*Purpose:
* Prints formatted data on stdout using the format string to
* format data and getting as many arguments as called for
* Uses temporary buffering to improve efficiency.
* _output does the real work here
*
*Entry:
* char *format - format string to control data format/number of arguments
* followed by list of arguments, number and type controlled by
* format string
*
*Exit:
* returns number of characters printed
*
*Exceptions:
*
*******************************************************************************/
int __cdecl printf (
const char *format,
...
)
/*
* stdout ''PRINT'', ''F''ormatted
*/
{
va_list arglist;
int buffing;
int retval;
va_start(arglist, format);
_ASSERTE(format != NULL);//断言宏。如果输出格式字符串指针为空,则在DEBUG版下断言,报告错误。
_lock_str2(1, stdout);
buffing = _stbuf(stdout);//stdout:指定输出到屏幕
retval = _output(stdout,format,arglist);
_ftbuf(buffing, stdout);
_unlock_str2(1, stdout);
return(retval);
}
以上为printf()的源代码
1、从含有可选参数函数中获得可选参数,以及操作这些参数
typedef char *va_list;
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
假定函数含有一个必选参数和多个可选参数,必选参数声明为普通数据类型,且能通过参数名来获得该变量的值。可选参数通过宏va_start、va_arg和va_end(定义在stdarg.h或varargs.h中)来进行操作,即通过设置指向第一个可选参数指针、返回当前参数、在返回参数后重新设置指针来操作所有的可选参数。
va_start:为获取可变数目参数的函数的参数提供一种便捷手段。设置arg_ptr为指向传给函数参数列表中的第一个可选参数的指针,且该参数必须是va_list类型。prev_param是在参数列表中第一个可选参数前的必选参数。
va_arg:返回由arg_ptr所指向的参数的值,且自增指向下一个参数的地址。type为当前参数的类型,用来计算该参数的长度,确定下一个参数的起始位置。它可以在函数中应用多次,直到得到函数的所有参数为止,但必须在宏va_start后面调用。
va_end:在获取所有的参数后,设置指针arg_ptr为NULL。
下面举例说明:
#include
#include
int average( int first, ... );
void main( void )
{
/* Call with 3 integers (-1 is used as terminator). */
printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );
/* Call with 4 integers. */
printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );
/* Call with just -1 terminator. */
printf( "Average is: %d\n", average( -1 ) );
}
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 );
}
返回值为:
Average is: 3
Average is: 8
Average is: 0
综上所述,在printf()函数中,可以只输出一个字符串,也可按照一定的形式输出含有多个可选参数的字符串信息。因此,首先就要通过这些宏来获取所有的可选参数。在上面的源码可以看出printf()中,只使用了宏at_start,将可选参数的首地址赋给了arglist。
2、锁定字符串及输出字符串到屏幕
#define _lock_str2(i,s) _lock_file2(i,s)
void __cdecl _lock_file2(int, void *);
#define _unlock_str2(i,s) _unlock_file2(i,s)
void __cdecl _unlock_file2(int, void *);
int __cdecl _stbuf(FILE *);
void __cdecl _ftbuf(int, FILE *);
int __cdecl _output(FILE *, const char *, va_list);
在output函数中,读取格式字符串中的每一个字符,然后对其进行处理,处理方式根据每一个字符所代表的意义来进行,如:普通字符直接利用函数WRITE_CHAR(ch, charsout);输出到控制台。
其中的主要部分是对转换说明符(d,c,s,f)的处理,现在将对其中的部分代码进行详细说明,这里只说明最基本的转换说明符,对这些须基本的转换说明符进行修饰的修饰符,程序中单独进行处理。下面是函数output()(output.c)部分源代码:
case ST_TYPE:
//表示当前处理的字符的类型为转换说明符。
...
switch (ch) {
//下面对参数的获取都是利用宏va_arg( va_list arg_ptr, type );来进行的。
case ''c'': {
//从参数表中获取单个字符,输出到缓冲字符串中,此时,type=int
buffer[0] = (char) get_int_arg(argptr); /* get char to print */
text = buffer;
textlen = 1; /* print just a single character */
}
break;
case ''s'': {
//从参数表中获取字符串,输出到缓冲字符串中,此时,type=char*
int i;
char *p; /* temps */
text = get_ptr_arg(argptr);
...
}
break;
case ''w'': {
//对宽字符进行处理
...
} /* case ''w'' */
break;
...
case ''e'':
case ''f'':
case ''g'': {
//对浮点数进行操作
...
#if !LONGDOUBLE_IS_DOUBLE
/* do the conversion */
if (flags FL_LONGDOUBLE) {
_cldcvt((LONGDOUBLE*)argptr, text, ch, precision, capexp);
va_arg(argptr, LONGDOUBLE);
//对长双精度型进行处理,此时,type=long double
}
else
#endif /* !LONGDOUBLE_IS_DOUBLE */
{
//对双精度型进行处理,此时,type=double
_cfltcvt((DOUBLE*)argptr, text, ch, precision, capexp);
va_arg(argptr, DOUBLE);
}
...
break;
//对整型变量处理
case ''d'':
case ''i'':
...
goto COMMON_INT;
case ''u'':
radix = 10;
goto COMMON_INT;
case ''p'':
...
goto COMMON_INT;
case ''o'':
...
注:对于浮点型double和long double,有相应的转换说明符(%f表示双精度型,%lf表示长双精度型),而float却没有。其中的原因是,在KRC下,float值用于表达式或用作参数前,会自动转换成double类型。而ANSI C一般不会自动把float转换成double。有些程序已假定其中的float参数会被转换成double,为了保护大量这样的程序,所有printf()函数的float参数还是被自动转换成double型。因此,在KRC或ANSI C下,都无需用特定的转换说明符来显示float型。
综上所述,转换说明符必须与待打印字符的类型。通常,用户有种选择。例如,如要打印一个int类型的值。则只可以使用%d,%x或%o。所有这些说明符都表示要打印一个int类型的值;它们只不过提供了一个数值的几种不同表示。类似一,可以用%f、%g和%e来表示double类型的值。但如果转换说明与类型不匹配,将会出现意想不到的结果。为什么呢?问题就在于C向函数传递信息的方式。
这个失败的根本细节与具体实现相关。它决定了系统中的参数以何方式传递。函数调用如下:
float n1;
double n2;
long n3;
long n4;
...
printf("%ld,%ld,%ld,%ld",n1,n2,n3,n4);
这个调用告诉计算机,要把变量n1,n2,n3和n4的值交给计算机,它把这些变量放进称作栈(stack)的内存区域中,来完成这一任务。计算机把这些值放进栈中,其根据是变量的类型而不是转换说明符,比如n1,把8个字节放入栈中(float被转换成double),类似地,为n2放了8字节,其后给n3和n4各放了4个字节。接着,控制的对象转移到printf();此函数从栈中读数,不过在这一过程中,它是在转换说明符的指导下,读取数值的。说明符%ld指定printf()应读4个字节(va_arg( va_list arg_ptr, type )中type=long),因此printf()读入栈中的4个字节,作为它的第一个值。但是这只是n1的前半部分,这个值被看成一个long整数。下一个说明符%ld读入4个字节,这正是n1的后半部分,这个值被看成第二个long整数。类似地,第三、第四次又读入n2的前后两部分。因此,尽管我们对n3和n4使用了正确的说明符,printf()仍然会产生错误。
这里也可以下载