函数进阶

本文深入探讨函数调用机制,包括栈帧、递归及其优化策略,详解字符串处理函数,如长度计算、拷贝、连接、比较及查找,同时覆盖输入输出函数,如错误报告、流操作和格式化I/O。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 函数的调用过程

函数的调用过程要为函数开辟栈空间,用于本次函数的调用中临时变量的保存、现场保护。这块栈空间称之为函数栈帧。

在函数调用的过程中ebp和esp这两个寄存器存放了维护这个栈的栈底和栈顶指针。ebp存放指向函数栈帧栈底的地址。 esp存放指向函数栈帧栈顶的地址。

eg:

#include <stdio.h>
int Add(int x, int y)
{   
       int z = 0;  
       z = x + y;  
       return z;
}
int main()
{  
       int a = 10;  
       int b = 20;   
       int ret = Add(a, b);  
       printf("ret = %d\n", ret);
       return 0;
} 

二. 函数递归

函数调用自身称为递归。(大事化小)

使用递归的条件:

1.存在限制条件,当满足这个限制条件时,停止递归;

2.每次递归调用后越来越接近这个限制条件。

eg1:接受一个整型值,把它转换为字符并且打印。如输入1234,输出1 2 3 4

#include<stdio.h>
void Print(int n)
{
       if (n > 9)
       {
              Print(n/10);
       }
       printf("%d ",n%10);
}
int main()
{
       int num = 1234;
       Print(num);
       return 0;
}

eg2:编写函数不允许创建临时变量,求字符串长度

#include <stdio.h>
int Strlen(const char*str)
{
       if (*str == '\0')   
              return 0;
       else return 1 + Strlen(str + 1);
}
int main()
{
       char *p = "abcdef";
       int len = Strlen(p);
       printf("%d\n", len);   
       return 0;
}

eg3:求n的阶乘

#include <stdio.h>
int factorial(int n)
{
       if (n <= 1)    
              return 1;  
       else      
              return n * factorial(n - 1);
}
int main()
{
       int n = 10;
       printf("%d\n",factorial(n));
}

eg4:求第n个斐波那契数列

#include <stdio.h>
int fib(int n)
{
       if (n <= 2)      
              return 1;
       else     
              return fib(n - 1) + fib(n - 2);
}
int main()
{
       int n = 20;
       printf("%d\n", fib(20));
       return 0;
}

以上例子,当参数特别大时会报错:栈溢出。

系统分配给程序的栈空间是有限的,但是如果出现了死循环或者死递归,可能导致一直开辟栈空间,最终将栈空间耗尽,这样的现象称为栈溢出。

为了解决栈溢出,可以将递归写成非递归。或者使用static对象替代nonstatic局部对象(栈对象),不仅可以减少每次递归调用和返回产生和释放nonstatic对象的开销,而且static对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。

例如求n个斐波那契数列:

#include <stdio.h>
int fib(int n)
{
       int result;  
       int pre_result;  
       int next_older_result;   
       result = pre_result = 1;    
       while (n > 2)
       {
              n -= 1;     
              next_older_result = pre_result;   
              pre_result = result;    
              result = pre_result + next_older_result;
       }    
       return result;
}
int main()
{
       int n = 500;
       printf("%d\n", fib(n));//315178285
       return 0;
}

三. main函数的参数

int main( int argc, char *argv[ ], char *envp[ ] ) 
{  
       program-statements
}

第一个参数:argc是个整型变量,表示命令行参数的个数(含第一个参数)。

第二个参数:argv是个字符指针的数组,每个元素是一个字符指针,指向一个字符串。这些字符串就是命令行中的每一个参数(字符串)。

第三个参数:envp是字符指针的数组,数组的每一个原元素是一个指向一个环境变量(字符串) 的字符指针。

#include <stdio.h>
int main(int argc, char* argv[], char* envp[])
{    
       int i = 0;  
       for(i=0; i<argc; i++)  
       {     
              printf("%s\n", argv[i]);  
       }    
       return 0;
}
//F:\C\test0718\Debug\test0718.exe
#include <stdio.h>
int main(int argc, char* argv[], char* envp[])
{
       int i = 0;
       while (envp[i] != NULL)
       {
              printf("%s\n", envp[i]);
              i++;
       }
       return 0;
}
/*
COMPUTERNAME=DESKTOP-QV5439H
ComSpec=C:\Windows\system32\cmd.exe
FPS_BROWSER_APP_PROFILE_STRING=Internet Explorer
FPS_BROWSER_USER_PROFILE_STRING=Default
HOMEDRIVE=C:
HOMEPATH=\Users\Yuri
LOCALAPPDATA=C:\Users\Yuri\AppData\Local
LOGONSERVER=\\DESKTOP-QV5439H
MSBuildLoadMicrosoftTargetsReadOnly=true
NUMBER_OF_PROCESSORS=4
OneDrive=C:\Users\Yuri\OneDrive
OS=Windows_NT
Path=C:\Program Files (x86)\Intel\iCLS Client\;C:\Program Files\Intel\iCLS Client\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;F:\Git\cmd;C:\Program Files\TortoiseGit\bin;C:\Users\Yuri\AppData\Local\Microsoft\WindowsApps;
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PkgDefApplicationConfigFile=C:\Users\Yuri\AppData\Local\Microsoft\VisualStudio\15.0_97bd02f3\devenv.exe.config
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_ARCHITEW6432=AMD64
PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 61 Stepping 4, GenuineIntel
*/
//以上只是部分

结果显示都是环境变量。envp数组的最后一个元素存放NULL指针。

四. 可变参数列表

通过将函数实现为可变参数的形式,可以使得函数可以接受1个以上的任意多个参数(不固定)。

eg:实现一个函数可以求任意个参数的平均值

#include <stdio.h>
#include <stdarg.h>
int average(int n, ...)
{   
       va_list arg;   
       int i = 0;    
       int sum = 0;   
       va_start(arg, n);
       for(i=0; i<n; i++)   
       {
              sum += va_arg(arg, int);
       }    
       return sum / n;  
       va_end(arg);
}
int main()
{
       int a = 1;   
       int b = 2;
       int c = 3;  
       int avg1 = average(2, a, c);  
       int avg2 = average(3, a, b, c);
       printf("avg1 = %d\n", avg1);   
       printf("avg2 = %d\n", avg2);   
       return 0;
}
/*
avg1 = 2
avg2 = 2
*/

声明一个 va_list  类型的变量 arg ,它用于访问参数列表的未确定部分。 

这个变量是调用 va_start 来初始化的。它的第一个参数是 va_list 的变量名,第2个参数是省略号前后一个有名字的参数。初始化过程把 arg 变量设置为指向可变参数部分的第一 个参数。 

为了访问参数,需要使用 va_arg ,这个宏接受两个参数: va_list 变量和参数列表中下一 个参数的类型。在这个例例子中所有的可变数都是整型。 va_arg 返回这个参数的值,并使用 va_arg 指向下⼀个可变参数。 

当访问完最后一个可变参数之后,我们需要调用 va_end。

可变参数的限制:

可变参数必须从头到尾逐个访问。如果在访问了几个可变参数之后想半途终止,这是可以的,但是,如果想⼀开始就访问参数列表中间的参数,那是不行的。

参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用va_start。

这些宏是无法直接判断实际存在参数的数量。

这些宏无法判断每个参数的类型。

如果在va_arg中指定了错误的类型,那么其后果是不可预测的。

五. 字符函数与字符串函数

1.求字符串长度

(1)strlen

size_t strlen ( const char * str );

字符串已 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。

参数指向的字符串必须要以 '\0' 结束。 

注意函数的返回值为size_t,是无符号的。

模拟实现:

计数器方式:

int my_strlen(const char * str)
{    
       int count = 0;    
       while(*str)  
       {      
              count++;    
              str++;  
       }   
       return count;
}

不创建临时变量计数器方式:

int my_strlen(const char * str)
{
       if (*str == '\0')    
              return 0;   
       else       
              return 1 + my_strlen(str + 1);
}

指针方式:

int my_strlen(char *s)
{
       char *p = s;
       while (*p != '\0')
              p++;
       return p - s;
}

2.长度不受限制的字符串函数

(1)strcpy字符串拷贝

char* strcpy(char * destination, const char * source );

源字符串必须以 '\0' 结束。

会将源字符串中的 '\0' 拷贝到目标空间。 

目标空间必须足够大,以确保能存放源字符串。 

目标空间必须可变。

不能将字符串拷贝到常量字符串中。

模拟实现:

char *my_strcpy(char *dest, const char*src)
{
       char *ret = dest;    
       assert(dest != NULL);
       assert(src != NULL);       
       while ((*dest++ = *src++))
       { ; }   
       return ret;
}

(2)strcat 字符串连接

char * strcat ( char * destination, const char * source );

源字符串、目标字符串都必须以 '\0' 结束。 

目标空间必须有足够的大,能容纳下源字符串的内容。 

目标空间必须可修改。

模拟实现:

char *my_strcat(char *dest, const char*src)
{
       char *ret = dest;  
       assert(dest != NULL);
       assert(src != NULL);  
       while (*dest)
       {
              dest++;
       }   
       while ((*dest++ = *src++))
       { ; }
       return ret;
}

(3)strcmp字符串比较

int strcmp ( const char * str1, const char * str2 );

标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字

#include<stdio.h>
int main()
{
       char *p = "abcdef";
       char *q = "abcdef";
       if (p == q)
       {
              printf("p==q");
       }
       else
       {
              printf("p!=q");
       }
       return 0;
}
//输出p==q
//常量字符串存一份,p与q指向同一块空间。若是两个内容一样的字符串数组,则不相等。

3.长度受限制的字符串函数

(1)strncpy

char * strncpy ( char * destination, const char * source, size_t num );

拷贝num个字符从源字符串到目标空间。 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

(2)strncat

char * strncat ( char * destination, const char * source, size_t num );
#include <stdio.h>
#include <string.h>
int main() {
       char str1[20];
       char str2[20];
       strcpy(str1, "Hello ");
       strcpy(str2, "i am a student");
       strncat(str1, str2, 7);
       puts(str1);
       return 0;
}
//Hello i am a 

(3)strncmp

int strncmp ( const char * str1, const char * str2, size_t num );

比较到出现另一个字符不一样或者另一个字符串结束或者num个字符全部比较完.

eg:

#include <stdio.h>
#include <string.h>
int main() {
       char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
       int n;
       printf("Looking for R2 astromech droids :\n");
       for (n = 0; n < 3; n++)
              if (strncmp(str[n], "R2xx", 2) == 0)
              {
                     printf("found %s\n", str[n]);
              }
       return 0;
}
/*
Looking for R2 astromech droids :
found R2D2
found R2A6
*/

4.字符串查找

(1)strstr

char * strstr ( const char *, const char * );

返回str1中str2的第1次出现的指针或空指针。

函数返回一个指针,指向字符串中第一个出现的str2,如果str2没有出现在str1中,则返回NULL。如果str2指向长度为零的字符串,则函数返回字符串。

eg:

#include <stdio.h>
#include <string.h>
int main()
{
       char str[] = "This is a simple string";
       char * pch;
       pch = strstr(str, "simple");
       strncpy(pch, "sample", 6);
       printf("%s\n", str);
       return 0;
}
//This is a sample string

模拟实现:

char *my_strstr(const char* str1, const char* str2)
{
       assert(str1);    
       assert(str2);   
       char *cp = (char*)str1;  
       char *substr = (char *)str2;  
       char *s1 = NULL;     
       if (*str2 == '\0')  
              return NULL;  
       while (*cp)
       {
              s1 = cp;     
              substr = str2;   
              while (*s1 && *substr && (*s1 == *substr))
              {
                     s1++;
                     substr++;
              }    
              if (*substr == '\0')
                     return cp;
              cp++;
       }
}

(2)strtok(在字符串中查找下一个标记)

char * strtok ( char * str, const char * sep );

第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。

strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。) 

strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。

strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。 

如果字符串中不存在更多的标记,则返回 NULL 指针。

eg:

#include <stdio.h>
#include <string.h>
int main()
{
       char str[] = "- This, a sample string.";
       char * pch;
       printf("Splitting string \"%s\" into tokens:\n", str);
       pch = strtok(str, " ,.-");
       while (pch != NULL)
       {
              printf("%s\n", pch);
              pch = strtok(NULL, " ,.-");
       }
       return 0;
}
/*
Splitting string "- This, a sample string." into tokens:
This
a
sample
string
*/
#include <stdio.h>
int main()
{
       char *p = "Taylor@163.com";
       const char* sep = ".@";
       char arr[30];
       char *str = NULL;
       strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容  
       for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
       {
              printf("%s\n", str);
       }
}
/*
Taylor
163
com
*/

5.错误信息报告

(1)strerror

char * strerror ( int errnum );
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
       FILE * pFile;
       pFile = fopen("unexist.ent", "r");
       if (pFile == NULL)
              printf("Error opening file unexist.ent: %s\n", strerror(errno));
       //errno: Last error number  
       return 0;
}
/*
Error opening file unexist.ent: No such file or directory
*/

6.内存操作函数

(1)memcpy

void * memcpy ( void * destination, const void * source, size_t num );

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。 

这个函数在遇到 '\0' 的时候并不会停下来。 

如果source和destination有任何的重叠,复制的结果都是未定义的。

#include <stdio.h>
#include <string.h>
struct {
       char name[40];
       int age;
}person, person_copy;
int main()
{
       char myname[] = "Taylor de Fermat";
       //using memcpy to copy string:
       memcpy(person.name, myname, strlen(myname) + 1);
       person.age = 22;
       //using memcpy to copy structure:
       memcpy(&person_copy, &person, sizeof(person));
       printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);
       return 0;
}
//person_copy: Taylor de Fermat, 22

模拟实现:

void * memcpy(void * dst, const void * src, size_t count)
{
       void * ret = dst;     
       assert(dst);
       assert(src);
       //copy from lower addresses to higher addresses
       while (count--)
       {
              *(char *)dst = *(char *)src;
              dst = (char *)dst + 1;
              src = (char *)src + 1;
       }
       return(ret);
}

(2)memmove

void * memmove ( void * destination, const void * source, size_t num );

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。 

如果源空间和目标空间出现重叠,就得使用memmove函数处理。

#include <stdio.h>
#include <string.h>
int main()
{
       char str[] = "memmove can be very useful......";
       memmove(str + 20, str + 15, 11);
       puts(str);
       return 0;
}
//memmove can be very very useful.

模拟实现:

void * memcpy(void * dst, const void * src, size_t count)
{
       void * ret = dst;
       if (dst <= src || (char *)dst >= ((char *)src + count))
       {
              //Non-Overlapping Buffers
              //copy from lower addresses to higher addresses
              while (count--)
              {
                     *(char *)dst = *(char *)src;
                     dst = (char *)dst + 1;
                     src = (char *)src + 1;
              }
       }
       else {
              //Overlapping Buffers
              //copy from higher addresses to lower addresses
              dst = (char *)dst + count - 1;
              src = (char *)src + count - 1;
              while (count--) { *(char *)dst = *(char *)src;
              dst = (char *)dst - 1;
              src = (char *)src - 1;
              }
       }
       return(ret);
}

(3)memcmp

int memcmp ( const void * ptr1,  const void * ptr2,  size_t num );

比较从ptr1和ptr2指针开始的num个字节。

返回值:

<0ptr1 less than ptr2
0ptr1 identical to ptr2
>0

ptr1 greater than ptr2

#include <stdio.h>
#include <string.h>
int main()
{
       char buffer1[] = "DWgaOtP12df0";
       char buffer2[] = "DWGAOTP12DF0";
       int n;
       n = memcmp(buffer1, buffer2, sizeof(buffer1));
       if (n > 0)
              printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
       else if (n < 0)
              printf("'%s' is less than '%s'.\n", buffer1, buffer2);
       else printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
       return 0;
}
//'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'.

六. 输入输出函数

1.错误报告函数

void perror ( const char * str );

perror 以一种简单、统一的方式报告错误。ANSIC函数库的许多函数调用操作系统来完成某些任务,I/O函数尤其如此,当操作系统按照要求执行某些任务的时候就可能会失败,例如:尝试打开⼀个根本就不存在的文件,读取内容。操作系统的做法就是发生错误的时候,能够提示发生了错误。标准库函数提供了一个全局的变量 errno (在errno.h中定义),来记录错误的错误码,交给用户程序,用于提示错误的准确原因。

eg:

#include <stdio.h>
int main ()
{
       FILE * pFile;
       pFile=fopen ("unexist.ent","rb");  
       if (pFile == NULL)   
              perror ("The following error occurred");
       else    
              fclose (pFile);  
       return 0;
}
//The following error occurred: No such file or directory

如果perror函数的参数str指针不是NULL,并指向一个非空的字符串,perror函数先打印这个字符串,然后跟着输出一个分号和一个空格,接着输出错误信息提示。

errno这个值只有在调用库函数的时候发生问题了,才能会被设置。而函数调用成功之后也不会修改,所以不能使用errno来判断一个函数是否执行成功。

2.终止执行

void exit (int status);

参数status返回给操作系统,用于提示程序是否正常完成。 

C语言定义了预定义符号: EXIT_SUCCESS 和  EXIT_FAILURE 来表示成功或者失败返回。

#include <stdio.h>      /* printf, fopen */
#include <stdlib.h>     /* exit, EXIT_FAILURE */
int main()
{
       FILE * pFile;
       pFile = fopen("myfile.txt", "r");  
       if (pFile == NULL)
       {
              printf("Error opening file");  
              exit(EXIT_FAILURE);
       }
       else
       {   
              /* file operations here */
       }
       return 0;
}
//Error opening file

3.I/O函数

功能

函数名

适用于

字符输入函数

getchar

标准输入流

字符输出函数

putchar

标准输出流

字符输入函数

fgetc, getc

所有输入流

字符输出函数

fputc, putc

所有输出流

文本行输入函数

fgets, gets

所有输入流

文本行输出函数

fputs, puts

所有输出流

格式化输入函数

scanf

标准输入流

格式化输出函数

printf

标准输出流

格式化输入函数

fscanf

所有输入流

格式化输出函数

fprintf

所有输出流

二进制输入

fread

文件

二进制输出

fwrite

文件

4.打开流

FILE * fopen ( const char * filename, const char * mode );
#include <stdio.h>
int main ()
{
       FILE * pFile;
       pFile = fopen ("myfile.txt","w");
       if (pFile!=NULL)
       {  
              fputs ("fopen example",pFile);
              fclose (pFile);
       }
       return 0;
}

5.关闭流

int fclose ( FILE * stream );
#include <stdio.h>
int main ()
{
       FILE * pFile;
       pFile = fopen ("myfile.txt","wt");
       fprintf (pFile, "fclose example");
       fclose (pFile);
       return 0;
}

文件使用方式

含义

如果指定文件不存在

r(只读)

为了输入数据,打开一个已经存在的文本文件

出错

w(只写)

为了输出数据,打开一个文本文件

建立一个新文件

a(追加)

向文本文件尾添加数据

出错

rb(只读)

为了输入数据,打开一个二进制文件

出错

wb(只写) 

为了输出数据,打开一个二进制文件

建立一个新文件

ab(追加)

向一个二进制文件尾添加数据

出错

r+(读写)

为了读和写,打开一个文本文件

出错

w+(读写)

为了读和写,建议一个新的文件

建立一个新文件

a+(读写)

打开一个文件,在文件尾进行读写

建立一个新文件

rb+(读写)

为了读和写打开一个二进制文件

出错

wb+(读写)

为了读和写,新建一个新的二进制文件

建立一个新文件

ab+(读写)

打开一个二进制文件,在文件尾进行读和写

建立一个新文件

6.字符I/O

(1)getchar和putchar函数

int getchar ( void );
int putchar ( int character );
#include<stdio.h>
int main()
{
       int c;
       puts("Enter text. Include a dot ('.') in a sentence to exit");
       do
       {   
              //getchar 是从标准输⼊入获取⼀一个字符  
              c=getchar();   
              //putchar 是向标准输出流输出⼀一个字符   
              putchar (c);  
       } while (c != '.');
       return 0;
}
/*
Enter text. Include a dot ('.') in a sentence to exit
edd
edd
cc
cc
vv
vv
*/

(2)getc和putc函数

int getc ( FILE * stream ); 
int putc ( int character, FILE * stream );
#include <stdio.h>
int main ()
{
       FILE * pFile;  
       int c;
       int n = 0;
       pFile=fopen ("myfile.txt","r");
       if (pFile==NULL)
              perror ("Error opening file");
       else
       {    
              do   
              {    
                     c = getc (pFile);   
                     if (c == '$') n++;
              }
              while (c != EOF);
              fclose (pFile);
              printf ("File contains %d$.\n",n);
       }  
       return 0;
}
//File contains 0$.

(3)fgetc和fputc函数

int fgetc ( FILE * stream ); 
int fputc ( int character, FILE * stream );

getc和fgetc功能一样。 putc和fputc功能一样。 

有些编译器将getc和putc实现为宏。

7.未格式化的行I/O

(1)gets和puts

char * gets ( char * str ); 
int puts ( const char * str );
#include <stdio.h>
int main()
{
       char string[256];
       printf("Insert your full address: ");  
       gets(string);     
       // warning: unsafe (see fgets instead)  
       printf ("Your address is: %s\n",string);
       puts(string);
       return 0;
}
/*
Insert your full address: jjsd8294rhv
Your address is: jjsd8294rhv
jjsd8294rhv
*/

(2)fgets和fputs

char * fgets ( char * str, int num, FILE * stream );
 int fputs ( const char * str, FILE * stream );
#include <stdio.h>
int main() {
       FILE * pFile;  
       char mystring[100];
       pFile = fopen("myfile.txt", "r");  
       if (pFile == NULL)
              perror("Error opening file");
       else
       {
              if (fgets(mystring, 100, pFile) != NULL)   
                     puts(mystring);    
              fclose(pFile);
       }  
       return 0;
}
//fclose example

8.格式化的行I/O

(1)scanf和printf

int scanf ( const char * format, ... ); 
int printf ( const char * format, ... );

(2)fscanf和fprintf

int fscanf ( FILE * stream, const char * format, ... ); 
int fprintf ( FILE * stream, const char * format, ...);
#include <stdio.h>
int main()
{
       char str[80];
       float f;
       FILE * pFile;
       pFile = fopen("myfile.txt", "w+");
       fprintf(pFile, "%f %s", 3.1416, "PI");
       rewind(pFile);
       fscanf(pFile, "%f", &f);
       fscanf(pFile, "%s", str);
       fclose(pFile);
       printf("I have read: %f and %s \n", f, str);
       return 0;
}
//I have read: 3.141600 and PI

9.二进制I/O

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream ) ;
#include <stdio.h>
int main()
{
       FILE * pFile;
       char buffer[] = { 'x' , 'y' , 'z' };
       pFile = fopen("myfile.bin", "wb");
       fwrite(buffer, sizeof(char), sizeof(buffer), pFile);
       fclose(pFile);
       return 0;
}

10.文件结束判定

在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

文本文件读取是否结束,判断返回值是否为EOF (fgetc),或者NULL(fgets) 

例如: fgetc判断是否为EOF。fgets判断返回值是否为NULL。

二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。 

例如: fread判断返回值是否小于实际要读的个数。

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
       int c;
       FILE* fp = fopen("test.txt", "r");
       if(!fp) {
              perror("File opening failed");
              return EXIT_FAILURE;
       }    
       //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF    
       while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环    
       {
              putchar(c);
       }   
       //判断是什么原因结束的    
       if (ferror(fp))
              puts("I/O error when reading");
       else if (feof(fp))
              puts("End of file reached successfully");
       fclose(fp);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值