c 存储类 链接 内存管理 文件输入输出 结构类型,函数指针

 存储类 链接 内存管理 文件输入输出 结构类型,函数指针

存储类、链接和内存管理

 

五种不同类型的存储模型,可以按照一个变量的存储时期,作用域,以及它的链接来描述它。

 

作用域

 

描述了程序中可以访问一个标识符的一个或多个区域。有:

 

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 修饰的外部变量。

 

存储类说明符

 

Autoregister,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()返回0null

 

文件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-posftell的返回值

 

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字节大小的数据块从缓冲区写入到文件。要保存一个包含10double值得数组可以这样写:

 

  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}

 

 

 

由于定义时valueauthor之后,所以。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是一个指向具有10int值得数组的指针。 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};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值