问个小程序:C陷阱与缺陷上的

昵称很不好取 2009-09-14 09:50:16
int main()
{

int c;
char buf[BUFSIZ];
setbuf(stdout, buf);

while((c=getchar())!=EOF)
putchar(c);

return 0;
}

下面是它的解释,我没看懂,请教下说的是什么意思?
--遗憾的是,这个程序是错误的,仅仅是因为一个细微的原因。程序中对库函数setbuf的调用,通知了输入/输出库所有字符的标准输出应该首先缓存在buf中。要找到问题出自何处,我们不妨思考一下buf缓冲区最后一次被清空是在什么时候?答案是在main函数结束之后,作为程序交回控制给操作系统之前C运行时库所必须进行的清理工作的一部分。但是,在此之前buf字符数组已经被释放!
...全文
452 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
lvchun012 2009-09-14
  • 打赏
  • 举报
回复
标准 C I/O
clearerr
语法:

#include <stdio.h>
void clearerr( FILE *stream );

clearerr函数重置错误标记和给出的流的EOF指针. 当发生错误时,你可以使用perror()判断实际上发生了何种错误.
相关主题:
feof(), ferror(), 和perror().
fclose
语法:

#include <stdio.h>
int fclose( FILE *stream );

函数fclose()关闭给出的文件流, 释放已关联到流的所有缓冲区. fclose()执行成功时返回0,否则返回EOF.
相关主题:
fopen(), freopen(), 和fflush().
feof
语法:

#include <stdio.h>
int feof( FILE *stream );

函数feof()在到达给出的文件流的文件尾时返回一个非零值.
相关主题:
clearerr(), ferror(), perror(), putc()和 getc().
ferror
语法:

#include <stdio.h>
int ferror( FILE *stream );

ferror()函数检查stream(流)中的错误, 如果没发生错误返回0,否则返回非零. 如果发生错误, 使用perror()检测发生什么错误.
相关主题:
clearerr(), feof(), perror(),
fflush
语法:

#include <stdio.h>
int fflush( FILE *stream );

如果给出的文件流是一个输出流,那么fflush()把输出到缓冲区的内容写入文件. 如果给出的文件流是输入类型的,那么fflush()会清除输入缓冲区. fflush()在调试时很实用,特别是对于在程序中输出到屏幕前发生错误片段时. 直接调用 fflush( STDOUT )输出可以保证你的调试输出可以在正确的时间输出.

printf( "Before first call\n" );
fflush( STDOUT );
shady_function();
printf( "Before second call\n" );
fflush( STDOUT );
dangerous_dereference();

相关主题:
fclose(), fopen(), fread(), fwrite(), getc(), 和putc().
fgetc
语法:

#include <stdio.h>
int fgetc( FILE *stream );

fgetc()函数返回来自stream(流)中的下一个字符,如果到达文件尾或者发生错误时返回EOF.
相关主题:
fputc(), getc(), putc(), 和fopen().
fgetpos
语法:

#include <stdio.h>
int fgetpos( FILE *stream, fpos_t *position );

fgetpos()函数保存给出的文件流(stream)的位置指针到给出的位置变量(position)中. position变量是fpos_t类型的(它在stdio.h中定义)并且是可以控制在FILE中每个可能的位置对象. fgetpos()执行成功时返回0,失败时返回一个非零值.
相关主题:
fsetpos(), fseek()和 ftell().
fgets
语法:

#include <stdio.h>
char *fgets( char *str, int num, FILE *stream );

函数fgets()从给出的文件流中读取[num - 1]个字符并且把它们转储到str(字符串)中. fgets()在到达行末时停止,在这种情况下,str(字符串)将会被一个新行符结束. 如果fgets()达到[num - 1]个字符或者遇到EOF, str(字符串)将会以null结束.fgets()成功时返回str(字符串),失败时返回NULL.
fopen
语法:

#include <stdio.h>
FILE *fopen( const char *fname, const char *mode );

fopen()函数打开由fname(文件名)指定的文件, 并返回一个关联该文件的流.如果发生错误, fopen()返回NULL. mode(方式)是用于决定文件的用途(例如 用于输入,输出,等等)
Mode(方式) 意义
"r" 打开一个用于读取的文本文件
"w" 创建一个用于写入的文本文件
"a" 附加到一个文本文件
"rb" 打开一个用于读取的二进制文件
"wb" 创建一个用于写入的二进制文件
"ab" 附加到一个二进制文件
"r+" 打开一个用于读/写的文本文件
"w+" 创建一个用于读/写的文本文件
"a+" 打开一个用于读/写的文本文件
"rb+" 打开一个用于读/写的二进制文件
"wb+" 创建一个用于读/写的二进制文件
"ab+" 打开一个用于读/写的二进制文件

示例:

char ch;
FILE *input = fopen( "stuff", "r" );
ch = getc( input );

fprintf
语法:

#include <stdio.h>
int fprintf( FILE *stream, const char *format, ... );

fprintf()函数根据指定的format(格式)(格式)发送信息(参数)到由stream(流)指定的文件. fprintf()只能和printf()一样工作. fprintf()的返回值是输出的字符数,发生错误时返回一个负值.

示例:

char name[20] = "Mary";
FILE *out;
out = fopen( "output.txt", "w" );
if( out != NULL )
fprintf( out, "Hello %s\n", name );

相关主题:
printf() 和fscanf().
fputc
语法:

#include <stdio.h>
int fputc( int ch, FILE *stream );

函数fputc()把给出的字符ch写到给出的输出流. 返回值是字符, 发生错误时返回值是EOF.
相关主题:
fgetc(), fopen(), fprintf(), fread(), 和fwrite().
fputs
语法:

#include <stdio.h>
int fputs( const char *str, FILE *stream );

fputs()函数把str(字符串)指向的字符写到给出的输出流. 成功时返回非负值, 失败时返回EOF.
相关主题:
fgets(), gets(), puts(), fprintf(), 和fscanf().
fread
语法:

#include <stdio.h>
int fread( void *buffer, size_t size, size_t num, FILE *stream );

函数fread()读取[num]个对象(每个对象大小为size(大小)指定的字节数),并把它们替换到由buffer(缓冲区)指定的数组. 数据来自给出的输入流. 函数的返回值是读取的内容数量...

使用feof()或ferror()判断到底发生哪个错误.
相关主题:
fwrite(), fopen(),fscanf(), fgetc()和getc().
freopen
语法:

#include <stdio.h>
FILE *freopen( const char *fname, const char *mode, FILE *stream );

freopen()函数常用于再分配一个以存在的流给一个不同的文件和方式(mode).在调用本函数后,给出的文件流将会用mode(方式)指定的访问模式引用fname(文件名). freopen()的返回值是新的文件流,发生错误时返回NULL.
相关主题:
fopen() 和fclose().
fscanf
语法:

#include <stdio.h>
int fscanf( FILE *stream, const char *format, ... );

函数fscanf()以scanf()的执行方式从给出的文件流中读取数据. fscanf()的返回值是事实上已赋值的变量的数,如果未进行任何分配时返回EOF.
相关主题:
scanf() 和fprintf().
fseek
语法:

#include <stdio.h>
int fseek( FILE *stream, long offset, int origin );

函数fseek()为给出的流设置位置数据. origin的值应该是下列值其中之一(在stdio.h中定义):
名称 说明
SEEK_SET 从文件的开始处开始搜索
SEEK_CUR 从当前位置开始搜索
SEEK_END 从文件的结束处开始搜索

fseek()成功时返回0,失败时返回非零. 你可以使用fseek()移动超过一个文件,但是不能在开始处之前. 使用fseek()清除关联到流的EOF标记.
相关主题:
ftell(), rewind(), fopen(), fgetpos()和 fsetpos().
fsetpos
语法:

#include <stdio.h>
int fsetpos( FILE *stream, const fpos_t *position );

fsetpos()函数把给出的流的位置指针移到由position对象指定的位置. fpos_t是在stdio.h中定义的. fsetpos()执行成功返回0,失败时返回非零.
相关主题:
fgetpos(), fseek()和 ftell().
ftell
语法:

#include <stdio.h>
long ftell( FILE *stream );

ftell()函数返回stream(流)当前的文件位置,如果发生错误返回-1.
相关主题:
fseek()和 fgetpos().
fwrite
语法:

#include <stdio.h>
int fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

fwrite()函数从数组buffer(缓冲区)中, 写count个大小为size(大小)的对象到stream(流)指定的流. 返回值是已写的对象的数量.
相关主题:
fread(), fscanf(), getc()和 fgetc().
getc
语法:

#include <stdio.h>
int getc( FILE *stream );

getc()函数从stream(流)获取并返回下一个字符,如果到达文件尾返回EOF. getc()和fgetc()是一样的. 例如:

char ch;
FILE *input = fopen( "stuff", "r" );

ch = getc( input );
while( ch != EOF ) {
printf( "%c", ch );
ch = getc( input );
}

相关主题:
fputc(), fgetc(), putc()和 fopen().
getchar
语法:

#include <stdio.h>
int getchar( void );

getchar()函数从STDIN(标准输入)获取并返回下一个字符,如果到达文件尾返回EOF.
相关主题:
fputc(), fgetc(), putc()和 fopen().
gets
语法:

#include <stdio.h>
char *gets( char *str );

gets()函数从STDIN(标准输入)读取字符并把它们加载到str(字符串)里,直到遇到新行(\n)或到达EOF. 新行字符翻译为一个null中断符. gets()的返回值是读入的字符串,如果错误返回NULL.
相关主题:
fputs(), fgetc(),fgets()和 puts().
perror
语法:

#include <stdio.h>
void perror( const char *str );

perror()函数打印str(字符串)和一个相应的执行定义的错误消息到全局变量errno中.
printf
语法:

#include <stdio.h>
int printf( const char *format, ... );

printf()函数根据format(格式)给出的格式打印输出到STDOUT(标准输出)和其它参数中.

字符串format(格式)由两类项目组成 - 显示到屏幕上的字符和定义printf()显示的其它参数. 基本上, 你可以指定一个包含文本在内的format(格式)字符串,也可以是映射到printf()其它参数的"特殊"字符. 例如本代码

char name[20] = "Bob";
int age = 21;
printf( "Hello %s, you are %d years old\n", name, age );

显示下列输出:

Hello Bob, you are 21 years old

%s 表示, "在这里插入首个参数,一个字符串." %d 表示第二个参数(一个整数)应该放置在那里. 不同的"%-codes"表示不同的变量类型, 也可以限制变量的长度.
Code 格式
%c 字符
%d 带符号整数
%i 带符号整数
%e 科学计数法, 使用小写"e"
%E 科学计数法, 使用大写"E"
%f 浮点数
%g 使用%e或%f中较短的一个
%G 使用%E或%f中较短的一个
%o 八进制
%s 一串字符
%u 无符号整数
%x 无符号十六进制数, 用小写字母
%X 无符号十六进制数, 用大写字母
%p 一个指针
%n 参数应该是一个指向一个整数的指针
指向的是字符数放置的位置
%% 一个'%'符号

一个位于一个%和格式化命令间的整数担当着一个最小字段宽度说明符,并且加上足够多的空格或0使输出足够长. 如果你想填充0,在最小字段宽度说明符前放置0. 你可以使用一个精度修饰符,它可以根据使用的格式代码而有不同的含义.

* 用%e, %E和 %f,精度修饰符让你指定想要的小数位数. 例如,

%12.6f

将会至少显示12位数字,并带有6位小数的浮点数.
* 用%g和 %G, 精度修饰符决定显示的有效数的位数最大值.
* 用%s,精度修饰符简单的表示一个最大的最大长度, 以补充句点前的最小字段长度.

所有的printf()的输出都是右对齐的,除非你在%符号后放置了负号. 例如,

%-12.4f

将会显示12位字符,4位小数位的浮点数并且左对齐. 你可以修改带字母l和h%d, %i, %o, %u和 %x 等类型说明符指定长型和短型数据类型 (例如 %hd 表示一个短整数). %e, %f和 %g 类型说明符,可以在它们前面放置l指出跟随的是一个double. %g, %f和 %e 类型说明符可以置于字符'#'前保证出现小数点, 即使没有小数位. 带%x类型说明符的'#'字符的使用, 表示显示十六进制数时应该带'0x'前缀. 带%o类型说明符的'#'字符的使用, 表示显示八进制数时应该带一个'0'前缀.

你可以在输出字符串中包含 连续的Escape序列.

printf()的返回值是打印的字符数,如果发生错误则返回一个负值.
相关主题:
scanf()和 fprintf().
putc
语法:

#include <stdio.h>
int putc( int ch, FILE *stream );

putc()函数把字符ch写到stream(流)中. 返回值是写入的字符, 发生错误时返回EOF. 例如:

char ch;
FILE *input;
input = fopen( "temp.cpp", "r" );
ch = getc( input );
while( ch != EOF ) {
printf( "%c", ch );
ch = getc( input );
}

显示"temp.cpp"的内容到屏幕.
相关主题:
fgetc(), fputc(), getchar()和 putchar().
putchar
语法:

#include <stdio.h>
int putchar( int ch );

putchar()函数把ch写到STDOUT(标准输出). 代码

putchar( ch );



putc( ch, STDOUT );

一样.

putchar()的返回值是被写的字符, 发生错误时返回EOF.
相关主题:
putc()
puts
语法:

#include <stdio.h>
int puts( char *str );

函数puts()把str(字符串)写到STDOUT(标准输出)上. puts() 成功时返回非负值, 失败时返回EOF.
相关主题:
putc(), gets()和 printf().
remove
语法:

#include <stdio.h>
int remove( const char *fname );

remove()函数删除由fname(文件名)指定的文件. remove()成功时返回0,如果发生错误返回非零.
相关主题:
rename()
rename
语法:

#include <stdio.h>
int rename( const char *oldfname, const char *newfname );

函数rename()更改文件oldfname的名称为newfname. rename()成功时返回0,错误时返回非零.
lvchun012 2009-09-14
  • 打赏
  • 举报
回复
一般全局变量存放在数据区,局部变量存放在栈区,
动态变量存放在堆区,函数代码放在代码区。
---------------------------------------------------------------

栈区是普通的栈数据结构,遵循LIFO后进先出的规则,局部变量安排在那里是ASM时就规定的,这样可以在一个函数结束后平衡堆栈,操作简单,效率高
堆(动态区)在这里应当叫堆栈(不要和数据结构中的堆搞混)是程序在编译时产生的一块用于产生动态内存分配使用的块,操作比较栈要麻烦许多,在分配时要判断最优的地址(防止产生无用的内存碎片(由于屡次的NEW和DELETE产生的夹在两块使用中内存中的空余小内存(不容易被分配))),分配和回收时的效率比栈低多了
---------------------------------------------------------------

栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而栈是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率 >有一定降低。栈是系统数据结构,对于进程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。栈空间分静态分配和动态分配两种。静态分配是编译器完成的,比如自动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配无需释放(是自动的),也就没有释放函数。为可移植>的程序起见,栈的动态分配操作是不被鼓励的!堆空间的分配总是动态的,虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存/ 释放内存匹>配是良好程序的基本要素。
这是我对堆与栈收藏内容~
---------------------------------------------------------------

堆是程序员管理的,栈是系统管理的.
---------------------------------------------------------------
---------------------------------------------------------------
---------------------------------------------------------------
另外关于静态和全局的一些问题:
静态变量的特点:

1、 一次存储:静态局部变量只被初始化一次,下一次初始化根据上一次的结果值,有点类似于c++中类的静态成员变量,即无论该类型生成多少个实例对象,所有的对象共用一个静态变量,到这里就是无论这个函数调用多少次,该静态变量只初始化一次,并没有因为超出其生存期而被销毁,只是外部不可见而已,用个例子说明之:
void fun1( int v )
{
static int value = v;
static int value = v;
}
int main( int arc, char *args[ ])
{
fun1( 50 );
fun1( 100 );
}

执行的结果是:value : 50 value : 50
说明在第二次调用fun1( )时的初始化value的采用的是上一次value的值,value在静态区的存储空间并没有因为fun1( )的结束而被释放,即体现了一次存储;

2、 作用域限定:静态修饰的作用域限定功能同时体现在函数与变量上;

a) 对于函数而言,任何用static修饰的函数,其作用域仅为当前源文件,而对外部来说这个函数是不可见的,即只有和其在同一源文件中的函数才能调用这个静态函数;反过来说,如果一个函数仅仅被同一源文件中的其他函数调用,那么这个函数应该声明为静态的,这样做的好处在于:可以一定程度上的解决不同源文件之间函数的命名冲突问题;

b) 对于变量而言,static修饰的全局变量,只在当前源文件中有效,对外部不可见,外部文件不能够引用;

顾名思义,全局变量是指能够在全局引用的变量,相对于局部变量的概念,也叫外部变量;同静态变量一样,全局变量位于静态数据区,全局变量一处定义,多处引用,用关键字“extern”引用“外部”的变量。

全局变量也可以是静态的,在前面有过说明,静态全局变量的意义就是不让“外部”引用,是单个源文件里的全局变量,即是编译阶段的全局变量,而不是连接阶段的全局变量。

通过上面的分析,我们不难得出以下结论:

1、 静态函数与普通函数的区别在于:静态函数不可以被同一源文件以外的函数调用。

2、 静态局部变量与普通局部变量的区别在于:静态局部变量只初始化一次,下一次初始化实际上是依然是上一次的变量;

3、 静态全局变量与普通全局变量的区别在于:静态全局变量的作用域仅限于所在的源文件

////////////////////////////////////////////////////////////////////////////////////////////

堆栈
在计算机领域,堆栈是一个不容忽视的概念,但是很多人甚至是计算机专业的人也没有明确堆栈其实是两种数据结构。

要点:

堆:顺序随意

栈:先进后出

堆和栈的区别

一、预备知识—程序的内存分配

一个由c/C++编译的程序占用的内存分为以下几个部分

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放

4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放

5、程序代码区—存放函数体的二进制代码。

二、例子程序

这是一个前辈写的,非常详细

//main.cpp

int a = 0; 全局初始化区

char *p1; 全局未初始化区

main()

{

int b; 栈

char s[] = "abc"; 栈

char *p2; 栈

char *p3 = "123456"; 123456\0在常量区,p3在栈上。

static int c =0; 全局(静态)初始化区

p1 = (char *)malloc(10);

p2 = (char *)malloc(20);

分配得来得10和20字节的区域就在堆区。

strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。

}

二、堆和栈的理论知识

2.1申请方式

stack:

由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间

heap:

需要程序员自己申请,并指明大小,在c中malloc函数

如p1 = (char *)malloc(10);

在C++中用new运算符

如p2 = (char *)malloc(10);

但是注意p1、p2本身是在栈中的。

2.2

申请后系统的响应

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,

会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
2.3申请大小的限制

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

2.4申请效率的比较:

栈由系统自动分配,速度较快。但程序员是无法控制的。

堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.

另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活

2.5堆和栈中的存储内容

栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。

当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

2.6存取效率的比较

char s1[] = "aaaaaaaaaaaaaaa";

char *s2 = "bbbbbbbbbbbbbbbbb";

aaaaaaaaaaa是在运行时刻赋值的;

而bbbbbbbbbbb是在编译时就确定的;

但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。

比如:

#include

void main()

{

char a = 1;

char c[] = "1234567890";

char *p ="1234567890";

a = c[1];

a = p[1];

return;

}

对应的汇编代码

10: a = c[1];

00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]

0040106A 88 4D FC mov byte ptr [ebp-4],cl

11: a = p[1];

0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]

00401070 8A 42 01 mov al,byte ptr [edx+1]

00401073 88 45 FC mov byte ptr [ebp-4],al

第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。

?
2.7小结:

堆和栈的区别可以用如下的比喻来看出:

使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。

使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

堆和栈的区别主要分:

操作系统方面的堆和栈,如上面说的那些,不多说了。

还有就是数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;栈实际上就是满足先进后出的性质的数学或数据结构。

虽然堆栈,堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因
lvchun012 2009-09-14
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 thefirstz 的回复:]
to:6楼
to:4楼
请教你们下,现在有点乱?
1,那为什么把数组buf声明为static数组就可以解决此问题?——static数组在main函数返回后不一样清空吗?
2,为什么不用setbuf数组putchar就可以实现输入一个字符打印一个字符,C语言不也是默认有缓冲的吗?还是putchar默认就已经调用了fflush这个函数?
[/Quote]

1、在C或C++(其他的应该也是类似的),全局变量存放在数据区,而局部变量存放在堆栈中,生命周期随申明空间的结束而结束,static类型的变量也存放在数据区,基本上相当于全局变量使用,所以它跟main函数的结束没有关系;
2、setbuf()函数设置stream(流)使用buffer(缓冲区),如果buffer(缓冲区)是null,关闭缓冲. 如果使用非标准缓冲尺寸, 它应该由BUFSIZ字符决定长度.
3、建议楼主多看看C内存处理的资料
zhtigt 2009-09-14
  • 打赏
  • 举报
回复
XUEXILA
lvlangx 2009-09-14
  • 打赏
  • 举报
回复
不太明白怎么回事。
lvchun012 2009-09-14
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 thefirstz 的回复:]
引用 2 楼 lvchun012 的回复:
setbuf() 用来设置缓冲区特性,如果需要改变缓冲的特点和大小等,使用该调用
你的buf数组的内存没有清空,所以在执行的时候会出现问题,而buf数组是在main结束时才去清空,但是在main结束之前,局部变量的指针空间已经被释放,所以会出现内存的泄露

还是不太明白,你说的局部变量的指针空间是指什么?哪来的局部变量?
[/Quote]
在main函数中,buf本来就是个局部变量,一个函数申明的所有局部变量会在函数结束前遇到"}"符号时释放内存空间,所以buf在遇到main函数的}时候,内存被释放了,成了野指针。
challenge99 2009-09-14
  • 打赏
  • 举报
回复
楼上两位解释的很好, 学习了~~~
jinwei1984 2009-09-14
  • 打赏
  • 举报
回复
BUGS
The setbuffer() and setlinebuf() functions are not portable to versions
of BSD before 4.2BSD, and are available under Linux since libc 4.5.21.
On 4.2BSD and 4.3BSD systems, setbuf() always uses a suboptimal buffer
size and should be avoided.

You must make sure that the space that buf points to still exists by
the time stream is closed, which also happens at program termination.

For example, the following is illegal:

#include <stdio.h>

int
main(void)
{
char buf[BUFSIZ];
setbuf(stdin, buf);
printf("Hello, world!\n");
return 0;
}


其实这里的关键是main函数并不是程序执行时的第一个函数
以前看过minix的,os会执行一些初始化工作才开始执行main函数

buf是main中的局部变量,退出main后,os还会做一些清理工作
根据我的猜测,这里的清理工作就包括关闭流这些
You must make sure that the space that buf points to still exists by
the time stream is closed
main退出的时候buf不存在了,正好符合上面这句话
所以必须把buf改为全局或者static,这样buf就存放在数据区(段)了,不是在栈上
昵称很不好取 2009-09-14
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 brookmill 的回复:]
普通的局部变量,是保存在main函数的栈区,main结束的时候就会释放(不是清空)。
static的变量,和全局变量保存在一起,main结束的时候仍然有效,直到程序退出才失效。
从main结束,到程序退出,其间有一个过程,我们现在讨论的就是在这个短暂的过程中可能发生的事情。
[/Quote]
多谢多谢,这个我明白了
再请教下你关于缓冲区的问题,现在还是有点不明白了,就是如果使用setbuf函数,那么会把保存在static数组中的东西全部打印出,那为什么不用setbuf函数就可以一个字母一个字母的打印出来?C语言默认的缓冲区跟缓冲到数组不是一样的吗?
昵称很不好取 2009-09-14
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 brookmill 的回复:]
static数组在main函数返回后  不 清空
[/Quote]
谢谢你的回复,static数组在main函数返回后不清空,那何时清空?
brookmill 2009-09-14
  • 打赏
  • 举报
回复
普通的局部变量,是保存在main函数的栈区,main结束的时候就会释放(不是清空)。
static的变量,和全局变量保存在一起,main结束的时候仍然有效,直到程序退出才失效。
从main结束,到程序退出,其间有一个过程,我们现在讨论的就是在这个短暂的过程中可能发生的事情。
brookmill 2009-09-14
  • 打赏
  • 举报
回复
c语言的默认缓冲,在main结束时不会被释放
brookmill 2009-09-14
  • 打赏
  • 举报
回复
static数组在main函数返回后 不 清空
昵称很不好取 2009-09-14
  • 打赏
  • 举报
回复
to:6楼
to:4楼
请教你们下,现在有点乱?
1,那为什么把数组buf声明为static数组就可以解决此问题?——static数组在main函数返回后不一样清空吗?
2,为什么不用setbuf数组putchar就可以实现输入一个字符打印一个字符,C语言不也是默认有缓冲的吗?还是putchar默认就已经调用了fflush这个函数?
昵称很不好取 2009-09-14
  • 打赏
  • 举报
回复
to:6楼
to:4楼
请教你们下,现在有点乱?
1,那为什么把数组buf声明为static数组就可以解决此问题?——static数组在main函数返回后不一样清空吗?
2,为什么不用setbuf数组putchar就可以实现输入一个字符打印一个字符,C语言不也是默认有缓冲的吗?还是putchar默认就已经调用了fflush这个函数?
james_hw 2009-09-14
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 brookmill 的回复:]
所谓“buf缓冲区最后一次被清空”,是指putchar函数放到buf里面的所有字符都真正输出到stdout
问题是,这个事情发生在main函数返回之后,那时候buf那块内存有可能被用作其他用途,其中的内容有可能已经被其他部分的程序改动了。
[/Quote]

顶这个解释
brookmill 2009-09-14
  • 打赏
  • 举报
回复
buf就是局部变量呀,main返回之后,buf里面的内容就不可靠了。
如果main返回之前那些内容还没来得及真正输出,那么就有可能丢失了
昵称很不好取 2009-09-14
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 lvchun012 的回复:]
setbuf() 用来设置缓冲区特性,如果需要改变缓冲的特点和大小等,使用该调用
你的buf数组的内存没有清空,所以在执行的时候会出现问题,而buf数组是在main结束时才去清空,但是在main结束之前,局部变量的指针空间已经被释放,所以会出现内存的泄露
[/Quote]
还是不太明白,你说的局部变量的指针空间是指什么?哪来的局部变量?
brookmill 2009-09-14
  • 打赏
  • 举报
回复
所谓“buf缓冲区最后一次被清空”,是指putchar函数放到buf里面的所有字符都真正输出到stdout
问题是,这个事情发生在main函数返回之后,那时候buf那块内存有可能被用作其他用途,其中的内容有可能已经被其他部分的程序改动了。
  • 打赏
  • 举报
回复
顶一贴!
加载更多回复(2)

69,379

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧