存储类 链接 内存管理 文件输入输出 结构类型,函数指针
存储类、链接和内存管理
五种不同类型的存储模型,可以按照一个变量的存储时期,作用域,以及它的链接来描述它。
作用域
描述了程序中可以访问一个标识符的一个或多个区域。有:
1. 代码块作用域:花括号中的:{}、函数形参、FOR,WHILE,IF语句。
2. 函数原型作用域:int mighty(int mouse,double large)从变量定义处一直到原型声明的末尾。如变长数组参数的定义。
3. 文件作用域:一个在所有函数之外定义的变量具有文件作用域。具有文件作用域的变量从它定义处到包含该定义的文件结尾都是可见的。
还有一个是函数作用域,它只适用于goto语句使用的标签。意味着一个特定函数中的goto标签对该函数中任何地方的代码都是可见的。无论标签出现在哪一个代码块中。
链接
1. 外部链接:文件作用域的非static变量具有外连接。
2. 内部链接:文件作用域的static变量有内链接。
3. 空链接:代码块作用域或者函数原型作用域的变量有空链接。
储存期
1. 静态储存期:文件作用域的变量
2. 自动存储期:代码块作用域的变量。
以三种分类的5种类型的存储类
存储类 |
时期 |
作用域 |
链接 |
声明方式 |
自动 |
自动 |
代码块 |
空 |
代码块 |
寄存器 |
自动 |
代码块 |
空 |
代码块 |
具有外部链接的静态 |
静态 |
文件 |
外部 |
所有函数之外 |
具有内部链接的静态 |
静态 |
文件 |
内部 |
所有函数之外关键字 static |
空链接的静态 |
静态 |
代码块 |
空 |
代码块内,关键字static |
不带{}的代码块
由于for if while可以定义变量,也可以省略大括号,也属于代码块作用域。
自动变量的初始化
没有显式初始化自动变量,否则它不会自动初始化,不要指望这个值是0.
寄存器变量
使用关键字register int quick;
仅是一个请求。编译器会权衡请求与寄存器的个数或者可用高速内存的数量之间做。所以你可能达不到自己的愿望。其它和自动变量时一样的。
具有代码块作用域的静态变量
静态是指变量的位置固定不动,具有文件作用域的变量具有静态存储时期。代码块作用域的静态变量具有空链接和静态存储时期。对于函数参量不能使用static:
Int wontwork(static int flu);//不允许
具有外部链接的静态变量
具有外部链接的静态变量具有文件作用域、在使用外部变量的函数中使用extern再次声明它,如果变量定义在别的文件中,使用extern来声明变量就是必须的。引用声明的时候可以忽略数组的大小。
外部变量的初始化
外部变量的初始化可以被显式的初始化,不可以有变量的表达式,如果不对外部变量进行初始化,它们将自动被赋值初值为0;
定义和声明
定义声明和引用声明,一个外部变量只可进行一次初始化,而且一定是在变量被定义时进行。
Extern char permis = ‘Y’;//错误的。
具有内部链接的静态变量
Static 修饰的外部变量。
存储类说明符
Auto、register,static,extern以及typedef.不可以在一个声明中使用一个以上的存储类说明符。
存储类和函数
外部的:默认或extern
静态的:static
内联函数:
程序设计中需要知道原则,尽可能保持每个函数的内部工作对该函数的私有性,只共享那些需要共享的变量。除了自动类型以外。
Windows命令行运行
分配内存 malloc(),free()
运行时分配内存 malloc();对应释放内存free();
若没释放,程序结束后,会自动释放内存。
Calloc()函数,特性是将全部位置置为0(某些硬件系统中,浮点值0 不是用全部位为0来表示的),
Free 也可以用来释放calloc分配的内存。
动态内存分配与变长数组
区别是变长数组是自动存储,可以自动分配和释放。
Maclloc创建的数组不必局限在一个函数中。
多维数组
Int n = 5;
Int m = 6;
Int ar2[n][m]; //变长数组nXm
Int (* p2) [6]; //c99之前可以使用
Int (* p3) [m]; 、、要求变长数组支持
P2 =(int (*)[6])malloc(n*6*sizeof(int));
P3 = (int (*)[m]) malloc (n*m*sizeof(int));
//上面的表达式也要求变长数组的支持
存储类与动态内存分配
具有外部链接,具有内部链接,具有空链接的静态变量;
一个自动变量,一般实现为一个堆栈。
一个动态分配内存,malloc free 可能导致碎片。
Ansic的类型限定词
Const:
1) 普通变量,定义的变量是只读的,并且只能在声明的时候赋值。
2) 指针分2种:a.指针指向的值是不可变的(const int * ptr == int const * int ptr),b.指针是不可变的 (int * const ptr);c.const int * const ptr;
对函数的参量使用const。
Volatile:
Restrict(c99):
C99赋予限定词一个新属性幂等,就是声明中可以不止一次的使用一限定词,多余的将被忽略掉。
Const const 等价于const.
Typedef cons tint zip;
Const zip q= 8;
常量放到include文件里要加static,
要不就是引用声明,定义声明放在源文件里。上面的声明有弊端,不能声明过大的数组。或变量。
类型限定词
1) Volatile:告诉编译器这个变量除了程序改变以外还可能被其它的代理改变。
2) Restrict:只用于指针,并表明指针是访问一个数据对象的唯一且初始的方式。
如:int * restrict restar = (int *) malloc(10 * sizeof (int));
Void ofmouth(int * const a1,int * restrict a2,int n);
Void ofmouth (int a1[const],int a2[restrict],int n);
文件输入输出
C中文件有两种视图,文本视图和二进制视图。
IO级别:低级I/O使用操作系统提供的基本i/o服务,标准高级i/o使用一个标准的C库函数包和stdio.h头文件中定义。因为无法保证所有的操作系统都可以用相同的低级I/O模型表示。Ansi c只支持标准I/O包,
标准I/O
标准输入
标准输出
标准错误输出
Fopen模式字符串参数
R |
只读 |
W |
先截取文件的长度,没有文件先创建,然后写入 |
A |
没有先创建,在文件的尾部追加内容 |
R+ |
可以更细也可以读取和写入文件 |
W+ |
可以更新 但先截取为0长度。 |
A+ |
可以更新 但是只能追加 |
Rb wb ab ab+ a+b wb+ w+b ab+ a+b |
|
Fopen()返回一个文件指针,这个指针不指向实际文件,而是指向一个关于文件信息的数据包。其中包括文件I/O缓冲区的当前缓冲的能力以及所使用的文件,不能打开文件,返回一个空指针,如果参数为NULL程序将退出,磁盘满,文件名非法,存取权限不够或者硬件问题等都会导致函数执行失败。
Getc() putc() EOF
Fclose()返回0或null
文件IO FPRINTF() fscanf() fgets fputs
While(gets(words)!=null&&words[0] != ‘\0’)
Fprintf(fp,”%s”,words);
While(fscanf(fp,“%s”,words) == 1)
Puts(words);
Fgets(buf,max,fp);自动在结尾添加空字符串。Gets()函数读取到换行符后将它丢弃。
Fputs不添加换行 puts添加换行。
随机读取 fseek() ftell()
第一个参数是文件指针,第二个参数成为偏移量,
模式 |
偏移量的起点 |
|
SEEK_SET |
文件开始 |
|
SEEK_CUR |
当前位置 |
|
SEEK_END |
文件结束 |
|
如果一切正常fseek()返回值为0,如果有错误出现,例如视图移动超出文件范围,则fseek()的返回值为-1;
Ftell函数为long类型,它返回文件的当前位置。
Fseek(fp,0l,EEK_END);
LAST = FTELL(FP);
把文件开始到文件结尾的字节数目赋给last。
For(count =1L;count <= last;count++)
{
Fseek(fp,-count,SEEK_END);
Ch = getc(fp);
}
在文本模式下,ftell返回一个将\r\n看成一个字节的计数值。
可移植性
1) 对于二进制模式,c实现不需要支持seek_end模式,
2) 文本模式中,可以确保有效的fseek()调用,
Fseek(file,0l,SEEK_SET) |
到文件的开始 |
Fseek(file,0l,SEEK_CUR) |
当前位置不动 |
Fseek(file,0l,SEEK_END) |
到文件的结尾 |
Fseek(file,FTELL-pos,SEEK_SET) |
到距文件开始处ftell-pos字节的位置,ftell-pos是ftell的返回值 |
Int fgetpos(FILE * restrict stream,fpos_t * restrict pos);在pos所指的位置放置一个fpos_t值,这个值描述了文件中的一个位置。如果成功返回0.否则返回一个非零值。
Int fsetpos(FILE * stream,fpos_t * pos);该函数使用pos指向的位置上的那个fpos_t值设定文件指针指向该值所指示的位置。成功0,否则返回一个非零值。
Fp文件指针通常包括一个数据结构:
一个文件位置指示器,以确定在流中的当前位置,
错误指示器和文件结尾指示器
一个指向缓冲区起始处的指针
一个文件标识符
一个记录实际复制到缓冲区中的字节数计数器
其它IO函数
Int ungetc(int c,FILE * FP) 放回输入流,
Int fflush(FILE *fp) 把缓冲区中任何未写的数据发送到一个由fp指定的输出文件中去,这个过程称为刷新缓冲区。,只要最近一次使用流的操作部是输入操作,就可以对一个更新流使用这个函数。
Int setvbuf(FILE * restrict fp,char * restrict bu ,int mode,size_t size):
二进制IO
Fread()
Fwrite()
缺少例子
Size_t fwrite(const void * restrict ptr,size_t size,size_t nmemb,FILE * restrict fp)函数
Char buffer[256];
Fwrite(buffer,256,1,fp);
将一块256字节大小的数据块从缓冲区写入到文件。要保存一个包含10个double值得数组可以这样写:
Double earnings[10];
Fwrite(earnings,sizeof(double),10,fp)
这一调用将earnings数组中的数据写入文件,数据分成10快,每块都是double大小。
第一个参数不是一个固定的类型。返回成功写入的项目数,正常情况下,它与NMEMB相等,不过如果有写入错误的话,返回值就会小于NMEMB。
Size_t fread()
Size_t fread(void * restrict ptr,size_t size,size_t nmemb,FILE *restrict fp);
与上一个的区别是一个是读取,一个是写入。
Int feof(FILE *fp) and int ferror(FILE *fp)函数
当标准的输入函数返回EOF时,通常表示已经到达了文件的结尾,可是这也有可能表示发生了读取错误。
使用feof(),ferror()函数可以区分这两种可能性,如果最近一次输入调用检测到文件结尾,feof()返回一个非零值,否则返回零值,如果发生了错误读写,ferror()函数返回一个非零值,否则返回零值。
结构和其它数据形式
把数组和指针字符串理解后,其它都是变形。
结构相当于JAVA中的类,sql中的结构,和java不一样的是定义变量时前面还要加上struct 关键词
Struct book library,doyle,panshin,* ptbook;
Struct book{ //BOOk 可以省略
Char title[MAXTITL];
Char author[MAXAUTL];
Float value;
} library;
初始化:
Struct book library = {
“The Pirate and the Devious Damsel”,
“Renee vivotte”,
1.95
}
花括号,逗号分割。
如果初始化具有静态存储时期的结构,初始化项目列表中的值必须是常量表达式,如果存储时期是自动,列表中的值就不必是常量了。
&Library.value 点具有更高的优先级,所以等同于&(library.value);
Struct book gift ={
.value = 25.99,
.author = “James broadfool”,
.title = “Rue for the Toad”
};
可以只赋值其中的一部分。也可以像数组那样覆盖前面的
Struct book gift = {.value = 18.90,
.author = “Philionna Pestle”,
0.25}
由于定义时value在author之后,所以。25覆盖了前面的18.90;
嵌套结构
图中2处等价说明 him->handle 意思是取指针him所指向结构的handle属性。
结构的名字不是结构的地址。
向函数传递结构
传递结构中的基本类型,像平常的基本类型。
传递结构的地址。形参时指针。
使用结构,调用时是复制副本。
结构可以赋值,数组不可以。
如结构:
O_data = n_data;
即使成员中有数组也照样完成赋值。也可以把一个结构初始化另一个同样类型的结构。
示例:指针和传结构本身
指针版本说明指针传递的是地址,操作里面的数据,会影响被传递过来的值。
传递结构本身版本。
结构,还是指向结构的指针
指针作为参数有两个优点:它即工作在较早的C实现上,也工作在新的C实现上,而且执行起来很快;只需传递一个地址,缺点是缺少对数据的保护。可用const来解决。
结构作为参数传递优点是安全性,缺点较早的C中不支持,和浪费空间。
结构中使用字符串还是数组
如果需要一个结构来存储字符串,请使用字符数组成员,存储字符指针有它的用处,但也有被严重误用的可能。
Struct names{
Char first[20];
Char last[20];
};
Struct pnames{
Char *first;
Char *last;
}
Strtct names veep ={“Talia”,”Summers”};
Struct pnames treas ={“Brad”,”fallingjaw”};
这事正确的。
但是如果用来输入函数可能出现问题:
Scanf(“%s”,treas.last);
但是也可以用malloc来生成:
Char temp[20];
Gets(temp);
Pst->=(char *) malloc(strlen(temp)+1);
Strcpy(pst->fname,temp);
但是要清理请求的空间。
复合结构
(Struct book) {“dawei”,”han”,20.00}
也可取地址
& (Struct book) {“dawei”,”han”,20.00}
伸缩性结构
Struct flex{
Int count;
Double average;
Double scores[];
};
分配的时候要:
Pf1=malloc(sizeof(struct flex)+5*sizeof(double));
高级数据表示:链表,堆栈,队列,树有n层那总共的节点为2n次方减1;
1. 联合
Union hold{
Int digit;
Double bigfl;
Char letter;
};
声明时创建最大类型的大小。
Union hold valA;
valA.letter = ‘R’;
union hold valB = valA;
union hold valc ={88};
union hold vald = {.bigfl = 118.2};
间接运算符和指针也可以运用到联合。
1. 枚举
Enum spectrum{red,orange,yellow,green,blue,violet};
Enum spectrum color;
Color = blue;
指定值:
Enum levels{low = 100,medium = 500,high= 1000};
C使用术语名字空间来标识识别一个名字的程序部分。
名字相同但是不同的作用域的两个变量不会冲突。
结构标记,联合标记,以及枚举标记都共享一个名字空间,并且这个名字空间与普通变量使用的名字空间是不同的。
如:
Struct rect{double x;double y;}
Int rect;//c z中不会引起冲突
2. Typedef
工具是一种高级数据特性,它使您能够为某一类型创建您自己的名字。
与#define 有相似的地方也有不同之处,
1) Typedef 仅限于类型,而不是对值。
2) Typedef 的解释由编译器,而不是预处理器。
3) 虽然它的范围有限,但在其受限范围内,typedef比#define更灵活。
Typedef unsigned char BYTE;
BYTE X,Y[10],*Z;
#define BYTE unsigned char;
类似 typedef unsigned char BYTE;
Typedef char * STRING;
STRING name,sign; == char * name,* sign;
和 define STRING char *;
STRING name,sign;
== char * name,sign; 只有name是一个指针。
结构:
Typedef struct complex{
Float real;
Float imag;
} COMPLEX;
这样就可以用类型COMPLEX代替struct complex 来表示复数。
Typedef char(* FRPTC())[5];
这把FRPTC声明一个函数类型,该类型的函数返回一个指向含有5个元素的char 数组的指针。记住它不创建新的类型;它只是创建了便于使用的标签,这意味着,创建的String类型的变量可以作为参数传递给需要char指针类型参数的函数。
Ps:
1. 和()具有相同的优先级,这个优先级高于间接运算符*;
2. 【】()都是从左到右进行结合的。
3. 因为上面的原因所以下面的声明在应用方括号之前先将*和rusks组合在一起。这意味着rusks是一个指向具有10个int值得数组的指针。 Int (* rusks) [10];
函数指针同样,必须说明函数的参量类型和返回类型。
Void ToUpper(char *);声明指针为:
Void (*pf)(char *);
这样PF就是一个指向函数 void ToUpper(char *) 的指针,可以用(*fp)来代替函数名。
这样就可以赋值了
Pf = ToUpper;后面不要加括号。
这样由于PF是指针所以可以重新赋值,ANSI C接受这两种。
那么一个函数原型:
Void show(void (* fp) (char *),char * str);
可以下面调用:
Show (ToLower,mis); //fp = ToLower
Show (pf,mis); //show()使用pf指向函数;fp=pf;
Show函数来使用函数的指针呢?
(*fp)(str);
Puts(str);
如果带有函数的返回值则把返回值传递给了调用函数。
Ps:
这种情况下可以使用:
Typedef void(* V_FP_CHARP)(char *);
Void show(V_FP_CHARP fp,char *);
V_FP_CHARP pfun;
V_FP_CHARP arpf[4] = {ToUpper,ToLower,Transpose,Dummy};
相关推荐
### C语言中文件的输入输出知识点详解 #### 一、文件基本概念与分类 - **文件定义**: 在C语言中,文件通常指的是存储在外部介质(例如磁盘、磁带等)上的数据集合。 - **操作系统管理**: 操作系统以文件为单位对...
本章我们对C语言文件输入输出的基本概念、函数和应用进行了详细的介绍,包括文件的基本概念、文件的分类、文件的顺序读写、fgetc函数、fputc函数、feof函数等。这些知识点对于C语言程序员来说是非常重要的,它们可以...
在C语言编程中,文件的输入输出是不可或缺的一部分,它允许程序员将程序处理的数据持久化存储到磁盘或其他外部设备上,以便后续使用或长期保存。本章重点讲解了如何在C语言中进行文件的操作,包括打开、关闭、读取和...
Matlab 输入输出函数可以分为以下几类: 一、文件操作函数 * fclose:关闭文件 * fopen:打开文件 * fread:从文件中读入二进制数据 * fwrite:把二进制数据写入文件 * fgetl:逐行从文件中读取数据并放弃换行符 *...
本资源摘要信息对应的知识点涵盖了C语言文件编程的基础理论和编程技术,涉及到文件的概述、分类、文件系统、缓冲文件输入输出系统、非缓冲文件输入输出系统、文件类型结构、文件指针、文件的打开与关闭、文件的读写...
在C语言中,文件的输入输出操作是通过标准库提供的函数实现的,这些函数建立和管理与文件关联的“流”。 流是C语言中进行文件操作的核心概念,它代表了程序与文件之间信息传输的通道。流分为正文流(字符流)和二...
这份“C++课件”涵盖了C++的基础知识,包括函数、输入输出流、数组、指针以及数据类型和表达式等核心概念。这些知识点是学习C++编程的基石,对于初学者来说至关重要。 1. 函数:函数是C++中组织代码的基本单元,它...
设备文件则简化了用户对输入输出设备的管理,将每个设备视为一个文件,便于操作。 文件根据内容的存储方式可分为ASCII文件(文本文件)和二进制文件。文本文件以字符序列的形式存储数据,每个字节对应一个ASCII字符...
指针的使用可以实现动态内存分配、函数指针、数组指针等。 在上面的代码中,使用了指针来实现字符串的逆序输出。使用 char 数组来存储输入的字符串,然后使用指针来交换字符串中的字符,从而实现逆序输出。 内存...
C函数可以大致分为以下几类:输入/输出函数、数学函数、字符串处理函数、内存管理函数、错误处理函数、时间日期函数、文件操作函数等。 3. **输入/输出函数** 包括`printf`和`scanf`等,用于标准输入输出,`fputs...
普通文件通常存储在如磁盘、磁带这样的外部存储介质上,设备文件则对应输入输出设备,如键盘、显示器、打印机等。按数据的组织形式,文件可以是文本文件或二进制文件。文本文件以ASCII码形式存储,每个字节代表一个...
3. 使用数组或指针类型作为参数,注意输入输出方向。 4. 注意内存管理,避免缓冲溢出和空指针异常。 5. 可能需要进行类型转换以匹配C/C++和C#之间的类型差异。 通过以上步骤,你可以在C#项目中安全地调用C/C++ DLL...
- C语言支持函数指针,这使得可以将函数作为参数传递给其他函数,或者将函数存储在变量中,增强了程序的灵活性。 **内联函数:** - 内联函数是为优化性能而设计的,它试图在编译时展开函数调用,以减少函数调用带来...
### C语言文件系统详解:数据输入与输出 ...通过对`FILE`结构体、`fopen`和`fclose`函数以及各种读写操作函数的学习和应用,开发人员能够有效地管理数据的输入输出,实现数据的持久化存储和检索。
而多字节输入输出函数则是处理字符串,它可以一次性读写多个字符。 除了单字节和多字节的函数外,C语言还提供了格式化的读写函数fprintf和fscanf,它们允许用户按照指定的格式进行输入输出,非常适用于各种数据的...
在C程序设计中,文件的输入输出是编程中不可或缺的一部分,尤其在处理数据持久化存储和数据交换时显得尤为重要。本章重点讲解了C语言中关于文件操作的基础知识,包括文件的基本概念、打开与关闭文件、顺序读写与随机...
4.3 数据输入输出的概念及在 C 语言中的实现 54 4.4 字符数据的输入输出 54 4.4.1 putchar 函数(字符输出函数) 54 4.4.2 getchar函数(键盘输入函数) 55 4.5 格式输入与输出 55 4.5.1 printf 函数(格式输出函数...
4.3 数据输入输出的概念及在 C 语言中的实现 54 4.4 字符数据的输入输出 54 4.4.1 putchar 函数(字符输出函数) 54 4.4.2 getchar函数(键盘输入函数) 55 4.5 格式输入与输出 55 4.5.1 printf 函数(格式输出函数...
在C语言中,对文件的输入输出是编程中不可或缺的一部分,尤其在处理数据持久化、存储及传输时显得尤为重要。本章将详细讲解C语言中关于文件操作的相关知识点。 首先,我们来了解一下C语言的编译过程。一个C程序通常...