c语言html文件按字符顺序读入,C语言完整笔记

本文详细介绍了C语言中宏定义的用法,包括宏替换规则、带参宏定义、嵌套宏、文件包含的原理及条件编译的应用。特别关注了结构体的定义、成员操作、指针使用以及动态存储分配。通过实例演示了如何避免常见错误,提高代码效率和可维护性。
摘要由CSDN通过智能技术生成

上例程序中首先进行宏定义,定义M来替表明达式(y*y+3*y),在s=3*M+4*M+5*M中做了宏调用。在预处理时经宏展开后该语句变为:

s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y);

但要注意的是,在宏定义中表达式(y*y+3*y)两边的括号不能少。不然会发生错误。如看成如下定义后:

#difineM y*y+3*y

在宏展开时将获得下述语句:

s=3*y*y+3*y+4*y*y+3*y+5*y*y+3*y;

这至关于:

3y2+3y+4y2+3y+5y2+3y;

显然与原题意要求不符。计算结果固然是错误的。所以在做宏定义时必须十分注意。应保证在宏代换以后不发生错误。

对于宏定义还要说明如下几点:

1)宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中能够含任何字符,能够是常数,也能够是表达式,预处理程序对它不做任何检查。若有错误,只能在编译已被宏展开后的源程序时发现。

2)宏定义不是说明或语句,在行末没必要加分号,如加上分号则连分号也一块儿置换。

3)宏定义必须写在函数以外,其做用域为宏定义命令起到源程序结束。如要终止其做用域可以使用# undef命令。

例如:

#definePI 3.14159

main()

{

……

}

#undef PI

f1()

{

……

}

表示PI只在main函数中有效,在f1中无效。

4) 宏名在源程序中若用引号括起来,则预处理程序不对其做宏代换。

【例9.2】

#defineOK 100

main()

{

printf("OK");

printf("\n");

}

上例中定义宏名OK表示100,但在printf语句中OK被引号括起来,所以不做宏代换。程序的运行结果为:OK这表示把“OK”当字符串处理。

5)宏定义容许嵌套,在宏定义的字符串中可使用已经定义的宏名。在宏展开时由预处理程序层层代换。

例如:

#define PI 3.1415926

#define S PI*y*y /* PI是已定义的宏名*/

对语句:

printf("%f",S);

在宏代换后变为:

printf("%f",3.1415926*y*y);

6)习惯上宏名用大写字母表示,以便于与变量区别。但也容许用小写字母。

7)可用宏定义表示数据类型,使书写方便。

例如:

#define STU struct stu

在程序中可用STU做变量说明:

STU body[5],*p;

#defineINTEGER int

在程序中便可用INTEGER做整型变量说明:

INTEGER a,b;

应注意用宏定义表示数据类型和用typedef定义数听说明符的区别。

宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是做简单的代换,而是对类型说明符从新命名。被命名的标识符具备类型定义说明的功能。

请看下面的例子:

#definePIN1 int *

typedef(int *) PIN2;

从形式上看这二者类似, 但在实际使用中却不相同。

下面用PIN1,PIN2说明变量时就能够看出它们的区别:

PIN1a,b;在宏代换后变成:

int *a,b;

表示a是指向整型的指针变量,而b是整型变量。

然而:

PIN2 a,b;

表示a,b都是指向整型的指针变量。由于PIN2是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟是做字符代换。在使用时要分外当心,以避出错。

8)对“输出格式”做宏定义,能够减小书写麻烦。

【例9.3】中就采用了这种方法。

#defineP printf

#defineD "%d\n"

#defineF "%f\n"

main(){

inta=5, c=8, e=11;

floatb=3.8, d=9.7, f=21.08;

P(DF,a,b);

P(DF,c,d);

P(DF,e,f);

}

9.2.2带参宏定义

C语言容许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。

对带参数的宏,在调用中,不只要宏展开,并且要用实参去代换形参。

带参宏定义的通常形式为:

#define宏名(形参表) 字符串

在字符串中含有各个形参。

带参宏调用的通常形式为:

宏名(实参表);

例如:

#defineM(y) y*y+3*y /*宏定义*/

……

k=M(5);/*宏调用*/

……

在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为:

k=5*5+3*5

【例9.4】

#defineMAX(a,b) (a>b)?a:b

main(){

intx,y,max;

printf("inputtwo numbers: ");

scanf("%d%d",&x,&y);

max=MAX(x,y);

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

}

上例程序的第一行进行带参宏定义,用宏名MAX表示条件表达式(a>b)?a:b,形参a,b均出如今条件表达式中。程序第七行max=MAX(x,y)为宏调用,实参x,y,将代换形参a,b。

宏展开后该语句为:

max=(x>y)?x:y;

用于计算x,y中的大数。

对于带参的宏定义有如下问题须要说明:

1.带参宏定义中,宏名和形参表之间不能有空格出现。

例如把:

#defineMAX(a,b) (a>b)?a:b

写为:

#define MAX (a,b) (a>b)?a:b

将被认为是无参宏定义,宏名MAX表明字符串 (a,b) (a>b)?a:b。宏展开时,宏调用语句:

max=MAX(x,y);

将变为:

max=(a,b)(a>b)?a:b(x,y);

这显然是错误的。

2.在带参宏定义中,形式参数不分配内存单元,所以没必要做类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,所以必须做类型说明。这是与函数中的状况不一样的。在函数中,形参和实参是两个不一样的量,各有本身的做用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。

3.在宏定义中的形参是标识符,而宏调用中的实参能够是表达式。

【例9.5】

#defineSQ(y) (y)*(y)

main(){

inta,sq;

printf("inputa number: ");

scanf("%d",&a);

sq=SQ(a+1);

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

}

上例中第一行为宏定义,形参为y。程序第七行宏调用中实参为a+1,是一个表达式,在宏展开时,用a+1代换y,再用(y)*(y) 代换SQ,获得以下语句:

sq=(a+1)*(a+1);

这与函数的调用是不一样的,函数调用时要把实参表达式的值求出来再赋予形参。而宏代换中对实参表达式不做计算直接地照原样代换。

4.在宏定义中,字符串内的形参一般要用括号括起来以免出错。在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,所以结果是正确的。若是去掉括号,把程序改成如下形式:

【例9.6】

#defineSQ(y) y*y

main(){

inta,sq;

printf("input a number: ");

scanf("%d",&a);

sq=SQ(a+1);

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

}

运行结果为:

inputa number:3

sq=7

一样输入3,但结果倒是不同的。问题在哪里呢? 这是因为代换只做符号代换而不做其它处理而形成的。宏代换后将获得如下语句:

sq=a+1*a+1;

因为a为3故sq的值为7。这显然与题意相违,所以参数两边的括号是不能少的。即便在参数两边加括号仍是不够的,请看下面程序:

【例9.7】

#defineSQ(y) (y)*(y)

main(){

inta,sq;

printf("inputa number: ");

scanf("%d",&a);

sq=160/SQ(a+1);

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

}

本程序与前例相比,只把宏调用语句改成:

sq=160/SQ(a+1);

运行本程序如输入值仍为3时,但愿结果为10。但实际运行的结果以下:

input a number:3

sq=160

为何会得这样的结果呢?分析宏调用语句,在宏代换以后变为:

sq=160/(a+1)*(a+1);

a为3时,因为“/”和“*”运算符优先级和结合性相同,则先做160/(3+1)得40,再做40*(3+1)最后得160。为了获得正确答案应在宏定义中的整个字符串外加括号,程序修改以下:

【例9.8】

#defineSQ(y) ((y)*(y))

main(){

inta,sq;

printf("inputa number: ");

scanf("%d",&a);

sq=160/SQ(a+1);

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

}

以上讨论说明,对于宏定义不只应在参数两侧加括号,也应在整个字符串外加括号。

5.带参的宏和带参函数很类似,但有本质上的不一样,除上面已谈到的各点外,把同一表达式用函数处理与用宏处理二者的结果有多是不一样的。

【例9.9】

main(){

inti=1;

while(i<=5)

printf("%d\n",SQ(i++));

}

SQ(inty)

{

return((y)*(y));

}

【例9.10】

#defineSQ(y) ((y)*(y))

main(){

inti=1;

while(i<=5)

printf("%d\n",SQ(i++));

}

在例9.9中函数名为SQ,形参为Y,函数体表达式为((y)*(y))。在例9.10中宏名为SQ,形参也为y,字符串表达式为(y)*(y))。 例9.9的函数调用为SQ(i++),例9.10的宏调用为SQ(i++),实参也是相同的。从输出结果来看,却大不相同。

分析以下:在例9.9中,函数调用是把实参i值传给形参y后自增1。 而后输出函数值。于是要循环5次。输出1~5的平方值。而在例9.10中宏调用时,只做代换。SQ(i++)被代换为((i++)*(i++))。在第一次循环时,因为i等于1,其计算过程为:表达式中前一个i初值为1,而后i自增1变为2,所以表达式中第2个i初值为2,两相乘的结果也为2,而后i值再自增1,得3。在第二次循环时,i值已有初值为3,所以表达式中前一个i为3,后一个i为4,乘积为12,而后i再自增1变为5。进入第三次循环,因为i 值已为5,因此这将是最后一次循环。计算表达式的值为5*6等于30。i值再自增1变为6,再也不知足循环条件,中止循环。

从以上分析能够看出函数调用和宏调用两者在形式上类似,在本质上是彻底不一样的。

6.宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。看下面的例子。

【例9.11】

#defineSSSV(s1,s2,s3,v) s1=l*w;s2=l*h;s3=w*h;v=w*l*h;

main(){

int l=3,w=4,h=5,sa,sb,sc,vv;

SSSV(sa,sb,sc,vv);

printf("sa=%d\nsb=%d\nsc=%d\nvv=%d\n",sa,sb,sc,vv);

}

程序第一行为宏定义,用宏名SSSV表示4个赋值语句,4 个形参分别为4个赋值符左部的变量。在宏调用时,把4个语句展开并用实参代替形参。使计算结果送入实参之中。

9.3文件包含

文件包含是C预处理程序的另外一个重要功能。

文件包含命令行的通常形式为:

#include"文件名"

在前面咱们已屡次用此命令包含过库函数的头文件。例如:

#include"stdio.h"

#include"math.h"

文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。

在程序设计中,文件包含是颇有用的。一个大的程序能够分为多个模块,由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件便可使用。这样,可避免在每一个文件开头都去书写那些公用量,从而节省时间,并减小出错。

对文件包含命令还要说明如下几点:

1.包含命令中的文件名能够用双引号括起来,也能够用尖括号括起来。例如如下写法都是容许的:

#include"stdio.h"

#include

可是这两种形式是有区别的:使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找;

使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。用户编程时可根据本身文件所在的目录来选择某一种命令形式。

2.一个include命令只能指定一个被包含文件,如有多个文件要包含,则需用多个include命令。

3.文件包含容许嵌套,即在一个被包含的文件中又能够包含另外一个文件。

9.4条件编译

预处理程序提供了条件编译的功能。能够按不一样的条件去编译不一样的程序部分,于是产生不一样的目标代码文件。这对于程序的移植和调试是颇有用的。

条件编译有三种形式,下面分别介绍:

1.第一种形式:

#ifdef标识符

程序段1

#else

程序段2

#endif

它的功能是,若是标识符已被 #define命令定义过则对程序段1进行编译;不然对程序段2进行编译。若是没有程序段2(它为空),本格式中的#else能够没有,便可以写为:

#ifdef标识符

程序段

#endif

2.第二种形式:

#ifndef标识符

程序段1

#else

程序段2

#endif

与第一种形式的区别是将“ifdef”改成“ifndef”。它的功能是,若是标识符未被#define命令定义过则对程序段1进行编译,不然对程序段2进行编译。这与第一种形式的功能正相反。

3.第三种形式:

#if常量表达式

程序段1

#else

程序段2

#endif

它的功能是,如常量表达式的值为真(非0),则对程序段1 进行编译,不然对程序段2进行编译。所以可使程序在不一样条件下,完成不一样的功能。

11结构体与共用体

11.1定义一个结构的通常形式

在实际问题中,一组数据每每具备不一样的数据类型。例如,在学生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整型;性别应为字符型;成绩可为整型或实型。显然不能用一个数组来存放这一组数据。由于数组中各元素的类型和长度都必须一致,以便于编译系统处理。为了解决这个问题,C语言中给出了另外一种构造数据类型——“结构(structure)”或叫“结构体”。 它至关于其它高级语言中的记录。“结构”是一种构造类型,它是由若干“成员”组成的。每个成员能够是一个基本数据类型或者又是一个构造类型。结构既是一种“构造”而成的数据类型,那么在说明和使用以前必须先定义它,也就是构造它。如同在说明和调用函数以前要先定义函数同样。

定义一个结构的通常形式为:

struct结构名

{成员表列};

成员表列由若干个成员组成,每一个成员都是该结构的一个组成部分。对每一个成员也必须做类型说明,其形式为:

类型说明符 成员名;

成员名的命名应符合标识符的书写规定。例如:

structstu

{

intnum;

charname[20];

charsex;

floatscore;

};

在这个结构定义中,结构名为stu,该结构由4个成员组成。第一个成员为num,整型变量;第二个成员为name,字符数组;第三个成员为sex,字符变量;第四个成员为score,实型变量。应注意在括号后的分号是不可少的。结构定义以后,便可进行变量说明。凡说明为结构stu的变量都由上述4个成员组成。因而可知, 结构是一种复杂的数据类型,是数目固定,类型不一样的若干有序变量的集合。

11.2结构类型变量的说明

说明结构变量有如下三种方法。以上面定义的stu为例来加以说明。

1.先定义结构,再说明结构变量。

如:

struct stu

{

int num;

charname[20];

charsex;

floatscore;

};

structstu boy1,boy2;

说明了两个变量boy1和boy2为stu结构类型。也能够用宏定义使一个符号常量来表示一个结构类型。

例如:

#define STU struct stu

STU

{

intnum;

charname[20];

charsex;

floatscore;

};

STU boy1,boy2;

2.在定义结构类型的同时说明结构变量。

例如:

struct stu

{

intnum;

charname[20];

charsex;

floatscore;

}boy1,boy2;

这种形式的说明的通常形式为:

struct结构名

{

成员表列

}变量名表列;

3.直接说明结构变量。

例如:

struct

{

intnum;

charname[20];

charsex;

floatscore;

}boy1,boy2;

这种形式的说明的通常形式为:

struct

{

成员表列

}变量名表列;

11.3结构变量成员的表示方法在程序中使用结构变量时,每每不把它做为一个总体来使用。在ANSIC中除了容许具备相同类型的结构变量相互赋值之外,通常对结构变量的使用,包括赋值、输入、输出、运算等都是经过结构变量的成员来实现的。

表示结构变量成员的通常形式是:

结构变量名.成员名

例如:

boy1.num即第一我的的学号

boy2.sex即第二我的的性别

若是成员自己又是一个结构则必须逐级找到最低级的成员才能使用。

例如:

boy1.birthday.month

即第一我的出生的月份成员能够在程序中单独使用,与普通变量彻底相同。

11.4结构变量的赋值

结构变量的赋值就是给各成员赋值。可用输入语句或赋值语句来完成。

【例11.1】给结构变量赋值并输出其值。

main()

{

struct stu

{

int num;

char *name;

char sex;

float score;

} boy1,boy2;

boy1.num=102;

boy1.name="Zhangping";

printf("inputsex and score\n");

scanf("%c%f",&boy1.sex,&boy1.score);

boy2=boy1;

printf("Number=%d\nName=%s\n",boy2.num,boy2.name);

printf("Sex=%c\nScore=%f\n",boy2.sex,boy2.score);

}

本程序中用赋值语句给num和name两个成员赋值,name是一个字符串指针变量。用scanf函数动态地输入sex和score成员值,而后把boy1的全部成员的值总体赋予boy2。最后分别输出boy2的各个成员值。本例表示告终构变量的赋值、输入和输出的方法。

11.5结构变量的初始化

和其余类型变量同样,对结构变量能够在定义时进行初始化赋值。

【例11.2】对结构变量初始化。

main()

{

struct stu /*定义结构*/

{

int num;

char *name;

char sex;

float score;

}boy2,boy1={102,"Zhang ping",'M',78.5};

boy2=boy1;

printf("Number=%d\nName=%s\n",boy2.num,boy2.name);

printf("Sex=%c\nScore=%f\n",boy2.sex,boy2.score);

}

本例中,boy2,boy1均被定义为外部结构变量,并对boy1做了初始化赋值。在main函数中,把boy1的值总体赋予boy2,而后用两个printf语句输出boy2各成员的值。

11.6结构数组的定义

数组的元素也能够是结构类型的。所以能够构成结构型数组。结构数组的每个元素都是具备相同结构类型的下标结构变量。在实际应用中,常常用结构数组来表示具备相同数据结构的一个群体。如一个班的学生档案,一个车间职工的工资表等。

方法和结构变量类似,只需说明它为数组类型便可。

例如:

struct stu

{

int num;

char *name;

char sex;

float score;

}boy[5];

定义了一个结构数组boy,共有5个元素,boy[0]~boy[4]。每一个数组元素都具备struct stu的结构形式。对结构数组能够做初始化赋值。

例如:

struct stu

{

int num;

char *name;

char sex;

float score;

}boy[5]={

{101,"Liping","M",45},

{102,"Zhangping","M",62.5},

{103,"Hefang","F",92.5},

{104,"Chengling","F",87},

{105,"Wangming","M",58};

}

当对所有元素做初始化赋值时,也可不给出数组长度。

11.7结构指针变量的说明和使用

11.7.1指向结构变量的指针一个指针变量当用来指向一个结构变量时,称之为结构指针变量。结构指针变量中的值是所指向的结构变量的首地址。经过结构指针便可访问该结构变量,这与数组指针和函数指针的状况是相同的。

结构指针变量说明的通常形式为:

struct结构名 *结构指针变量名

例如,在前面的例题中定义了stu这个结构,如要说明一个指向stu的指针变量pstu,可写为:

struct stu *pstu;

固然也可在定义stu结构时同时说明pstu。与前面讨论的各种指针变量相同,结构指针变量也必需要先赋值后才能使用。

赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。若是boy是被说明为stu类型的结构变量,则:

pstu=&boy

是正确的,而:

pstu=&stu

是错误的。

结构名和结构变量是两个不一样的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对它分配内存空间。只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间。所以上面&stu这种写法是错误的,不可能去取一个结构名的首地址。有告终构指针变量,就能更方便地访问结构变量的各个成员。

其访问的通常形式为:

(*结构指针变量).成员名

或为:

结构指针变量->成员名

例如:

(*pstu).num

或者:

pstu->num

应该注意(*pstu)两侧的括号不可少,由于成员符“.”的优先级高于“*”。如去掉括号写做*pstu.num则等效于*(pstu.num),这样,意义就彻底不对了。

下面经过例子来讲明结构指针变量的具体说明和使用方法。

【例11.5】

structstu

{

intnum;

char*name;

charsex;

floatscore;

}boy1={102,"Zhang ping",'M',78.5},*pstu;

main()

{

pstu=&boy1;

printf("Number=%d\nName=%s\n",boy1.num,boy1.name);

printf("Sex=%c\nScore=%f\n\n",boy1.sex,boy1.score);

printf("Number=%d\nName=%s\n",(*pstu).num,(*pstu).name);

printf("Sex=%c\nScore=%f\n\n",(*pstu).sex,(*pstu).score);

printf("Number=%d\nName=%s\n",pstu->num,pstu->name);

printf("Sex=%c\nScore=%f\n\n",pstu->sex,pstu->score);

}

本例程序定义了一个结构stu,定义了stu类型结构变量boy1并做了初始化赋值,还定义了一个指向stu类型结构的指针变量pstu。在main函数中,pstu被赋予boy1的地址,所以pstu指向boy1。而后在printf语句内用三种形式输出boy1的各个成员值。从运行结果能够看出:

结构变量.成员名

(*结构指针变量).成员名

结构指针变量->成员名

这三种用于表示结构成员的形式是彻底等效的。

11.7.2指向结构数组的指针

指针变量能够指向一个结构数组,这时结构指针变量的值是整个结构数组的首地址。结构指针变量也可指向结构数组的一个元素,这时结构指针变量的值是该结构数组元素的首地址。

设ps为指向结构数组的指针变量,则ps也指向该结构数组的0号元素,ps+1指向1号元素,ps+i则指向i号元素。这与普通数组的状况是一致的。

【例11.6】用指针变量输出结构数组。

struct stu

{

int num;

char*name;

charsex;

floatscore;

}boy[5]={

{101,"Zhouping",'M',45},

{102,"Zhangping",'M',62.5},

{103,"Lioufang",'F',92.5},

{104,"Chengling",'F',87},

{105,"Wangming",'M',58},

};

main()

{

structstu *ps;

printf("No\tName\t\t\tSex\tScore\t\n");

for(ps=boy;ps

printf("%d\t%s\t\t%c\t%f\t\n",ps->num,ps->name,ps->sex,ps->score);

}

在程序中,定义了stu结构类型的外部数组boy并做了初始化赋值。在main函数内定义ps为指向stu类型的指针。在循环语句for的表达式1中,ps被赋予boy的首地址,而后循环5次,输出boy数组中各成员值。

应该注意的是,一个结构指针变量虽然能够用来访问结构变量或结构数组元素的成员,可是,不能使它指向一个成员。也就是说不容许取一个成员的地址来赋予它。所以,下面的赋值是错误的。

ps=&boy[1].sex;

而只能是:

ps=boy;(赋予数组首地址)

或者是:

ps=&boy[0];(赋予0号元素首地址)

11.7.3结构指针变量做函数参数

在ANSI C标准中容许用结构变量做函数参数进行总体传送。可是这种传送要将所有成员逐个传送,特别是成员为数组时将会使传送的时间和空间开销很大,严重地下降了程序的效率。所以最好的办法就是使用指针,即用指针变量做函数参数进行传送。这时由实参传向形参的只是地址,从而减小了时间和空间的开销。

【例11.7】计算一组学生的平均成绩和不及格人数。用结构指针变量做函数参数编程。

struct stu

{

int num;

char *name;

char sex;

float score;}boy[5]={

{101,"Liping",'M',45},

{102,"Zhangping",'M',62.5},

{103,"Hefang",'F',92.5},

{104,"Chengling",'F',87},

{105,"Wangming",'M',58},

};

main()

{

structstu *ps;

voidave(struct stu *ps);

ps=boy;

ave(ps);

}

voidave(struct stu *ps)

{

intc=0,i;

floatave,s=0;

for(i=0;i<5;i++,ps++)

{

s+=ps->score;

if(ps->score<60)c+=1;

}

printf("s=%f\n",s);

ave=s/5;

printf("average=%f\ncount=%d\n",ave,c);

}

本程序中定义了函数ave,其形参为结构指针变量ps。boy被定义为外部结构数组,所以在整个源程序中有效。在main函数中定义说明告终构指针变量ps,并把boy的首地址赋予它,使ps指向boy数组。而后以ps做实参调用函数ave。在函数ave中完成计算平均成绩和统计不及格人数的工做并输出结果。

因为本程序所有采用指针变量做运算和处理,故速度更快,程序效率更高。

11.8动态存储分配

在数组一章中,曾介绍过数组的长度是预先定义好的,在整个程序中固定不变。C语言中不容许动态数组类型。

例如:

int n;

scanf("%d",&n);

int a[n];

用变量表示长度,想对数组的大小做动态说明,这是错误的。可是在实际的编程中,每每会发生这种状况,即所需的内存空间取决于实际输入的数据,而没法预先肯定。对于这种

问题,用数组的办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数能够按须要动态地分配内存空间,也可把再也不使用的空间回收待用,为有效地利用内存资源提供了手段。

经常使用的内存管理函数有如下三个:

1.分配内存空间函数malloc

调用形式:

(类型说明符*)malloc(size)

功能:在内存的动态存储区中分配一块长度为"size"字节的连续区域。函数的返回值为该区域的首地址。

“类型说明符”表示把该区域用于何种数据类型。

(类型说明符*)表示把返回值强制转换为该类型指针。

“size”是一个无符号数。

例如:

pc=(char *)malloc(100);

表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pc。

2.分配内存空间函数 calloc

calloc也用于分配内存空间。

调用形式:

(类型说明符*)calloc(n,size)

功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。

(类型说明符*)用于强制类型转换。

calloc函数与malloc 函数的区别仅在于一次能够分配n块区域。

例如:

ps=(struet stu*)calloc(2,sizeof(struct stu));

其中的sizeof(struct stu)是求stu的结构长度。所以该语句的意思是:按stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps。

2.释放内存空间函数free

调用形式:

free(void*ptr);

功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由malloc或calloc函数所分配的区域。

【例11.8】分配一块区域,输入一个学生数据。

main()

{

structstu

{

intnum;

char*name;

charsex;

floatscore;

} *ps;

ps=(struct stu*)malloc(sizeof(struct stu));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值