C语言-基础

0. 参考

https://www.runoob.com/cprogramming/c-tutorial.html

1. 关键字

https://blog.csdn.net/L_fengzifei/article/details/127921678!!!

1.1 auto

动态类型
所有局部变量 默认为auto类型
只能在函数内使用,只能修饰局部变量

块作用域、空链接

int func()
{
	// 下面两种写法都表示的是局部变量
	int mount;
	auto int month;
}

typedef

https://blog.csdn.net/weixin_45743799/article/details/104532763
https://blog.csdn.net/weixin_44948467/article/details/114452324

typdef 与 #define

#define是由预处理器来处理的,在预处理阶段,只是简单的字符序列替换
typedef是由编译器完成的,在编译阶段,给原来的数据类型起别名,可以作为一种新的数据类型进行使用

typedef int INGEGER;
INTEGER a,b; //等价于 int a,b;

typedef int* PTR_INT
PTR_INT p1,p2

typedef与数组

arr数组的类型是char [20]

typedef char ARRAY[20]; // ARRAY 是char [20]的别名
ARRAY arr; // 等价于char arr[20];

typedef与结构体

typedef strcut Stu{
    char name[10];
    int age;
}STU; // STU 是struct stu的别名
STU stu1,stu2; // 等价于struct stu stu1,stu2;   
typdef struct Stu{
    char name[10];
    int age;
}STU,*PSTU;

// version2
typdef struct Stu{
    char name[10];
    int age;
}; 
typedef struct Stu STU;
typedef struct Stu* PSTU;

STU stu1; // 等价于 struct Stu su1;
PSTU pstu2; // 等价于 struct Stu *pstu2

结构体数组

#define MAXSIZE 10

typedef struct
{
    int data;
    int next; // 下一个元素的数组下表
}SLinkList[MAXSIZE];


int main()
{
    SLinkList arr; // struct arr[MAXSIZE];
    return 0;
}

typedef与指针

typedef int (*PTR_ARR)[4]; //二维数组 PTR_ARR 是int *[4]的别名
PTR_ARR p1,p2; //等价于int (*p1)[4],(*p2)[4];

typedef 函数指针

typedef int (*PTR_TO_FUNC)(int, int);
PTR_TO_FUNC pfunc;

// 例子
typedef int (*PTR_TO_FUNC)(int, int);
int max(int a, int b){
    return a>b ? a : b;
}
PTR_TO_FUNC pfunc = max;
printf("max: %d\n", (*pfunc)(10, 20));

1.3 const

整个作用域值不被改变,常量,(建议使用大写名进行)
使用const关键字 必须进行初始化

const 修饰局部变量,在栈区创建
const 修饰全局变量,在常量区创建

const 不能用于switch-case语句,实际上const 修饰之后变成的是只读变量,不是实际的常量

不要将const修饰的数据 赋值给非const修饰的数据,不然会报错 !!! 不完全对 !!! – 见下面的const和非const
可以将非const修饰的数据,赋值给const修饰的数据

const int Var=10;

// const int var;
// var=20; // 不能进行这种初始化

// var=20; // 不能改变

进阶

int getNum()
{
	return 100;
}

int main()
{
	int n=10;
	const int M1=getNum(); // 运行时初始化
	const int M2=n; // 运行时初始化
	const int M3=30; // 编译时初始化
}

1.3.0 const 与 非const

https://blog.csdn.net/souching/article/details/6796705
https://blog.csdn.net/zxc024000/article/details/78624188

const某些情况下可以赋值给非const
本质上是值拷贝(),与地址拷贝的区别

int a=10;
const int &b=a;
int c=b; // 正确

printf("%#p\n",&a); // 0x61fe14
printf("%#p\n",&b); // 0x61fe14
printf("%#p\n",&c); // 0x61fe110

a=20;
cout<<b<<endl; // 20
cout<<c<<endl; // 10

const Student & topval(const Student &s) const;
Student s=s1.topval(s2); // 正确

1.3.1 const 与数组

数组不允许被修改,数组中的元素都被当成常量

const int a[10]; 

1.3.2 const 与 指针

可以修饰指针变量本身,或指针指向的数据

不要将const修饰的数据 赋值给非const修饰的数据,不然会报错!!! 不完全对 !!!)(要看是值传递还是地址传递)

const 离(指针)变量名远,修饰的是指针指向的数据
const 离(指针)变量名近,修饰的是指针变量本身

const int *p; // 指针指向的值不能修改,指针可以指向其他数据
int const *p; // 指针指向的值不能修改,指针可以指向其他数据
int* const p; // 指针变量本身不能修改(不能指向其他数据)

// 下面的方法 表示指针和指向的数据都是只读的
const int* const p4
int const * const p5
// 不同通过指针修改数组,但是可以通过数组名本身进行修改!!!
int arr[2]={0};
const int *p=arr;

// p[0]=1; // ERROR 错误
arr[0]=10; // 正确

1.3.3 const 与函数参数

const 通常用在函数形参中,如果形参是一个指针,为了防止在函数内部修改指针指向的数据,可以用const来限制

// 限制内部函数对字符串str进行修改
size_t strnchr(const char* str,char ch){
    int i,n=0,len=strlen(str);

    for (i=0;i<len;i++){
        if (str[i]==ch){
            n++;
        }
    }
    return n;
}

int main(){
    char* str="http"; //hello
    char ch='t';

    int n=strnchr(str,ch);

    printf("%d\n",n);

    return 0;
}

注意:上面针对的是指针传递,如果是值传递,本来实参传形参的时候就是深拷贝操作,所以不需要也不要加const

// 下面两种都要禁止
// void func(const int a);
// void func(const <T> a); // 用户定义的类型

1.4 static

https://www.runoob.com/w3cnote/cpp-static-usage.html – 还没看

静态变量
无论是全局的的还是局部的,都存储在静态数据区
静态数据区数据在程序启动时就会初始化,直到程序结束
对于代码块中的静态局部变量,即使代码块执行结束,也不会销毁
静态存储区的变量只能,且只需要初始化一次,由于不销毁,所以多次调用都是有效的

int sum(int n)
{
    static int reuslt=0;
    result+=n;
    return result;
}

int main()
{
    for (int i=0;i<100;i++)
    {
        result=sum(i);
    }
}

static也可以修饰全局变量,当修饰全局变量的时候,会使变量的作用域限制在声明它的文件中。全局声明的一个static变量或方法可以被任何函数或方法调用,只是必须与被修改的变量或方法在同一个文件中

static修饰静态函数,表示该函数只能在当前源文件中被调用

1.4.1 static/const/多文件

// s.h
/*
	如果下面的变量不加static 在file1.c和file2.c的引用过程中会出现重复定义
	如果加上static 则两个变量分别变成了file1.c 和 file2.c中的(私有)变量,不会造成重复定义的问题
*/
static const int a=1;
static const char* names[2]={"li","wang"};

// file1.c
#include "s.h"

// file2.c
#include "s.h"

1.5 extern

extern dataType var 相当于引用声明,表明声明不是一个定义,告诉编译器该变量的真正定义在别的地方,可以当前文件,也可以是别的文件
extern 关键字修饰的表达式,的变量,不会分配内存空间,他只是用来引用一个已经存在的外部定义
不能利用extern进行变量定义extern int a=1; 这种是错误的

用于提供一个全局变量的引用

跨文件调用的时候,要加上extern,虽然有些情况下不写extern仍然可以执行(函数不用extern可以,变量不可以)(因为对于函数声明的extern 加不加都是等价的

定义与声明

// main.cpp
int a=1; // 既是声明也是定义
int b; // 既是声明也是定义
extern int c; // 只是声明,c在其他文件中被定义 

简单用法

提供一个全局变量/函数的引用,全局变量对所有程序文件都是可见的
使用extern无法初始化(定义)变量,会把变量名指向一个之前定义过的存储位置
其实就是 A文件中定义了一个可以在B文件中使用的全局变量或函数,那么在B文件中就可以使用extern来得到A文件中定义的变量或函数的引用(用在在另一个文件中声明一个全局变量或函数)
进阶:A/B文件可以存在相互的使用extern

// A.c
#include <stdio.h>
int count;
extern void write_extern();

int main()
{
	count=5;
	write_extern();
}
// B.c
#include <stdid.h>
extern int count;
void write_extern(void)
{
	printf("count is %d\n",count)
}

简单用法2

时刻注意先声明后定义
可多次声明,但是不可重复定义

// 在一个文件内
// 例子1
int a; // 之所以下面可以使用extern 是因为此时a相当于一个外部链接变量(外部链接的静态)
	   // 当在函数内不写extern的时候,那么根据作用域进行判断使用局部变量a或全局变量a

int main() // or some function
{
	extern int a; // 使用的是全局变量的a
	// int a; // 重新定义了一个局部变量a ; 如果extern int a 和int a 同时存在于一个作用域块内,则ERROR重复定义
}

// 例子2
int a=10; 

int main() // or some function
{
	extern int a; // 使用的是全局变量的a
	// int a; // 重新定义了一个局部变量a ; 如果extern int a 和int a 同时存在于一个作用域块内,则ERROR重复定义
}

// 例子3
// 值得注意的是:下面的例子是正确的!!!
int main()
{
	extern int a;
}
int a=10;

进阶用法
https://blog.csdn.net/m0_46606290/article/details/119973574
https://blog.csdn.net/rammuschow/article/details/107993479

/*test.h*/
extern int a; // 引用声明,引用了一个已经存在真正定义的变量,该变量先通过.c文件#include 然后进行了定义!!!
/*test.c*/
#include "test.h"
int a=10;
/*main.c*/
#include <stdio.h>
#include "test.h" // 会链接到test.c文件

int main()
{
	printf("%d\n",a); // 10 使用的是test.c中的a 具有外部链接的变量
	
	int a=2000; // 这个是具有空链接的变量:块作用域、局部变量,这个不会造成重复定义
	printf("%d\n",a); // 2000 就近原则,使用的是局部变量的a 
}

进阶用法2

/*test.h*/
void func(); // 函数声明
/*test.c*/
#include <stdio.h>
#include "test.h"

extern int times; // 引用声明,可以引用主文件中的全局变量

// 函数定义
void func()
{	
	int i=0;
	    int i=0;
    for ( i;i<times;i++)
    {
        printf("hello world\n");
    }
}
/*main.c*/
#include <stdio.h>
#include "test.h"

// 定义全局变量
int times=10; // test.c中extern 引用声明

int main()
{
	loop(); // 调用的是test.h 中连接test.c的函数
	return 0;
}

1.5.1 extern / #include

https://www.runoob.com/w3cnote/extern-head-h-different.html

1.6 register – 还要看

定义存储在寄存器中而不是RAM(内存)中的局部变量,意味着变量的最大只存等于寄存器的大小(一个字节),且不能也对它应用一元运算符???
通常变量存储在内存中
寄存器变量被存储在CPU的寄存器中,寄存器变量比普通变量具有更对的访问和读写速度
无法获得寄存器变量的地址
寄存器只用于需要快速访问的变量,如计数器等(定义了register,并不意味着变量将被存储在寄存器中,只是意味着变量可能存储在寄存器中,这取决于硬件和实现的限制
一般不能定义double类型

int func()
{
	register int miles;
}

void func(register int n);

1.7 volatile – 没看懂???

通常用于 硬件地址和与其他并行运行的程序共享的数据(例如:一个地址中可能保存着当前的时钟事件,不管程序做些什么,该地址的值都会随着时间而改变。另一种情况是一个地址被用来接收来自其他计算机的信息)
语法同const

防止编译器、寄存器过分优化
一般用于嵌入式

volatile int a;
volatile int b;

// 防止a和b,共享x???
a=x;
b=x; 

控制方法

if-else

注意,else总是与其前面位置最近if配对
判断语句中,可以是数值、表达式、赋值表达式、变量等,只要是非0都可以

if (condition)
{
	...
}
else if (condition)
{
	...
}
else ()
{
	...
}

//例子
if (a=b) // 把b的值赋值a,如果b为非0,则执行
{
	//TODO
}

switch-case

switch-expression 必须是一个常量表达式,一个整型(整型表达式)或一个枚举类型,(其实字符常量也可以,因为字符和整型可以相互转换),(也就是说不能包含任何变量,浮点型也不行
expression1/2 必须是与expression类型相同的数据,且必须是常量或字面量(字符也行,可以与整型相互转换
expression 总结

switch后面接的expression 可以是变量表达式(可以包括运算公式),但是必须是整型变量表达式 --> (整型常量、整型变量、整型常量表达式、整型变量表达式、枚举类型)
case 后面接的expression 必须是常量表达式表达式的结果必须是整型就可以,但不能是变量)(可以包括运算公式)(不能是变量),必须与switch后的expression具有相同的类型 (整型常量、整型常量表达式)
两种表达式不能是浮点型但是可以是字符型
const 后面接const 常量也不行,因为const int val 是只读变量 不是常量

如果case不加break,控制流会继续后面的case的语句(无论满不满足下一个case的条件),直到遇到break
当所有的case都不匹配的时候,有一个可选语句default(default不是必须的),default语句中的break不是必须的

swtich(expression)
{
	case expression1:
		...
		break;
	case expression2:
		...
		break;
	default:
		...
}

例子

{
    char c='b'; // c存储在常量区
    scanf("%c",&c);
    switch (c)
    {
    case 'a':
        cout<<'a'<<endl;
        break;
    
    default:
        cout<<"not a"<<endl;
        break;
    }
    return 0;
}
#include <stdio.h>
 
int main(){
    enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } day; // day是整型常量
    scanf("%d", &day);
    switch(day){
        case Mon: puts("Monday"); break;
        case Tues: puts("Tuesday"); break;
        case Wed: puts("Wednesday"); break;
        case Thurs: puts("Thursday"); break;
        case Fri: puts("Friday"); break;
        case Sat: puts("Saturday"); break;
        case Sun: puts("Sunday"); break;
        default: puts("Error!");
    }
    return 0;
}

while

while(condition)
{
	...
}

注意!!!

#include <iostream>
using namespace std;

int main()
{
    int a=10;
    while (a--)
    {
        cout<<a<<endl; // 第一次a已经是9了!!!
    }

    // for (;a>=0;a--)
    // {
    //     cout<<a<<endl;
    // }

    return 0;
}

for

int会首先被执行一次,且只会被执行一次;这一步可以声明并初始化任何循环控制变量,也可以不写任何语句,只有一个分号即可
然后会判断condition,如果为真则执行循环主体,如果为假,则不执行循环主体,且控制流会跳转到紧接着for循环的下一条语句
执行完for循环主体后,控制流会跳回increment,该语句可以留空,只要在条件后有一个分号出现即
条件再次被判断,如果为真,则执行循环,条件变为假的时候,for循环终止

for(int;condition;increment)
{
	...;
}

do-while

函数体至少会被执行一次

do
{
	...;
}while(codition);

文件操作

C语言中硬件设备可以看做文件
打开文件–保存到一个FILE类型的结构体变量中;关闭文件就是释放结构体变量

文本和二进制

都是二进制存储的,不过是采用特定的编码方式能够进行读取

换行符

linux : \n程序中的数据会原封不动的写入文本文件
windows: \r\n作为文本文件的换行符

如果以文本方式打开文件,当读取文件时,程序会将文件中所有\r\n转换成一个字符\n(如果文本文件中有连续的两个字符\r\n,则程序会丢弃前面的\r,只读\n
当写入文件时,程序会将\n转换成\r\n写入

位置指针/EOF

文件位置指针,指向当前读写到的位置,也就是读写到第几个字节

  • rewind(fp)

将文件中的位置指针重新定位到文件开头

部分函数再读取出错时也会返回EOF,有些情况下无法判断文件读取结束和是读取出错,feof() ferror()

  • fseek

移动文件指针到任意位置
fseek一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错
一般搭配fread\fwrite使用

int fseek(FILE* fp,long offset,int origin)
// offset 移动的字节数,offser 为正,向后移动;offset为负时,向前移动
// origin 为起始位置,表示从何处开始计算偏移量
// // 文件开头 0;当前位置 1;文件末尾 2 
  • feof

判断文件内部指针是否指向了文件末尾 feof(FILE *fp)

  • ferror

判断文件操作是否出错 ferror(FILE *fp)

if (ferror(fp))
{
	puts("出错")
}
else
{
	puts("读取完毕")
}

fopen/fclose

#include <stdio.h>
FILE *fp=fopen("filename","openmode");

// 打开错误就返回一个空指针NULL
FILE *fp;
if (fp=fopen("filename","openmode")==NULL) "ERROR..."

// 正常关闭的时候返回值为0,非0表示有错误发生
fclose(fp); 

fgetc/fputc

int fgetc(FILE *fp) // 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF(EOF在stdio.h中定义的宏,通常是一个负值)
int fputc(FILE *fp)

char ch;
FILE *fp=fopen("filename","filemode");
ch=fgetc(fp)
FILE *fp=NULL;
char ch;

if ((fp=fopen("F:\\c-learning\\file.txt","rt"))==NULL)
{
	puts("failed to open file");
	exit(0);
}
while ((ch=fgetc(fp))!=EOF)
{
	putchar(ch);
}
putchar('\n');

if (ferror(fp))
{
	puts("出错")
}
else
{
	puts("读取完毕")
}

fclose(fp);
char ch='a';
fputc(ch,fp);

// 知道遇到回车结束
while ((ch=getchar())!='\n')
{
	fputc(ch,fp);
}

fgets/fputs

读写一个字符串或一个数据块
char* fgets*(char *str,int n,FILE *fp) str为字符数组,读取成功时返回字符数组首地址
int fputs(char *str,FILE *fp) 向指定的文件写入字符串,写入成功返回非负数,失败返回EOF (注意:fputs不自动添加\0)

读取到的字符串会在末尾自动添加\0,n个字符也包括\0。实际值读取到了n-1个字符
在读取n-1个字符之前如果出现了 换行 或者读到了文件末尾,则读取结束。即不管n的值多大,fgets()最多只能读取一行数据,不能跨行
C语言中没有按行读取文件的函数,只能将n设置的足够大,每次读取到一行数据
fgets() 遇到换行符时 会将换行符一起存入到当前字符串 !!!

// 读取100个字符
#define N 101
char str[N];
FILE *fp=fopen("filename","filemode");
fgets(str,N,fp);

//例子
int main()
{
	FILE *fpl
	char str[N+1];
	if ((fp=fopen("filename","openmode"))==NULL)
	{
		puts("failed to open file");
		exit(0);
	}

	while (fgets(str,N,fp)!=NULL)
	{
		printf("%s\n",str);
	}

	fclose(fp);
	return 0;
}
if ((fp=fopen("F:\\c-learning\\file.txt","at+"))==NULL)
{
	puts("failed to open file");
	exit(0);
}
char *str2="hello\nqt\n";
fputs(str2,fp);

fclose(fp);

fread/fwrite

以数据块的形式读写文件,
注意,使用fread/fwrite时应该以 二进制 的形式打开
其实就是多个字节数据:一个字节、一个字符串、多行数据等

// 读写成功,返回读写成功的块数count
size_t fread(void* ptr,size_t size,size_t count,FILE *fp) // 读取文件
size_t fwrite(void* ptr,size_t,size,size_t couont FILE *fp) // 写入文件
// ptr 内存区块的指针,可以是数组、变量、结构体等,用于存放读取到的数据
// fwrite 的ptr 用于存放写入的数据
// size 表示 *每个数据块* 的字节数
// count 表示要读写的 *数据块的块数*
// fp 表示文件指针
// 每次读写size*count个字节的数据

// 读写成功,返回读写成功的块数count,如果返回值小于count
// fwrite 发生了写入错误,可以用ferror()函数检测
// fread 可能读到了文件末尾或发生错误,用ferror() 或feof()检测
int main()
{
    int N=5;
    int a[5]={1,2,3};
    int b[5];
    int i,size=sizeof(int);

    FILE *fp;
    if ((fp=fopen("F:\\c-learning\\file2.txt","rb+"))==NULL)
    {
        puts("file to open");
        exit(0);
    }

    // size 每个块的字节数; N 为块数
    fwrite(a,size,N,fp); // 把a写入到fp
    
    rewind(fp);
    fread(b,size,N,fp); // 把fp读到b
    

    for (int i=0;i<N;i++)
    {
        printf("%d\t",b[i]);
    }
    printf("\n");
    fclose(fp);

    return 0;
}

进阶-读写结构体

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define N 2

struct stu
{
    char name[10];
    int num;
    int age;
    float score;
}boya[N],boyb[N],*pa,*pb;

int main()
{
    FILE *fp;
    int i;
    pa=boya;
    pb=boyb;

    strcpy(pa[0].name,"zhangsan");
    strcpy(pa[1].name,"lisi");
    for (int i=0;i<N;i++)
    {
        pa[i].num=10;
        pa[i].age=20;
        pa[i].score=60.0;
    }

    if ((fp=fopen("./file.txt","wb+"))==NULL)
    {
        puts("fail to open file\n");
        exit(0);
    }

    fwrite(boya,sizeof(struct stu),N,fp); // 写入fp
    rewind(fp);
    fread(boyb,sizeof(struct stu),N,fp); // 从fp读入结构
    // fread(&someCertainStructInstance,sizeof()...)

    for (int i=0;i<N;i++)
    {
        printf("%d\n",pa[i].num);
        printf("%d\n",pa[i].age);
        printf("%f\n",pa[i].score);
    }
    
    fclose(fp);
    return 0;
}

fprintf/fscanf – 有问题

scanf\printf字符串输出到标准输入输出设备上(显示器)
fscanf把格式化字符串输出到执行文件
fprintf 把文件的内容输出到指定的数据

int fscanf(FILE *fp,char *format); // 返回参数列表中被成功赋值的参数个数
int fprintf(FILE *fp,char *format); // 返回成功写入的字符的个数,失败则返回负数

FILE *fp;
int i,j;
char *str,ch;
fscanf(fp,"%d,%s",&i,str); // 读取文件
fprintf(fp,"%d %c",j,ch); // 写入文件

文件复制

注意:除非对于文本格式,否则fopen一定要以二进制的形式打开

开辟一个缓冲区,不断从源文件中读取内容到缓冲区,每读取完一次就将缓冲区中的内容写入到新建的文件,直到把原文件的内容读取完
通常来讲,缓冲区的数据时没有结束标志的,如果缓冲区填充不满,–> 一般借助的就是fread 记录每次读取到的字节数

size_t fread(void* ptr,size_t size,size_t count,FILE *fp)
// 如果 size =1 ,则返回的就是读取的字节数,
int copyFile(char *fileRead,char *fileWrite);
int main()
{
    char fileRead[100]; // 要复制的文件名
    char fileWrite[100]; // 复制后的文件名

    strcpy(fileRead,"file1.wmv");
    strcpy(fileWrite,"file2.mp4");

    if (copyFile(fileRead,fileWrite))
    {
        printf("copy done ...");
    }
    else
    {
        printf("copy failed...");
    }

    return 0;
}

int copyFile(char *fileRead,char *fileWrite)
{
    FILE *fpRead;
    FILE *fpWrite;

    // 设置缓冲区大小
    int bufferLen=1024*4;
    // 开辟缓冲区
    char *buff=(char*)malloc(bufferLen);
    // 实际读取的字节数
    int readCount;

    if ((fpRead=fopen(fileRead,"rb"))==NULL || (fpWrite=fopen(fileWrite,"wb"))==NULL)
    {
        printf("connot open file");
        exit(1);
    }

    // 不断从fileRead读取内容,放在缓冲区,再将缓冲区的内容写入filewrite
    // readcount 表示实际是多少个字节
    while ((readCount=fread(buff,1,bufferLen,fpRead))>0)
    {
        // 下面两种写法都可以 !!!
        fwrite(buff,readCount,1,fpWrite); 
        // fwrite(buff,1,readCount,fpWrite);
    }

    free(buff); //手动释放动态内存
    fclose(fpRead);
    fclose(fpWrite);

    return 1;
}

ftell

long int ftell(FILE* fp); //获得文件内部指针(位置指针)距离文件开头的字节数
long fsize(FILE *fp);

int main()
{
    long size=0;
    FILE *fp=NULL;

    char filename[30]="file1.wmv";

    if ((fp=fopen(filename,"rb"))==NULL)
    {
        printf("failed open...");
        exit(1);
    }

    printf("%ld\n",fsize(fp));

    return 0;
}

long fsize(FILE *fp)
{
    long n;
    // 当前位置
    // fpos_t 保存文件的内部指针
    fpos_t fpos; // typedef long long fpos_t
    // 获取当前位置
    // fgetpos 获得文件内部指针,fsetpos 设置文件内部指针
    fgetpos(fp,&fpos); 
    // 设置到文件末尾
    fseek(fp,0,SEEK_END);

    // 获得文件内部指针(位置指针)距离文件开头的字节数
    n=ftell(fp);

    // 恢复之前的位置
    fsetpos(fp,&fpos);

    return n;
}

exit

#include<stdlib.h>
exit(1); // 异常退出,返回给操作系统
exit(0); // 正常退出 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值