1. 如果对fopen函数的返回值不进行错误检查,可能会出现什么后果?
解析:
如果由于任何原因导致打开失败,函数的返回值将是NULL。当这个值传递给后续的I/O函数时,该函数就会失败。至于程序是否失败,则取决于编译器。如果程序并不终止,那么I/O操作可能会修改内存中某些不可预料的位置的内容。
2. 如果试图对一个从未打开过的流进行I/O操作,会发生什么情况?
解析:
程序将会失败,因为试图使用的FILE结构没有适当地初始化。某个不可预料的内存地址的内容可能会被修改。
3. 如果一个程序fclose调用失败,但程序并未对它的返回值进行错误检查,可能会出现什么后果?
解析:
fclose中的参数可能会因为fopen和fclose之间的一个程序bug而发生修改。而这个bug无疑将导致程序失败。在那些并不检查fopen函数返回值的程序中,fclose中的参数甚至可能是NULL。在任何一种情况下,fclose都将失败,而且程序很可能在fclose被调用之前很早便已终止。
4. 如果一个程序员在执行时它的标准输入已重定向到一个文件,程序如何检测到这个情况呢?
解析:
不同的操作系统提供不同的机制来检测这种重定向,但程序员通常并不需要知道输入来自于文件还是键盘。操作系统负责处理绝大多数与设备无关的输入操作的许多方面,剩余部分则由库I/O函数提供。对于绝大多数应用程序,程序从标准输入读取的方式相同,不管输入实际来自何处。
5. 如果调用fgets函数时使用一个长度为1的缓冲区,会发生什么呢?长度为2呢?
解析:
fgets无法把字符串读入到一个长度小于两字符的缓冲区,因为其中一个字符需要为NUL字节保留。长度为2时只能读入一个字符到缓冲区。
#include <stdio.h>
#include <stdlib.h>
#define ARR_LEN 20
int main( void ){
char buffer[ARR_LEN];
int buffer_size;
buffer_size = 1;
fgets( buffer, buffer_size, stdin );
if( *buffer == '\0' ){
printf( "there is an only \'\\0\'.\n" );
} else{
printf( "there are more one character, buffer = %s\n", buffer );
}
buffer_size = 2;
fgets( buffer, buffer_size, stdin );
if( *buffer == '\0' ){
printf( "there is a only \'\0\'.\n" );
} else{
printf( "there are more one character, buffer = %s\n", buffer );
}
return EXIT_SUCCESS;
}
输出:
6. 为了保证下面这条sprintf语句所产生的字符串不溢出,缓冲区至少应该有多大?假定你的机器的上整数的长度为2字节。
sprintf( buffer, "%d %c %x", a, b, c );
解析:
缓冲区的长度至少应该为:有符号整数a中的数字个数+字符b的个数+ 无符号整数c中的数字个数+每个空格字符的个数+'\0'字符的个数,这个办法不会浪费内存空间
更简单的操作就是INT_MAX中的数字个数+字符b的个数+UINT_MAX中的数字个数+每个空格字符的个数+'\0'字符的个数,不过这个办法会浪费一定的内存空间。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
int strlen_dec_int( int n );
int strlen_hex_int( int n );
int strlen_char( char c );
int main( void ){
int total;
int a;
char b;
int c;
total = 0;
a = 123;
b = 'a';
c = 0xa;
printf( "c = %d\n", c );
total += strlen_dec_int( a );
total += strlen_char( b );
total += strlen_char( (char)' ' );
total += strlen_char( (char)' ' );
total += strlen_char( (char)'\0' );
total += strlen_hex_int( c );
printf( "total = %d\n", total );
char buffer[total];
buffer[total - 1] = '\0';
sprintf( buffer, "%d %c %x", a, b, c );
int buf_len;
buf_len = strlen( buffer );
printf( "buf_len = %d, buffer = %s\n", buf_len, buffer );
if( *(buffer + total - 1) == '\0' ){
printf( "true!\n" );
} else{
printf( "false!\n" );
}
return EXIT_SUCCESS;
}
int strlen_dec_int( int n ){
int counter = 1;
while( n / 10 ){
counter++;
n /= 10;
}
return counter;
}
int strlen_hex_int( int n ){
int counter = 1;
while( n / 16 ){
counter++;
n /= 16;
}
return counter;
}
int strlen_char( char c ){
return 1;
}
输出:
7. 为了保证下面这条sprintf语句所产生的字符串不溢出,缓冲区至少应该有多大?
sprintf( buffer, "%s", a );
解析:
缓冲区大小至少应该为:strlen(a) + '\0'字符的个数。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* method:this method need the size of a during compilation */
char a[] = "hello, world!";
size_t a_size = sizeof(a) / sizeof(*a);
char buffer[sizeof(a) / sizeof(*a)];
int main( void ){
printf( "a = %s, a_size = %zd\n", a, a_size );
sprintf( buffer, "%s", a );
printf( "buffer = %s\n", buffer );
if( *(buffer + a_size - 1) == '\0' ){
printf( "true!\n" );
} else{
printf( "false!\n" );
}
/* method2:this method can know the length of a during the time of running */
int size;
printf( "please input the size of ps:" );
scanf( "%d", &size );
int ch;
while( (ch = getchar()) != EOF && ch != '\n' ){
;
}
char *ps;
ps = (char *)malloc( sizeof(char) * (size + 1) );
*(ps + size ) = 0;
char *result;
printf( "please input the content of ps:" );
result = fgets( ps, size + 1, stdin );
if( !result ){
exit( EXIT_FAILURE );
}
char *pline;
pline = strchr( result, '\n' );
if( !pline ){
while( (ch = getchar()) != EOF && ch != '\n' ){
;
}
} else{
*pline = '\0';
}
size_t buffer2_len = strlen( ps );
char buffer2[buffer2_len + 1];
sprintf( buffer2, "%s", ps );
*(buffer2 + buffer2_len) = 0;
printf( "buffer2 = %s\n", buffer2 );
if( *(buffer2 + buffer2_len) == '\0' ){
printf( "true!\n" );
} else{
printf( "false!\n" );
}
return EXIT_SUCCESS;
}
输出:
8. %f格式代码所打印的最后一位数字是经过四舍五入呢,还是未打印的数组被简单地截掉呢?
解析:
四舍五入。
#include <stdio.h>
#include <stdlib.h>
int main( void ){
float f = 0.5678856f;
printf( "f = %f\n", f );
return EXIT_SUCCESS;
}
输出:
9. 如何得到perror函数可能打印的所有错误信息列表?
解析:
perror输出的错误原因依照全局变量errno的值来决定要输出的字符串。
标准库函数在一个外部整型变量errno(在errno.h中定义)中保存错误代码代码之后把这个信息
传递给用户程序,提示操作失败的准确原因。perror函数简化向用户报告这些特定错误的过程。
perror打印出一条用于解释errno当前错误代码的信息。所以,我们只需要查看errno可能的值就能知道perror能够打印的所有的错误信息列表。
10. 为什么fprintf、fscanf、fputs和fclose函数都接受一个指向FILE结构的指针作为参数而不是FILE结构本身。
解析:
传递一个指针的效率更高。首先指针的字节大小小于FILE结构的字节大小,这可以提高实参向形参进行赋值的效率。传递一个指针是为了能够修改它所指向变量的成员的值,假如传递一个结构,还必须返回一个结构,再一次进行赋值操作修改原来的变量的值,效率就会降低。
#include <stdio.h>
#include <stdlib.h>
int main( void ){
printf( "sizeof(FILE *) = %zd, sizeof(FILE) = %zd\n", sizeof(FILE *), sizeof(FILE) );
return EXIT_SUCCESS;
}
输出:
11. 你希望打开一个问题进行写入,假定:不希望文件原来的内容丢失;希望能够写入到文件的任何位置。那么该怎样设置打开模式呢?
解析:
the mode r+ does the job,The w modes truncate the file,and the a modes restrict writing to the end of the file.
(r+ 这个模式可以做这个工作,w模式会截断文件,a模式会限制只能在文件末尾添加)。
参考链接:《C与指针》第十五章练习 - _monster - 博客园 (cnblogs.com)。
#include <stdio.h> /* 这个问题的源码可以选择性查看 */
#include <stdlib.h>
#include <string.h>
int main( void ){
FILE *file;
file = fopen( "data.txt", "wb" );
if( !file ){
printf( "fail to create a data.txt file.\n" );
perror( "data.txt" );
exit( EXIT_FAILURE );
}
char buffer[] = "I am a student.\n";
char buffer2[] = "I am a teacher.\n";
/*
** You can check the answer of expression If you are interested in it.
** int size3 = sizeof("I am a student.\n") / sizeof(*"I am a student.\r\n") ;
*/
int size = sizeof(buffer) / sizeof(*buffer);
int size2 = sizeof(buffer2) / sizeof(*buffer2);
printf( "size = %d, size2 = %d\n", size, size2 );
fpos_t current_pos;
int result;
result = fgetpos( file, ¤t_pos );
printf( "result = %d, current_pos = %d\n", result, current_pos );
fwrite( buffer, size, 1, file );
result = fgetpos( file, ¤t_pos );
printf( "after fwrite( buffer1, size, 1, file ), result = %d, current_pos = %d\n",
result, current_pos );
fwrite( buffer2, size2, 1, file );
result = fgetpos( file, ¤t_pos );
printf( "after fwrite( buffer2, size2, 1, file ), result = %d, current_pos = %d\n",
result, current_pos );
fflush( file );
fclose( file );
file = fopen( "data.txt", "rb+" );
if( !file ){
printf( "fail to open a data.txt file.\n" );
perror( "data.txt" );
exit( EXIT_FAILURE );
}
rewind( file );
fgetpos( file, ¤t_pos );
printf( "after rewind( file ), result = %d, current_pos = %d\n", result, current_pos );
char *d_buffer;
char *d_buffer2;
d_buffer = (char *)malloc( sizeof(char) * size );
d_buffer2 = (char *)malloc( sizeof(char) * size2 );
printf( "print original content of data.txt:\n" );
if( d_buffer ){
fread( d_buffer, size, 1, file );
fgetpos( file, ¤t_pos );
printf( "after fread( d_buffer, size, 1, file ), current_pos = %d\n", current_pos );
printf( "%s", d_buffer );
}
if( d_buffer2 ){
fread( d_buffer2, size2, 1, file );
fgetpos( file, ¤t_pos );
printf( "after fread( d_buffer2, size2, 1, file ), current_pos = %d\n", current_pos );
printf( "%s", d_buffer2 );
}
/*
** set current location is the first location. rewind func can achieve the same function.
** current_pos = 0;
** result = fsetpos( file, ¤t_pos );
*/
rewind( file );
result = fgetpos( file, ¤t_pos );
printf( "after rewind( file), current_pos = %d, result = %d\n", current_pos, result );
char buffer3[] = "I am a worker.\n";
int size3 = sizeof(buffer3) / sizeof(*buffer3);
printf( "size3 = %d\n", size3 );
printf( "add the content to the beginning of file:\n" );
fwrite( buffer3, size3, 1, file );
fgetpos( file, ¤t_pos );
printf( "after fwrite( buffer3, size3, 1, file ), current_pos = %d\n", current_pos );
printf( "print content of data.txt added:\n" );
rewind( file );
fgetpos( file, ¤t_pos );
char *d_buffer3;
/*char *d_buffer4;*/
d_buffer3 = (char *)malloc( sizeof(char) * size3 );
if( d_buffer3 ){
fread( d_buffer3, size3, 1, file );
result = fgetpos( file, ¤t_pos );
printf( "1:after fread( d_buffer3, size3, 1, file ),"
" current_pos = %d, d_buffer3 = %s", current_pos, d_buffer3 );
}
/*
d_buffer4 = (char *)malloc( sizeof(char) * (strlen( buffer4 ) + 1) );
if( d_buffer4 ){
fread( d_buffer4, size4, 1, file );
printf( "2:%s", d_buffer4 );
}
*/
free( d_buffer );
d_buffer = (char *)malloc( sizeof(char) * size );
if( d_buffer ){
fread( d_buffer, size, 1, file );
fgetpos( file, ¤t_pos );
printf( "2:after fread( d_buffer, size, 1, file ),"
" current_pos = %d, d_buffer = %s", current_pos, d_buffer );
}
free( d_buffer2 );
d_buffer2 = (char *)malloc( sizeof(char) * size2 );
if( d_buffer2 ){
fread( d_buffer2, size2, 1, file );
fgetpos( file, ¤t_pos );
printf( "4:after fread( d_buffer2, size2, 1, file ),"
" current_pos = %d, d_buffer2 = %s", current_pos, d_buffer2 );
}
free( d_buffer );
free( d_buffer2 );
free( d_buffer3 );
/*
free( d_buffer4 );
*/
fclose( file );
return EXIT_SUCCESS;
}
输出:
这个代码的输出是这样的,但是不能得到相应的结果。这里使用的是fgetpos、rewind、fsetpos函数,所以我采用的是二进制形式读写的方式来证明我的猜想。有问题的原因在于rb+进行写操作时会修改该文件原来存在该位置的内容,而不是把原来的内容依次后移。
下面是采用文本形式的参考,也可以证明采用文本形式读写的方式会修改原来该位置的内容,并且如果小于原来存在于此处的字符串大小,那么它只会改变前面部分的字符。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( void ){
FILE *file;
file = fopen( "data_2.txt", "w" );
if( !file ){
printf( "fail to create a data_2.txt file.\n" );
perror( "data_2.txt" );
exit( EXIT_FAILURE );
}
char buffer[] = "I am a student.\n";
char buffer2[] = "I am a teacher.\n";
/*
** You can check the answer of expression If you are interested in it.
** int size3 = sizeof("I am a student.\n") / sizeof(*"I am a student.\r\n") ;
*/
int size = sizeof(buffer) / sizeof(*buffer);
int size2 = sizeof(buffer2) / sizeof(*buffer2);
printf( "size = %d, size2 = %d\n", size, size2 );
long location = ftell( file );
printf( "location = %d\n", location );
fputs( buffer, file );
location = ftell( file );
printf( "after fputs( buffer, file ), location = %d\n", location );
fputs( buffer2, file );
location = ftell( file );
printf( "after fputs( buffer2, file ), location = %d\n", location );
fflush( file );
fclose( file );
file = fopen( "data_2.txt", "r+" );
if( !file ){
printf( "fail to open a data_2.txt file.\n" );
perror( "data_2.txt" );
exit( EXIT_FAILURE );
}
location = ftell( file );
printf( "location = %d\n", location );
char *d_buffer;
char *d_buffer2;
d_buffer = (char *)malloc( sizeof(char) * size );
d_buffer2 = (char *)malloc( sizeof(char) * size2 );
printf( "print original content of data_2.txt:\n" );
if( d_buffer ){
fgets( d_buffer, size, file );
/*fgetc( file );*/
location = ftell( file );
printf( "after fgets( d_buffer, size, file ), location = %d\n", location );
fputs( d_buffer, stdout );
}
if( d_buffer2 ){
fgets( d_buffer2, size2, file );
/*fgetc( file );*/
location = ftell( file );
printf( "after fgets( d_buffer2, size2, file ), location = %d\n", location );
fputs( d_buffer2, stdout );
}
fseek( file, 0, SEEK_SET );
location = ftell( file );
printf( "after fseek( file, 0, SEEK_SET ), location = %d\n", location );
char buffer3[] = "I am a worker.";
int size3 = sizeof(buffer3) / sizeof(*buffer3);
printf( "size3 = %d\n", size3 );
printf( "add the content to the beginning of file:\n" );
fputs( buffer3, file );
location = ftell( file );
printf( "after fputs( buffer3, file ), location = %d\n", location );
/*
printf( "print content of data_2.txt added:\n" );
fseek( file, 0, SEEK_SET );
location = ftell( file );
printf( "after fseek( file, 0, SEEK_SET ), location = %d\n", location );
char *d_buffer3;
*/
/*char *d_buffer4;*/
/*
d_buffer3 = (char *)malloc( sizeof(char) * size3 );
if( d_buffer3 ){
fgets( d_buffer3, size3, file );
location = ftell( file );
printf( "1:after fgets( d_buffer3, size3, file ),"
" location = %d\n", location );
fputs( d_buffer3, stdout );
}
*/
/*
d_buffer4 = (char *)malloc( sizeof(char) * (strlen( buffer4 ) + 1) );
if( d_buffer4 ){
fread( d_buffer4, size4, 1, file );
printf( "2:%s", d_buffer4 );
}
*/
/*
free( d_buffer );
d_buffer = (char *)malloc( sizeof(char) * size );
if( d_buffer ){
fgets( d_buffer, size, file );
location = ftell( file );
printf( "3:after fgets( d_buffer, size, file ),"
" location = %d\n", location );
fputs( d_buffer, stdout );
}
free( d_buffer2 );
d_buffer2 = (char *)malloc( sizeof(char) * size2 );
if( d_buffer2 ){
fgets( d_buffer2, size2, file );
location = ftell( file );
printf( "4:after fgets( d_buffer2, size2, file );,"
" location = %d\n", location );
fputs( d_buffer2, stdout );
}
*/
free( d_buffer );
free( d_buffer2 );
/*
free( d_buffer3 );
*/
/*
free( d_buffer4 );
*/
fclose( file );
return EXIT_SUCCESS;
}
输出:
data_2.txt的文本内容如下:
12. 为什么需要freopen函数?
解析:
freopen函数用于打开(或重新打开)一个特定的文件流。这个函数首先试图关闭这个流,然后用指定的文件和模式重新打开这个流。如果打开失败,函数返回一个NULL值。如果打开成功,函数就返回它的第三个参数值。这个函数可以简化关闭一个流和重新打开一个流需要进行的操作。
13. 对于绝大多数程序,有必要考虑fgetc(stdin)或getchar那个更好吗?
解析:
fgetc可用于所有的文件输入流,getchar只能用于标准输入流。fgetc是通过函数实现的,而getchar是通过宏实现的。如果是用于从标准输入流读取字符,建议采用getchar;否则,使用fgetc函数。
14. 在你的系统上,下面的语句将打印什么内容?
printf( "%d\n", 3.14 );
#include <stdio.h>
#include <stdlib.h>
int main( void ){
printf( "%d\n", 3.14 );
return EXIT_SUCCESS;
}
输出:
15. 请解释使用%-6.10s格式代码将打印出什么形式的字符串。
#include <stdio.h>
#include <stdlib.h>
int main( void ){
char buffer[] = "abcdefghilklmnopqrstuvwxyz";
printf( "%-6.10s", buffer );
return EXIT_SUCCESS;
}
输出:
16. 当一个特定的值用格式代码%.3f打印时,其结果是1.405。但这个值用格式代码%.2f打印时,其结果是1.40。似乎出现了问题,请解释器原因。
解析:
如果实际值是1.4049,格式代码%.3f将导致缀尾的4四舍五入至5,但使用格式代码%.2f,缀尾的0并没有进行四舍五入至1,因为它最后被截掉的第1个数字是4.
#include <stdio.h>
#include <stdlib.h>
int main( void ){
float f = 1.4049;
printf( "%.3f, %.2f", f, f );
return EXIT_SUCCESS;
}
输出: