C语言sprintf与sscanf函数

2024-06-12

C语言sprintf与sscanf函数(共13篇)

C语言sprintf与sscanf函数 篇1

函数部分的学习被安排在三大程序控制结构之后,有利于学生对函数知识点的系统学习,也能够使学生深刻理解结构化程序设计的全局思想,在大学期间开设《C语言程序设计》课程的主要目的是培养学生充分利用信息时代的优势,通过编程解决实际问题的能力。实际上C程序的基本组成单位是函数,课程所有知识点的学习最终都将落实到编写各种函数来进行验证和实现。因此,学生只有灵活掌握函数的运用,才能为后续学习奠定坚实基础。

二、传统函数教学存在的问题

传统函数教学流程如下:

其一,函数定义及其形式;

其二,函数形参,return语句,函数返回值;

其三,函数声明,格式,位置;

其四,函数调用,函数实参等。这种传统的教学流程过于强调函数语法知识,概念和规则的讲解,这样导致学生似懂非懂,与教学理念相违背,为了适应教学要求,突出以学生为主,教员为辅的教学理念,主要探讨函数的教学设计及实践[1]。

三、新的函数教学设计及方法

函数教学主要以发现问题、分析问题、解决问题为思路开展教学,以启发、引导、对比和总结为辅帮助学生分析问题解决提出的问题。不仅可以让学生带着兴趣学习,也会加深学生对知识点的深刻理解及灵活应用。本堂课的教学流程是:交代任务;提出问题、发现问题、解决问题;巩固练习[2]。

第一,提出问题、分析问题、解决问题。学生只学过在main函数中编写简单程序,于是向学生抛出问题:复杂程序应如何进行组织和设计?在此,给学生一些时间利用main函数来解决给定任务,解决完毕和同学生一起验证结果,然后引导学生分析这种解决方式在实际中存在的问题,并启发学生思考解决此问题的最佳方法,从而引出学习的内容——函数法。接着再次引导学生用函数法重新解决给定的任务,最后将解决该任务的两种方法进行对比,让学生讨论、体会并总结出复杂程序的设计方法。

第二,巩固练习。在这个环节,笔者设计了带有陷阱的小例子:通过编写swap函数来实现主函数中两个整数的交换,给学生一些时间讨论、验证结果,引导学生分析其中的问题,同时引出函数调用流程这一知识点,并通过动画演示的方式帮助学生分析函数调用流程。

四、函数教学实践

第一,利用结构化程序设计原则设计复杂程序。在main函数中编程解决问题的基础上引导学生独立解决给定任务,发现大部分学生都能很好地编写出程序,让学生观察上述代码,联系实际启发引导学生讨论并总结出如下问题:

其一,代码冗余;

其二,不易维护;

其三,可靠性差;

其四,可读性差。

在此基础上启发学生剖析问题并让学生给出解决方案,即main函数没有能力独自解决整个任务,这样很自然引出复杂C程序组织和设计的原则:自顶向下,逐步细化,模块化设计,结构化编码。在这一原则的指导下,人们可以将任意复杂任务分解若干子任务。程序设计时每个子任务看成独立模块,每个程序设计人员分别完成一个或多个模块。人们称这样的程序设计方法为“模块法”,在编写代码时每个模块对应编写一个函数。最后选择一种结构化语言对各个函数进行编码,然后在机器上反复调试修改验证。函数定义形式函数首部函数体含义作用及功能功能具体实现对应知识点函数名,函数形参,返回值如:longfact(intm)声明语句,执行语句第二,函数定义及调用。

其一,函数定义形式;

其二,函数调用。通过知识迁移将以前学过的使用库函数的语句引导学生总结出函数调用的形式:①无返回值;②带返回值。在此,详细讲解实参与形参的区别及联系,接着给学生一些时间利用函数调用的方法改写程序并将两种解决问题的方法进行对比,让学生观察总结出程序模块化的优点。

第三,进阶练习。布置小任务:编写swap函数来实现main函数中两个整数交换。在学生编写时,教员应根据每位学生的情况进行适当指导提示,并及时纠正学生们共性的错误。提示学生思考计算机本身是如何执行程序中的main和swap函数的,引导学生回答并指正。通过画图这种直观的方式,让学生理解函数调用的执行流程,同时也加深了学生对函数模块独立性的深刻理解,将抽象的知识变得通俗易懂。

五、结束语

综上所述,C语言中函数涉及的知识杂而多样,在教学中采用启发、引导、对比等多种教学方法,不拘泥于知识点的讲解,而是从程序设计全局角度出发,以分工合作思想为切入点,探讨了现实中复杂程序编写多个函数来实现的必要性,发现问题,解决问题,让学生主动学习的思路展开,实践证明该思路符合学生接受知识的思维习惯,这不仅很好地调动学生学习的积极性,从而培养了学生独立思考解决问题的能力。

参考文献:

C语言sprintf与sscanf函数 篇2

关键词:函数调用,实际参数,形式参数,参数传递

函数是C语言中的基本组成单位, 一个较大的C程序一般可分为若干个程序模块, 实现某一特定功能的模块主要由函数来完成。所以, 学习C语言程序设计要善于利用函数, 一来可以减少重复编写程序段的工作量, 二来可以方便的实现模块化程序设计。但是笔者在实际的教学过程中发现, 学生在学习函数这部分知识时显的比较吃力。因为在具体使用函数时, 要涉及到函数参数传递问题, 而参数类型多种多样, 使学生对函数问题产生了很多疑惑, 根据教学实际, 笔者对函数调用与参数传递问题进行了总结, 以便大家对函数问题的理解。

1、主调函数与被调函数

计算机在执行C程序时总是从main函数开始, 如果遇到要调用某个函数, 则主函数称为主调函数, 被调用者称为被调函数。一个C程序可由一个main函数和若干个其他函数构成, main函数用来解决整个问题, 它调用解决小问题的其他函数, 其他函数也可以相互调用。调用者就是主调函数, 被调者就是被调函数, 应当注意, main函数只能由系统调用。

2、实际参数与形式参数

在调用有参函数时, 主调函数和被调函数之间有数据传递关系。在主调函数中进行函数调用时, 函数名后面括弧中的参数称为实际参数, 简称实参。在定义函数时函数名后面括弧中的变量名就是形式参数, 简称形参。即实参出现在函数调用中, 形参出现在函数定义中。主调函数通过函数调用将实参中的数据传递给被调函数的形参, 从而实现函数间的数据传递。另外实参与形参进行数据传递时, 系统要求实参与形参在数量、类型、顺序应严格保持一致, 这一点在使用上要特别注意。

3、变量存储类型与作用域

主调函数和被调函数数据传递往往要通过变量进行, 不同的变量类型影响数据的处理结果。C语言中变量按存储时分配的空间不同可以分为自动变量, 寄存器变量, 静态变量和外部变量。按变量的生命周期可以分为局部变量和全局变量, 局部变量是在一个函数内部定义的变量, 在存储器的动态存储区进行分配空间, 作用域只在本函数内部有效, 比如在主函数里定义的自动变量, 寄存器变量, 函数中的形式参数等都属于局部变量, 在函数调用时, 系统才为其分配存储空间, 函数调用结束后, 空间释放。而对于静态型局部变量是程序编译时由系统在存储器的静态存储区为其分配存储空间, 函数调用结束后, 空间不释放, 其值要保留到程序退出。全局变量是在程序整个运行期间都要占用内存, 所以它是全程有效, 贯穿于主调函数与被调函数全过程, 其值也要保留到程序退出为止。

4、参数传递的本质与属性

函数参数传递的过程, 本质上是一种赋值过程即值传递过程, 在调用函数之前, 函数的每个实际参数将被复制, 复制的值代替对应的形式参数。所以形参实际上得到的不是实参本身, 而是实参的值或者实参所代表的值。因此, 如果一个变量传递给一个函数, 这个变量在调用环境中所存储的值并不会被函数修改, 所以形参的值不会反过来影响实参, 即实参与形参值传递是单向性的。这两个问题是学习和理解函数参数传递的根本, 很多学习C语言的人对这两个问题不是很理解, 下面举例说明函数调用时参数传递过程。

4.1 数值传递

当变量为普通变量时, 函数实参可以是自动局部变量, 静态局部变量, 数组元素, 寄存器变量, 结构体变量, 结构体变量成员, 常量等形式, 函数形参为对应类型的变量, 调用函数时, 由系统给形参分配存储单元, 存放从实参复制过来的数值。函数调用结束后, 形参存储单元释放。

例题1:

例题1中main函数调用func1函数时, 把实参n的值10 (注意不是n) 传给了形参x, x在func1函数中进行增1运算, 这时x的值发生了改变, 但该值不能返回到实参n中, 因为x是func1函数内部定义的变量, 属于局部变量, 调用函数时, 系统为x变量在存储器的动态存储区分配存储空间, 函数调用结束后, x变量被释放, 数值被清, 故n值不变, 体现了传值的单向性。

4.2 地址值传递

地址值传递是指实参与形参之间传递的数据是地址, 与数值传递不同的是, 地址值传递的是形参接收实参地址的复制值, 而不是实参值本身。另外, 地址值传递方式中系统不为形式参数变量分配存储空间, 这一点也与数值传递方式不同。因为函数调用完成数据传递后, 实参与形参拥有相同的变量地址, 它们指向同一变量单元, 该变量在主调函数定义时已经分配了存储空间, 形参只是接收了它的一个地址值, 并没有接收变量本身。根据参数类型的不同, 地址值传递方式常见的有如下几种情况。

4.2.1 实参为变量地址, 形参为指针

例题2:

例题2中在主调函数中将a, b的地址值传给了形参指针x和y, 在被调函数中将y值赋给了x, 这时x的值发生了改变, x存放的是y的存储地址, 即x指向3, 但是这个指向并不能返回到主调函数。因为这个地址值在函数调用结束后被释放, 其值消失。当然, 如果想在被调函数中修改主调函数中实参变量的值, 需要修改指针变量x和y所指向的地址中的内容。比如将x=y改为*x=*y, 即可达到修改实参变量的目的, 但是必须清楚, 虽然被调函数通过指针可以修改主调函数中的值, 但这只是一种间接访问数据的形式而已, 实参向形参传递数据的单向性是不变的。

4.2.2 实参为数组名, 形参为指针

在C语言中, 数组名是一个地址, 而且是一个地址常量, 它代表的是该数组元素的首地址, 不是一个变量。当使用数组名作为实参时, 实参的值就是数组的首地址, 形参指针接收的也是该数组的首地址, 被调函数通过形参指针的变化来访问主调函数中数据。

例题3:

例题3中形参有两个, 第一个表示形参接收一个整型类型的地址, 第二个表示接收一个整型类型的数据, 至于实参是不是一个指针, 是不是一个整型变量, 形式参数并不理会, 只管数据的类型是否匹配。而从主调函数中可以看出, 实参为数组名 (地址) 和整型数据 (整型类型) , 符合参数传递规则。这样发生函数调用时, 形参指针指向了数组a的第一个元素, 通过循环程序, 输出了数组a的所有元素。

这里还有一个问题, 形参指针x在被调函数中的值发生了变化, 这个值是不会返回给实参的, 很明显, 实参中的第一个参数a是数组名, 代表一个地址常量, 肯定不能对它进行赋值操作, 这里进一步验证了实参与形参值传递是单向性的。

4.2.3 实参为数组名, 形参为数组名

当实参与形参均为数组名时, 这种方式跟其他的地址值传递方式是一样的, 系统也是不给形参数组分配内存空间, 而是将形参数组名处理成一个指针, 因此形参数组并不存在。当发生函数调用时, 实参数组只是把首地址赋给形参数组名。这样形参数组名也指向实参数组, 两个数组共同占有一段内存空间。因此通过改变形参数组元素的值来达到改变实参数组元素的目的。

例题4:

例题中实参与形参均为数组名, 调用函数时, 实参数组的首地址复制后给了形参数组, 使形参数组名指向了实参数组, 当改变形参数组元素值时, 实参元素值必然改变, 因为实参数组和形参数组是同一块存储单元。

4.2.4 实参为指针, 形参为数组名

4.2.5 实参为指针, 形参为指针

对于 (1) , (5) 这两种情况比较好理解, 实参为指针, 其值为地址, 所以形参接收的也是地址, 实参与形参类型匹配, 可以进行数据传递, 在此不一一介绍。

参考文献

[1]梁宏涛, 姚立新.C语言程序设计与应用[M].北京:北京邮电大学出版社, 2011.

[2]王慧, 马茵.C语言中函数参数传递问题探讨[J].技术与市场, 2011, 8.

C语言函数说明与返回值 篇3

关键词:C语言;函数说明;返回值

函数的一般形式是:type-specifier function_name(parameter list)parameter declarations{body of the function}类型说明符定义了函数中return语句返回值的类型,该返回值可以是任何有效类型。如果没有类型说明符出现,函数返回一个整型值。参数表是一个用逗号分隔的变量表,当函数被调用时这些变量接收调用参数的值。一个函数可以没有参数,这时函数表是空的。但即使没有参数,括号仍然是必须要有的。参数说明段定义了其中参数的类型。当一个函数没有明确说明类型时,C语言的编译程序自动将整型(int)作为这个函数的缺省类型,缺省类型适用于很大一部分函数。当有必要返回其它类型数据时,需要分两步处理:首先,必须给函数以明确的类型说明符;其次,函数类型的说明必须处于对它的首次调用之前。只有这样,C编译程序才能为返回非整型的值的函数生成正确代码。

一、函数的类型说明可将函数说明为返回任何一种合法的C语言数据类型

类型说明符告诉编译程序它返回什么类型的数据。这个信息对于程序能否正确运行关系极大,因为不同的数据有不同的长度和内部表示。返回非整型数据的函数被使用之前,必须把它的类型向程序的其余部分说明。若不这样做,C语言的编译程序就认为函数是返回整型数据的函数,调用点又在函数类型说明之前,编译程序就会对调用生成错误代码。为了防止上述问题的出现,必须使用一个特别的说明语句,通知编译程序这个函数返回什么值。下例示出了这种方法。

第一个函数的类型说明sum()函数返回浮点类型的数据。这个说明使编译程序能够对sum()的调用产生正确代码。函数类型说明语句的一般形式是:type_specifier function_name(;)即使函数使用形参,也不要将其写入说明句。若未使用类型说明语句,函数返回的数据类型可能与调用者所要求的不一致,其結果是难以预料的。如果两者同处于一个文件中,编译程序可以发现该错误并停止编译。如果不在同一个文件中,编译程序无法发现这种错误。类型检查仅在编译中进行,链接和运行时均不检查。因此,必须十分细心以确保绝不发生上述错误。当被说明为整型的函数返回字符时,这个字符值被转换为整数。因为C语言以不加说明的方式进行字符型与整型之间的数据转换,因而多数情况下,返回字符值的函数并不是说明为返回字符值,而是由函数的这种字符型向整型的缺省类型转换隐含实现的。

二、返回语句

返回语句return有两个重要用途。第一,它使得内含它的那个函数立即退出,也就是使程序返回到调用语句处继续进行。第二,它可以用来回送一个数值。本章将说明这两个用途。

(一)从函数返回函数可以用两种方法停止运行并返回到调用程序

第一种是在执行完函数的最后一个语句之后,从概念上讲,是遇到了函数的结束符“}”(当然这个花括号实际上并不会出现在目标码中,但我们可以这样理解)。例如,下面的函数在屏幕上显示一个字符串。

一旦字串显示完毕,函数就没事可做了,这时它返回到被调用处。在实际情况中,没有多少函数是以这种缺省方式终止运行的。因为有时必须送回一个值,大多数函数用return语句终止运行,有时在函数中设立了多个终止点以简化函数、提高效率。切记,一个函数可以有多个返回语句。如下所示,函数在s1、s2相等时返回1,不相等时返回-1。

(二)返回值所有的函数

除了空值类型外,都返回一个数值。该数值由返回语句确定。无返回语句时,返回值是0。这就意味着,只要函数没有被说明为空值,它就可以用在任何有效的C语言表达式中作为操作数。这样下面的表达式都是合法的C语言表达式。x=power(y);if(max(x,y)>100)printf(“greater;”)for(ch=getchar();isdigit(ch);)...;可是,函数不能作为赋值对象,下列语句是错误的:swap(x,y)=100;C编译程序将认为这个语句是错误的,而且对含有这种错误语句的程序不予编译。所有非空值的函数都会返回一个值。我们编写的程序中大部分函数属于三种类型。第一种类型是简单计算型-函数设计成对变量进行运算,并且返回计算值。计算型函数实际上是一个“纯”函数,例如sqr()和sin()。第二类函数处理信息,并且返回一个值,仅以此表示处理的成功或失败。例如write(),用于向磁盘文件写信息。如果写操作成功了,write()返回写入的字节数,当函数返回-1时,标志写操作失败。最后一类函数没有明确的返回值。实际上这类函数是严格的过程型函数,不产生值。如果读者用的是符合ANSI建议标准的C编译程序,那么所有这一类函数应当被说明为空值类型。奇怪的是,那些并不产生令人感兴趣的结果的函数却无论如何也要返回某些东西。例如printf( )返回被写字符的个数。然而,很难找出一个真正检查这个返回值的程序。因此,虽然除了空值函数以外的所有函数都返回一个值,我们却不必非得去使用这个返回值。有关函数返回值的一个常见问题是:既然这个值是被返回的,我是不是必须把它赋给某个变量?回答是:不必。如果没有用它赋值,那它就被丢弃了。请看下面的程序,它使用了mul( )函数。mul( )函数定义为:int mul(int x, int y){......}

C 语言inline函数小结 篇4

1、函数代码很短,通过不超过10行

//

2、函数内不能出现循环、递归、switch...case //

3、需要在编译的时候开启优化选项-O //否则加上inline修饰,编译器也会视而不见

//强制inline宏

#define __inline

__attribute__((always_inline))

//inline放置的位置只要在函数的返回值的类型前面就可,如果函数又有static修饰,放在static的前后都可

//inline在不同编译器下放置的位置导致的结果是不同的,某些编译器如果将inline放在函数的声明的前面是不被视为inline函数的,但大部分编译器对放在函数声明和函数定义的前面视为一样

内联函数与宏的区别在于:宏是由预处理器来对宏进行替代,没有语法检查、类型检查和安全检查;内联函数是通过编译器的控制来实现的,有语法检查、类型检查和安全检查;内联函数是真正的函数,而且在调用的地方,由编译器负责把内联函数的函数体代码块替换到内联函数被调用的地方,这一点与宏替换很相似;内联函数有参数,有返回值;由于内联函数可以像宏一样被展开,所以调用内联函数的时候,取消了函数参数压栈、出栈所带来的开销,从而减少了函数调用开销;这就是内联函数的优越于宏的地方;

内联函数的声明和内联函数的函数体的定义必须在一起;下面声明内联函数的语句是无效的:inline int Max(int a, int b);

而下面的内联函数的定义是有效的: inline int Max(int a, int b){return((a > b)? a : b)};(c 语言声明)

C++类的成员函数也可以被定义为内联函数;比如: class Student {

private:

int nID

int nAge;

float fScore;

public:

void setID(int nid){ nID = nid;} //该成员函数默认自动为内联函数(隐式定义内联函数)

int getID(void){ return nID;}

//该成员函数默认自动为内联函数(隐式定义内联函数)

inline void setAge(int nage){ nAge = nage;}

//显式定义内联函数

inline int getAge(void){ return nAge;}

//显式定义内联函数

void setScore(float fscore);

//类定义体内没有声明为内联函数;

float getScore(void);

//类定义体内没有声明为内联函数;} inline void Student::setScore(float fscore){ fScore = fscore;} //类定义体外实现为内联函数;inline float Student::getScore(void){ return fScore;}

//类定义体外实现为内联函数;

C++中,在类定义体内部定义了函数体的成员函数,被编译器默认为内联函数,而不管这个函数头前面是否有关键字inline,比如:setID()、getID()、setAge()、getAge();也可以把实现在类定义体外部的成员函数定义为内联函数,这个时候在类定义体中只有成员函数头的声明,而其实现是在类定义体外部,比如:setScore()和getScore();

即便是可以把实现在类定义体外部的成员函数定义为内联函数,那该成员函数的实现也必须写在声明类定义体的哪个头文件(.h)中,不能违反规则;即:内联函数的定义和实现都必须在同一个头(.h)文件中;

内联函数的局限性:

1、由于内联函数与宏一样也是实现为代码替换,所以定义为内联函数的函数体不宜过大,如果函数体过大,则某些普通的编译器就会放弃内联方式,而改用调用普通函数的方式,这样就失去了内联函数的意义了;所以,内联函数的函数体代码不宜过大,一般就是3---4行代码即可;

c语言函数知识点总结 篇5

1、C语言编写的程序称为源程序,又称为编译单位。

2、C语言书写格式是自由的,每行可以写多个语句,可以写多行。

3、一个C语言程序有且只有一个main函数,是程序运行的起点。

第二节、熟悉vc++

1、VC是软件,用来运行写的C语言程序。

2、每个C语言程序写完后,都是先编译,后链接,最后运行。(.c—.obj—.exe)这个过程中注意.c和.obj文件时无法运行的,只有.exe文件才可以运行。(常考!)

第三节、标识符

1、标识符(必考内容):

合法的要求是由字母,数字,下划线组成。有其它元素就错了。

并且第一个必须为字母或则是下划线。第一个为数字就错了

2、标识符分为关键字、预定义标识符、用户标识符。

关键字:不可以作为用户标识符号。main define scanf printf 都不是关键字。迷惑你的地方If是可以做为用户标识符。因为If中的第一个字母大写了,所以不是关键字。

预定义标识符:背诵define scanf printf include。记住预定义标识符可以做为用户标识符。

用户标识符:基本上每年都考,详细请见书上习题。

第四节:进制的转换

十进制转换成二进制、八进制、十六进制。

二进制、八进制、十六进制转换成十进制。

第五节:整数与实数

1)C语言只有八、十、十六进制,没有二进制。但是运行时候,所有的进制都要转换成二进制来进行处理。(考过两次)

a、C语言中的八进制规定要以0开头。018的数值是非法的,八进制是没有8的,逢8进1。

b、C语言中的十六进制规定要以0x开头。

2)小数的合法写法:C语言小数点两边有一个是零的话,可以不用写。

1.0在C语言中可写成1.

0.1在C语言中可以写成.1。

3)实型数据的合法形式:

a、2.333e-1 就是合法的,且数据是2.333×10-1。

b、考试口诀:e前e后必有数,e后必为整数。请结合书上的例子。

4) 整型一般是4个字节, 字符型是1个字节,双精度一般是8个字节:

long int x; 表示x是长整型。

unsigned int x; 表示x是无符号整型。

第六、七节:算术表达式和赋值表达式

核心:表达式一定有数值!

1、算术表达式:+,-,*,/,%

考试一定要注意:“/” 两边都是整型的话,结果就是一个整型。 3/2的结果就是1.

“/” 如果有一边是小数,那么结果就是小数。 3/2.0的结果就是0.5

“%”符号请一定要注意是余数,考试最容易算成了除号。)%符号两边要求是整数。不是整数就错了。[注意!!!]

2、赋值表达式:表达式数值是最左边的数值,a=b=5;该表达式为5,常量不可以赋值。

1、int x=y=10: 错啦,定义时,不可以连续赋值。

2、int x,y;

x=y=10; 对滴,定义完成后,可以连续赋值。

3、赋值的左边只能是一个变量。

4、int x=7.7;对滴,x就是7

5、float y=7;对滴,x就是7.0

3、复合的`赋值表达式:

int a=2;

a*=2+3;运行完成后,a的值是12。

一定要注意,首先要在2+3的上面打上括号。变成(2+3)再运算。

4、自加表达式:

自加、自减表达式:假设a=5,++a(是为6), a++(为5);

运行的机理:++a 是先把变量的数值加上1,然后把得到的数值放到变量a中,然后再用这个++a表达式的数值为6,而a++是先用该表达式的数值为5,然后再把a的数值加上1为6,

再放到变量a中。 进行了++a和a++后 在下面的程序中再用到a的话都是变量a中的6了。

考试口诀:++在前先加后用,++在后先用后加。

5、逗号表达式:

优先级别最低。表达式的数值逗号最右边的那个表达式的数值。

(2,3,4)的表达式的数值就是4。

z=(2,3,4)(整个是赋值表达式) 这个时候z的值为4。(有点难度哦!)

z= 2,3,4 (整个是逗号表达式)这个时候z的值为2。

补充:

1、空语句不可以随意执行,会导致逻辑错误。

2、注释是最近几年考试的重点,注释不是C语言,不占运行时间,没有分号。不可以嵌套!

3、强制类型转换:

一定是 (int)a 不是 int(a),注意类型上一定有括号的。

注意(int)(a+b) 和(int)a+b 的区别。 前是把a+b转型,后是把a转型再加b。

4、三种取整丢小数的情况:

1、int a =1.6;

2、(int)a;

3、1/2; 3/2;

第八节、字符

1)字符数据的合法形式::

‘1’ 是字符占一个字节,”1”是字符串占两个字节(含有一个结束符号)。

‘0’ 的ASCII数值表示为48,’a’ 的ASCII数值是97,’A’的ASCII数值是65。

一般考试表示单个字符错误的形式:’65’ “1”

字符是可以进行算术运算的,记住: ‘0’-0=48

大写字母和小写字母转换的方法: ‘A’+32=’a’ 相互之间一般是相差32。

2)转义字符:

转义字符分为一般转义字符、八进制转义字符、十六进制转义字符。

一般转义字符:背诵�、、’、”、。

八进制转义字符: ‘141’ 是合法的, 前导的0是不能写的。

十六进制转义字符:’x6d’ 才是合法的,前导的0不能写,并且x是小写。

3、字符型和整数是近亲:两个具有很大的相似之处

char a = 65 ;

printf(“%c”, a); 得到的输出结果:a

printf(“%d”, a); 得到的输出结果:65

第九节、位运算

1)位运算的考查:会有一到二题考试题目。

总的处理方法:几乎所有的位运算的题目都要按这个流程来处理(先把十进制变成二进制再变成十进制)。

例1: char a = 6, b;

b = a<<2; 这种题目的计算是先要把a的十进制6化成二进制,再做位运算。

例2: 一定要记住,异或的位运算符号” ^ ”。0 异或 1得到1。

0 异或 0得到0。两个女的生不出来。

考试记忆方法:一男(1)一女(0)才可以生个小孩(1)。

例3: 在没有舍去数据的时候,<<左移一位表示乘以2;>>右移一位表示除以2。

C语言中调用Lua函数实例 篇6

记得上学时,初中英文课本中,上网叫做surfing the internet,中文叫网上冲浪,那个时期,人们经常称互联网为赛博空间。如今工作了,大量的零碎时间用于上微博,知乎,QQ,这些碎片化的阅读让人读起来轻松,也能获取些粗浅的信息。然而它们是消耗时间的黑洞,时间就这样一分一秒地飞逝,年末的时候,知乎会告诉你回答了多少问题,阅读了相当于一部《红楼梦》那么多的文字。只是当你静下来一想,这些浅阅读并没给你带来有深度,系统的知识。在你的时间线上,两条相邻信息往往是八竿子也打不着的。而且你还时不时去看看关注者有没有更新,期待让你眼前一亮的信息。结果往往是趁兴而去,败兴而回。屏幕上的信息永无止境地滚动着,是如此的热闹,仿佛每个人都在狂欢,而我的内心却如此的空虚与孤独。

在lua API中,调用一个函数的步骤很简单:

1.压入你要调用的函数,使用lua_getglobal。

2.压入调用参数。

3.使用lua_pcall

4.从栈中弹出结果。

举例说明,假设你有这么一个lua函数:

代码如下:

function f (x, y)

return (x^2 * math.sin(y))/(1 - x)

end

那么,我们就可以定义个c函数来封装这个调用:

代码如下:

/* call a function ‘f‘ defined in Lua */

double f (double x, double y)

{

double z;

lua_getglobal(L, “f”);

lua_pushnumber(L, x);

lua_pushnumber(L, y);

/* do the call (2 arguments, 1 result) */

if (lua_pcall(L, 2, 1, 0) != 0)

error(L, “error running function ‘f‘: %s”,

lua_tostring(L, -1));

if (!lua_isnumber(L, -1))

error(L, “function ‘f‘ must return a number”);

z = lua_tonumber(L, -1);

lua_pop(L, 1);

return z;

}

lua_pcall在压入结果的之前,会将函数,和参数弹出,如果返回多个结果,第一个最先压入,

如果lua_pcall运行出错,那么会返回个非0值。

C语言函数教学方法探讨 篇7

1 教学方法

函数的教学与流程控制不一样,流程控制中if、while、for知识点是由浅入深,一步一步来的。而函数的特点是,一个程序里会涉及到函数的很多知识点,所以只要把一个程序搞清楚了,那其他程序也就全懂了。针对函数的这个特点,我们的授课思路是首先用通俗易懂的例子来引入问题,使学生理解为什么需要函数;教师实际演示编写程序,调试执行程序让学生理解C函数的执行过程;通过对程序有意设置语法错误和空白内容的方法,使学生了解如何定义函数、调用函数,帮助学生掌握函数语法知识;设计案例,由浅入深,将知识全面化,系统化;布置作业,帮助学生加深对知识点的理解,激发学生的创新思维。

1)用通俗易理解的例子来引入问题

讲授任何一个知识,首先讲为什么需要这个知识[2],只有这样学生才会自然接受,这一点很重要。引例的选取最好难易适中,能充分调动学生的积极性。

函数教学案例一:“分别求a,b的最大值,c,d的最大值,e,f的最大值。”

此案例比较简单,用顺序结构和选择结构就可以求解,学生对此很熟悉,不会感觉枯燥,会更积极地参与到教学中来。

由教师实际编程演示此程序,调试并运行结果,而不是直接将写好的程序呈现给学生看。教师实际编程演示可以提高学生的兴致,使学生注意力更集中,并且在这个过程中,可以反映教师编程时的思维过程,体现良好的编程习惯。

程序:

教师提醒学生,在这个程序中,有三段代码的操作是重复性的。那么如果这个程序有一万个地方要输出两个数的最大值,那就意味着这段代码要重复一万次。这样写肯定不合适。至此引入函数加深学生对函数的感性认识。引导学生思考为什么需要函数,原因是函数可以避免重复性操作。

2)“一题多解、一题多变”的教学方法

首先,由教师实际编程演示,给出用带参数无返回值的函数实现案例一。

程序:

教师点击组建——开始调试——GO命令(或按F5键)进入调试模式,利用单步执行命令(快捷键F11)控制程序每次执行一条语句,观察编辑窗口箭头的走向,以及变量窗口各变量值的变化。程序从main函数进入,当执行到max(a,b)时,程序开始进入并执行max函数。在变量输出窗口可以看到参数i和j分别获得了a,b的值。运行到printf函数时,如果不需要调试执行库函数,则按下F10,连续执行被调用函数的全部语句。继续按F11键,执行完被调用的max函数后,返回到主函数max(a,b)处,往下执行max(c,d),程序第二次进入并执行max函数,执行完max函数返回到主函数max(c,d)处,往下执行max(e,f),程序第三次进入并执行max函数,执行完毕返回到主函数max(e,f)处,执行return 0,整个程序结束。

通过调试执行,学生可清楚了解到函数调用与返回的实现过程。程序从主函数main进入,当遇到函数调用时,暂停执行主调函数,然后转去执行被调函数。首先为被调函数分配调用过程中所需的数据区,包括调用后的返回地址,函数的形参以及各种局部变量,然后把实参的值复制到形参中去,接着把控制权转移给被调函数,完成调用后,如果函数有返回值,先保存计算结果,然后释放被调函数的数据区,返回主调函数暂停的位置(调用前保存了返回地址)继续执行。

接下来,教师引导学生用带参数有返回值的函数实现案例一。

程序:

仍然采用调试执行本程序,加深学生对函数调用过程的理解。将函数调用过程讲深讲透非常重要,这是科学性的要求。理解了函数的调用过程,就会理解什么是递归,什么是变量的作用域与变量的生命期。

3)设置错误寻求正确答案的教学方法和填空式的教学方法

传统的教学方法中,一般集中式的讲述语法知识,非常枯燥,学生学了也不会编程不会应用。在课堂教学中,可以有意设置语法错误,根据编译所提示的错误,引导学生思考并修正错误;或者是有意识地将一些内容空出,要求学生先读懂再填空,然后运行程序,调试程序,直到得到正确的运行结果。这样可激发学生的探索欲望,加深对语法的理解,也提高了学生的学习能力。

在程序的编写中,首先有意的将max2(a,b),max2(c,d),max2(e,f)这些函数调用的语句空出,让学生补充,或者将函授首部空出,并且有意设置一些语法错误,比如,在函数定义的首部加上分号,在形参中分隔符用分号,使形参和实参类型不一致,使调用函数的类型与main中的该函数的类型不一致,或者是函数的类型与return后的变量类型不一致,这样程序在编译过程中会给出语法错误提示,让学生自己去思考,修正这些语法错误,加深对函数的定义和调用的理解。

4)案例——任务驱动的教学方法

至此,学生已经掌握了函数的大部分知识点,但是在以往的教学中,通常面临一个问题,当学生自己写程序的时候,他们通常将语句一并写在main里,而不选择用函数实现。究其原因,还是学生对如何定义函数,以及如何在主调函数中调用自定义函数不够熟练,没有体会到函数的运用给编程带来的极大的便利。针对这个问题,我设计了两个案例要求学生完成。案例的选择要难度要由浅入深,循序渐进,每个案例欲教授的内容要目的明确,重点突出。

函数教学案例二:输入一个正整数n,生成一张2的乘方表,输出20到2n的值[3]。

程序:

教师要求学生自己定义pow函数,而不使用math.h中提供的数学库函数。

待学生思考几分钟后,教师可引导学生:定义pow函数应该首先确定函数的返回值类型,从power=pow(2,i)这条语句可以分析出,pow函数类型应该是double类型,因为它要返回一个doubler类型的值赋值给power变量;接着确定pow函数的形参,从主调函数中pow(2,i)的用法可以看出,是两个整形的实参,那么形参也应该是两个整型变量,同时强调函数的形参必须是变量。接着给出double pow(int i,int j)的实现。

通过这个库函数调用的案例,学生学会了如何自定义函数,加深了对函数库、库函数、连接程序、头文件的相关概念的理解,明白了函数是为解决大量同类型的问题而设计的,可提高代码的可重用性,将来无论哪一个程序需要这个功能都可以使用它,就好比我们printf、scanf、pow一样,也学会了在具体的应用中该如何自定义函数,如何确定函数的返回值类型和函数的形参列表。

函数教学案例三:计算组合数[4]。

要求学生自己写程序,

程序:

通过这个实例,学生会直观的体会到函数的运用给编程带来的极大的便利。这样,在今后的编程中,会尽可能地用模块化的思想来分析问题解决问题,这个实际上就是面向过程的一个思想,也是为什么需要函数的一个很重要的原因。

5)精讲多练的教学方法

C程序设计内容多,课时少,课堂上教师要做的不是拼命多讲,可以通过作业让学生去自学。也不是要拼命讲明白,因为程序只有学生自己想明白了才能为他所用。不能过于依赖教材,要引导学生将所学知识联系起来,现场演示设计程序的思维过程,讲清知识的深层原理。布置作业,作为课堂内容的补充和深化。学生通过查资料动手实验,根据完成作业的情况,教师可以发现问题所在,在上机实践课中重点讲解。

函数教学案例四:“编程设计一个简单的计算器程序,要求根据用户从键盘输入的表达式:操作数1运算符op操作数2来计算表达式的值,指定的运算符为加(+)、减(-)、乘(*)、除(/)”,要求学生给出不适用函数的实现方法,带参数的函数实现方法,不带参数的函数实现方法。

通过本案例,使学生理解并掌握函数的几个重要概念、实现方法和编程技巧。

函数教学案例五:通过调用swap函数,交换主函数中变量a,b的值。

这个作业的要点是swap函数中的形参在调用完毕空间就释放了,所以,虽然两个形参变量的值交换了,但是a,b的值却没变。

大部分学生接着就想,能否返回两个值带回主函数,编程实践发现无法做到,因为被调函数只能返回一个值。

那怎么办呢?这时候老师可以告诉学生,在后续课程指针章节可以找到答案。这样给学生留下思考余地,启发学生去思考,有利于提高学生的学习能力,培养学生的创新思维[2]。

2 结束语

C语言函数的教学很重要,使学生理解函数并且学会应用函数是教学的关键。在后续讲解数组、指针、结构、文件时,都会与函数的知识综合起来,例如,将数组名作为函数的参数,指针作为函数的参数,结构指针作为函数的参数,文件操作函数等,学生能更好地掌握函数的定义和调用的方法,也会慢慢理解为什么说C语言是由函数所组成的。该文所探讨的C语言函数的教学方法已经应用在实际教学中,激发了学生学习的自觉性和主动性,提高了教学质量,取得了良好的教学效果,希望对同行有所帮助。

参考文献

[1]高枚,杨志强,许兰兰,龚沛曾.C/C++教学改革的探索与实践[J].计算机时代,2005(11).

[2]谭浩强《.C程序设计》发行1000万册的启示[J].北京联合大学学报:自然科学版,2011(4).

[3]何钦铭,颜晖.C语言程序设计[M].北京:高等教育出版社,2012.

C语言sprintf与sscanf函数 篇8

1、程序运行后,用户任意输入一个字符串,求出字符串长度,然后输出字符串内容及个 数。不能使用strlen函数。

求字符串长度函数的函数原型必须如下:

int MyStrLenPt(char *str)

若输入:nihao123!

则输出:nihao123!:9 chars2、编写一个对一维数组进行排序的程序。

要求:

写两个函数,一个主函数,一个sort函数。在主函数中完成数组的输入和输出,通过调 用sort函数对数组进行排序。

sort函数的原型为:voidsort(int*p, intn);

说明:

(1)请大家按学号来坐,便于考勤和管理。

(2)请珍惜宝贵的实验时间!不要做与实验无关的事情,比如聊QQ、上网或打游戏。

(3)直接把C语言代码粘贴到相应的实验题目下方,上交实验报告时只交word文档。

C语言sprintf与sscanf函数 篇9

#include

#include

#include

#include

int system(const char * cmdstring)

{

pid_t pid;

int status;

if(cmdstring == NULL){

return (1);

}

if((pid = fork())<0){

status = -1;

}

else if(pid = 0){

execl(“/bin/sh”, “sh”, “-c”, cmdstring, (char *)0);

-exit(127); //子进程正常执行则不会执行此语句

}

else{

while(waitpid(pid, &status, 0) < 0){

if(errno != EINTER){

status = -1;

break;

}

}

}

return status;

}

分析一下原理估计就能看懂了:

当system接受的命令为NULL时直接返回,否则fork出一个子进程,因为fork在两个进程:父进程和子进程中都返回,这里要检查返回的pid,fork在子进程中返回0,在父进程中返回子进程的pid,父进程使用waitpid等待子进程结束,子进程则是调用execl来启动一个程序代替自己,execl(“/bin/sh”, “sh”, “-c”, cmdstring, (char*)0)是调用shell,这个shell的路径是/bin/sh,后面的字符串都是参数,然后子进程就变成了一个shell进程,这个shell的参数是cmdstring,就是system接受的参数,在windows中的shell是command,想必大家很熟悉shell接受命令之后做的事了。

再解释下fork的原理:当一个进程A调用fork时,系统内核创建一个新的进程B,并将A的内存映像复制到B的进程空间中,因为A和B是一样的,那么他们怎么知道自己是父进程还是子进程呢,看fork的返回值就知道,上面也说了fork在子进程中返回0,在父进程中返回子进程的pid。

windows中的情况也类似,就是execl换了个又臭又长的名字,参数名也换的看了让人发晕的,我在MSDN中找到了原型,给大家看看:

HINSTANCE ShellExecute(

HWND hwnd,

LPCTSTR lpVerb,

LPCTSTR lpFile,

LPCTSTR lpParameters,

LPCTSTR lpDirectory,

INT nShowCmd

);

用法见下:

ShellExecute(NULL, “open”, “c:a.reg”, NULL, NULL, SW_SHOWNORMAL);

你也许会奇怪 ShellExecute中有个用来传递父进程环境变量的参数 lpDirectory,linux中的execl却没有,这是因为execl是编译器的函数(在一定程度上隐藏具体系统实现),在linux中它会接着产生一个linux系统的调用 execve, 原型见下:

int execve(const char * file,const char **argv,const char **envp);

看到这里就会明白为什么system()会接受父进程的环境变量,但是用system改变环境变量后,system一返回主函数还是没变,

原因从system的实现可以看到,它是通过产生新进程实现的,从我的分析中可以看到父进程和子进程间没有进程通信,子进程自然改变不了父进程的环境变量。

使用了system函数就能执行dos指令。

#include

#include

xiaoyu()

{

char *a;

int n=0;

FILE *f;

f=fopen(“file.bat”,“w+”);/*新建一个批处理*/

if(f==NULL)

exit(1);

a=“echo”; /*DOS命令*/

for(n=65;n<=90;n++)/*大写A-Z*/

fprintf(f,“%s %c ”,a,n);/*利用ASCII码输出A-Z,写出批处理*/

fclose(f);

system(“file.bat”);/*运行批处理*/

}

main()

{

char *string;

xiaoyu();

string=“echo C语言的system函数 ”;/*输出中文*/

system(string);

system(“pause”);/*程序暂停*/

}

C语言sprintf与sscanf函数 篇10

1 printf函数的定义详解

printf函数是一个参数个数可变的函数,其功能是按照用户指定的格式,将数据按一定的格式输出若干个任意类型的数据。在头文件stdio.h中的函数原型声明为:int printf(const char*format,…),即其一般格式为:

printf(格式控制,输出表列)

1.1“格式控制”

“格式控制”是用双引号括起来的字符串,也称“转换控制字符串”,是一个指针变量,由普通字符、转义字符和格式说明字符组成,用来指定输出数据项的类型和格式。它包括格式说明和普通字符两种信息。格式控制由“%”和格式字符组成(如%d,%f),其作用是将输出的数据转换为指定的格式。需强调的是,格式说明总是由“%”字符开始的。以下是ANSI C标准中printf提供的各种数据格式。

%a——浮点数、十六进制数字和p-记数法(C99);

%A——浮点数、十六进制数字和p-记法(C99);

%c——一个字符;

%d——有符号十进制整数;

%e——浮点数、e-记数法;

%E——浮点数、E-记数法;

%f——浮点数、十进制记数法;

%g——根据数值不同自动选择%f或%e;

%G——根据数值不同自动选择%f或%e;

%i——有符号十进制数(与%d相同);

%o——无符号八进制整数;

%p——指针;

%s——字符串;

%u——无符号十进制整数;

%x——使用十六进制数字0f的无符号十六进制整数;

%X——使用十六进制数字0f的无符号十六进制整数;

%%——打印一个百分号

普通字符,即需要原样输出的字符。如,printf("the number is:%d",i)中的“the number is:”即是需原样输出的普通字符。

1.2“输出表列”

“输出表列”是需要输出的一些数据,也可以是表达式。

输出是单个数据时,参见上例。下面举一个输出表达式的例子。

运行结果为:The sum m+n=7。

printf的输出表列有时也可以是多个数据,或是多个表达式。

例如:printf("m=%d n=%d",m,n);

当进行多项输出时,其一般形式表示为:printf(参数1,参数2,参数3…参数n),其功能是将参数2—参数n按参数1给定的格式输出。例如:

2 printf函数应注意的问题

在使用printf函数进行数据输出、特别是进行多个输出时,要重点注意以下问题:

2.1 前后格式要一致

即printf函数“格式说明”中的格式字符应该和“输出表列”中数据的类型一致。如果类型不一致,在编译时没有语法错误,但是结果会出现错误。谭浩强编著的《C程序设计》一书举了这样一个例子:

运行结果为:

a=-1,177777,ffff,65535

b=-2,177776,fffe,65534

从这个例子中可以看出,int型数据也可以用%u格式输出,而unsigned型数据也可以用%d格式输出;同时,Unsigned型数据也可用%o或%x格式输出,由于指定输出格式的差异,同一个数据显示出了四个不同的结果。可见,使用printf函数时,前后格式一致有多么重要了。因此,在实际编程应用中,一定要对这个问题引起充分的注意。

2.2 输出个数要相等

即printf函数“格式说明”个数应该和“输出表列”中的数据个数相一致。

如果“格式说明”个数小于“输出表列”中数据的个数,将导致多出的数据被略掉。printf输出是按从左到右的进行,“格式说明”中的第一项和“输出表列”中第一个数据对应,“格式说明”中的第二项和输出表列中第二个数据对应(各个说明项之间用逗号隔开),如此一一对应,在“输出表列”中多余的数据被忽略掉。

例如:int m=2,n=4,p=3,q=5;printf("m=%d,n=%d",m,n,p,q);输出结果为:m=2,n=4。这样,多出来的变量p、q就被忽略掉了。

如果“格式说明”的个数大于“输出表列”数据个数,仍按照上述一一对应关系从左至右输出数据,但多出的格式说明没有被略掉,其对应的输出内容是由系统产生的随机数。

例如:int m=2,n=4;printf("m=%d,n=%d",m);输出结果显示m=3,但对于n,系统中产生了一个随机数,并将这个数赋予n按第二个“%d”指定的格式输出。

2.3 附加格式要清楚

使用printf输出时,还要注意附加格式说明字符。C语言提供如下四种附加格式说明:字母“l”用于长整型整型,可加在格式符d、o、x、u前面;字母“m”(代表一个正整数),表示数据最小宽度,如果数据的位数小于m,则左端补以空格,若大于m,则按实际位数输出;“.n”(代表一个正整数),对实数表示输出n位小数,对字符串表示截取的字符个数;“-”表示输出的数字或字符在域内向左靠。

下面,不妨看下面一个实例:

输出结果如下:

123.455994,123.455994,123.46,123.46,123.46,f的值应为123.456,但输出为123.455994,这是由于实数在内存中的存储误差引起的。如果这些附加格式的用法掌握不清,运算时就会引起一些不必要的错误。

此外,还有一些控制输出的字符要引起注意。在格式控制中,一些普通字符也可以是“转义字符”,如“n,t,b,r,f”等。其中,“n”是换行符、“t”是水平制表位,每八个字符为一列。还需说明的是,由“%”被用作格式控制,如果想输出“%”字符,则应该在“格式控制”中连续用两个“%”表示。

例如:int m=3;printf(“%d%%”,m);结果是:3%。

2.4 求值顺序要理清

我们知道,printf“输出表列”可以是表达式,在这种情况下特别是用“++”做自加运算时,一定要首先搞清编辑系统的运算顺序,是左优先还是右优先,否则就可能出现一些意料之外的结果,让人百思不得其解。例如,在Turbo C编辑环境中运行如下程序:

其输出结果为:4,9,4,8。很多人对此一时难以理解,认为输出结果应为:3,8,4,8才对。其实只要知道了Turbo C是按照从右到左的进行运算的就容易理解了:运算时,Turbo C首先用b为n赋值使n=8,然后进行自加使b=9;而后,对a进行自加使a=4。因此,出现上述的运行结果也就不足为奇了。同样,VC++6.0也是按从右到左的顺序进行运算的。

3 使用printf函数避错的几个小技巧

3.1 多用普通字符防混乱

C语言对可变参数函数的原型检查不太严格,往往会导致很多错误结果不易被察觉。如果输出的数据全部为整型、全部为实型及整形和实型相混合输出时,在格式控制中,最好加上普通字符,避免数据相混淆分不清楚。例如,整型数据和字符型数据之间的格式字符和数据类型可以相混合使用,不妨看下例:

输出结果将显示:a=A,b=97。这是因为:字符数据在内存中是以ASCII码形式存放,ASCII“65”对应的字符为‘A’,而字母‘a’的ASCII为97。由于显示的结果仍是一个字母和一个数值,往往会“蒙混过关”。为避免这种情况,不妨对printf函数行做如下改动

这样,出错就一目了然了!

3.2 多用换行符作分隔

整型数据、实型数据和字符型数据可以用换行符作为分隔符。例如:int m=2,n=5,l=4;printf("%dn%dn%d",m,l,n);输出结果分别在三行中显示2、5、4。

当输出的数据较多时,可以对这些数据分门别类,每一类单独输出一行,这样输出结果更具条理性,更便于查错。

3.3 多用交互作提醒

使用printf进行多项数据输出,有时计算机输出错误数据或程序不能正常运行,原因并不是出在printf函数上,而是由输入环节上的人为错误所导致的。这时利用printf函数输出一些提示信息是很必要的。例如,编写C语言程序时,经常会调用scanf函数来从键盘上输入一些数据。为避免输入错误,可在调用scanf函数之前先调用printf函数,输出一行信息提示。这样在程序执行过程中,就可以人机互动、根据提示信息进行准确输入。printf函数输出提示信息的格式是printf(“字符串”),应用起来并不增加多少工作量,但是大家特别是初学者在编程序时容易忽略了这一点,这样就容易造成一些麻烦。

总之,使用C语言编程时,printf函数的使用是不可回避的。只有准确地掌握该函数的定义和使用技巧,才能尽量减少错误、使用起来得心应手。

摘要:格式输出函数printf使用方便灵活,是C语言程序设计中使用频度最高的输出函数。但是如果不能准确掌握,也会导致经常出现错误。论文系统介绍了printf函数的功能,讨论了printf函数使用时的四点注意事项,并就如果减少错误发生了讲了三点小技巧,对于帮助C语言初学者快速掌握printf函数的使用有较高的参考价值。

关键词:C语言,printf函数,定义,使用技巧

参考文献

[1]谭浩强.C程序设计[M].第2版.北京:清华大学出版社,2002.

[2]康莉等.零基础学C语言[M].北京:机械工业出版社,2009.

[3]K.N.King著;吕秀锋,黄倩译.C语言程序设计:现代方法[M].第2版.北京:人民邮电出版社,2010.

C语言sprintf与sscanf函数 篇11

程序:

#include #include int prime(int num) //prime表示素数{int i = 0;int k = 0;k = sqrt(num);for (i = 2; i <= k; i++){if (num%i == 0){ return 0;}}return 1;}int main(){int num;int ret;printf(“请输入一个大于1的正整数:”);scanf(“%d”, &num);ret = prime(num);if (ret == 1){printf(“%d是素数n”, num);}else{printf(“%d不是素数n”, num);}return 0;}

结果一:

请输入一个大于1的正整数:8

8不是素数

请按任意键继续. . .

结果二:

请输入一个大于1的正整数:17

17是素数

C语言sprintf与sscanf函数 篇12

用密度泛函B3LYP方法对PuX+(X=H,O,N,C)分子离子体系进行了理论研究.结果表明,这些分子离子的基态电子状态分别是X7Σ-(PuH+)、X6Σ-(PuO+)、X5Σ+(PuN+)和X8Σ-(PuC+);势能函数为Murrell-Sorbie势函数.并得到了相应的几何性质、力学性质和光谱数据.

作 者:李权 王红艳 蒋刚 朱正和 作者单位:李权(四川师范大学化学系,)

王红艳,蒋刚,朱正和(四川大学原子与分子物理研究所,)

C语言sprintf与sscanf函数 篇13

这些文献主要描述了指针、指针和其它数据类型的关系。实际教学过程中发现同学们对指针作为函数参数时有诸多问题, 如为什么需要指针作为函数的参数, 形参和实参是如何传递的, 如何实现参数值的双向传递, 双重指针作为函数的参数时程序又是如何执行的等等。文献[8]采用printf函数将变量的地址输出, 通过输出的地址值来理解指针和地址, 对数组元素的地址、函数参数的地址传递都做了描述, 对学生学习指针有一定的帮助。但在这些文献中, 没有提及函数调用时的另一个重要的问题, 那就是变量的作用域和生存期。

本文以围绕内存的分配和回收, 利用一种图形化的方法来表述指针, 结合函数调用中涉及的变量的作用域和生存期, 采用动态的方式说明指针、指向指针的指针作为函数参数时, 形式参数和实际参数之间的传递关系, 以更形象、直观的方式展示函数调用时的参数传递问题, 帮助同学更好的理解指针的概念和相关应用。

1 指针的概念

指针就是地址。和整型、字符型、浮点型一样, 指针也是C语言的一种数据类型。一个整型数据表示一个整数, 而一个指针数据表示一个地址值。地址值就是内存中某个存储单元的编号。

1.1 内存的概念

内存:计算机的内存, 是线性连续的存储空间, 这也是计算机的CPU能够直接访问的空间。计算机所运行的程序, 以及程序运行所需要的数据都必须存储在内存中, 才能够被CPU读取。内存中存储单元的最小单位是字节。

地址:为了能够访问到内存中的某个存储单元, 需要为它编号, 这个编号称为内存地址。内存的地址是按字节编排的, 有了这个地址, 就可以访问该地址所标识的存储单元。

指针值实际上就是这样一个编号, 体现为一个整数, 所以可以使用printf函数打印出来, 让用户看到某个存储单元的地址值[8]。

1.2 指针的图形表示方法

本文中, 以一个一维的表格来表示一维线性内存空间, 表格中的一个小方格表示一个字节。如果一个变量占用多个字节, 那么, 变量存储单元内的字节之间用虚线表示。这些字节的第一个字节的地址代表这个变量的地址。小方格边的数字表示该字节的地址, 字母表示定义的变量名, 小方格中的字母或数字表示该存储单元的内容。如有以下定义:

int i=10, *pi=&i;

char c='c', *pc=&c;

上面定义了四个变量, 一个整型变量i, 一个字符型变量c和两个指针类型的变量, 它们在内存中的存储形式可以用图1表示。

C语言中, 变量在运行时会按照变量的数据类型分配一定的存储空间。变量名实际上是以一个名字代表的一个存储地址[9]。但内存中每一个字节都有一个地址编号, 一个变量名称只能代表一个地址, 就是变量所占存储空间首字节的地址。从图中可以看出, 不管指针变量的基类型是占两个字节的整型数据, 还是占一个字节的字符型数据, 指针变量本身所占用的存储空间都是相同的[8]。

2 指针变量作为函数参数时的图形化分析

指针变量作为函数的参数, 可以将主调函数中变量的地址传递到被调函数中, 这样就可以在被调函数中访问主调函数中变量的存储单元, 这就是函数调用中的地址传递方式。在函数调用过程中, 主调函数和被调函数都可能定义变量, 变量的作用域和生存期也应该一起分析。

2.1 变量的作用域和生存期

变量的作用域是指一个变量的有效范围。在一个函数内部定义的变量, 只能在本函数内有效, 这就是局部变量。而在函数外部定义的变量称为外部变量或全局变量, 其作用范围从定义变量的位置开始到本文件结束[9]。C语言中的形式参数是局部变量, 其作用范围仅仅为函数本身。

变量的生存期是指变量存在的时间。一般的局部变量, 包括函数的形式参数只有在定义它的函数中存在, 在调用函数时才临时分配存储单元, 在函数调用结束后就马上释放了。也称这种变量存储形式为动态存储方式。

2.2 普通变量作为函数参数

为了说明指针变量作为函数参数时各变量值的变化情况, 笔者采用了一种图形化动态分析方法。这里以普通变量作为函数参数时的调用情况为例, 说明这种分析方法。

交换a和b的值的swap函数[9]190 是各种教材中经常使用的一个函数自定义、调用的例子。如果使用普通变量作为函数的参数, 不能正确实现值交换的功能。swap函数定义如下:

主函数中定义了变量a、b, 并调用swap函数, 其完整定义如下:

程序从main函数开始执行, 在main函数中调用swap函数。按照图形化分析方法, 其过程中各个变量的值如图2 所示。

以程序运行时变量的内存分配和回收为核心, 可以将整个程序的执行过程分为五个步骤:

(1) main函数中, 定义a、b变量并分别赋初值10 和20。a、b变量在内存中分配的地址为200 和202, 内容为10 和20, 如图2 (a) 所示。

(2) main函数中调用函数swap, 形式参数x和y作为动态局部变量, 在被调用时会分配存储空间, 存储空间的地址分别为206 和208。实际参数a和b的值传递给形式参数x和y, 所以此时x和y的值也分别为a和b的值, 即10 和20, 如图2 (b) 所示。

(3) 在swap函数中, 通过中间变量temp交换x和y的值, 完成交换后x和y的值分别为20 和10, 如图2 (c) 所示。

(4) . swap函数执行结束, 回到被调函数, 即main函数中。由于swap函数的形式参数x和y是动态变量, 所以在函数调用结束后, 系统为x、y分配的存储空间会释放, 如图2 (d) 所示。

从以上分析可以看出, 用普通变量作为函数的参数, 不能影响主调函数中实际参数的值, 所以这种方法定义的swap函数, 不能实现交换两个变量值这一功能。

2.3 指针变量作为函数参数

指针变量作为函数的参数, 在实参和形参之间传递的是变量的地址。这样, 在被调函数中, 可以通过指针的引用访问主调函数中变量的存储单元, 从而实现主调函数中变量值的改变。这里仍然以swap函数为例来分析指针变量作为函数参数时的执行过程。swap函数定义如下:

主函数中定义了变量a、b, 并调用swap函数, 其完整定义如下:

这里swap函数的形式参数为指针变量, 所以在调用该函数时, 是取变量的地址作为调用函数的实际参数。为了更好的分析变量值的交换过程, 将交换值的过程细化, 以下面六个步骤进行分析, 如图3 所示。

(1) main函数中定义了a、b变量并赋初始化值, swap函数中定义了x、y两个指针变量, 并通过参数传递, 将实际参数a、b的地址200 和202 分别传递给变量x和y, 如图3 (a) 所示。

(2) 在swap函数中定义了整型变量temp, 系统为它分配了两个字节的存储空间, 如图3 (b) 所示。

(3) 在swap函数中, 通过中间变量temp, 将指针变量x和y所指向的存储单元的内容进行了交换, 如图3 (c) ~3 (e) 所示。

(4) 最后, 由于swap中的形式参数x、y, 以及局部变量temp均为局部动态变量, 所以在函数调用结束后其存储空间会被释放, 但此时a和b的值已经实现了交换, 如图3 (f) 所示。

从以上分析可见, 通过采用指针变量作为函数参数的方式, 可以实现被调函数访问主调函数中变量分配的存储空间。

3 指向指针的指针作为函数参数时的分析

如果说指针是c语言教学的一个难点, 那么, 指向指针的指针就是难点中的难点, 学生理解起来相当困难。其实, 指向指针的指针也是一个指针类型, 只是其基类型也是一个指针类型。也就是说, 指向指针的指针存储的是一个地址数据, 这个地址对应的存储单元中也是存储的一个地址数据。

3.1 指向指针的指针的图形化描述

指向指针的指针仍然分配一个指针类型所占用长度的存储空间。例如, 有如下定义:

int i=10, *p=&i, **p1=&p;

这里, i是一个整型变量, p是一个指针变量, p1 是一个指向指针的指针变量。他们在内存中的状态如图4 所示。图中带箭头的弧线表示指针变量的指向关系。P1 作为一个指向指针的指针, 指向了指针变量p所在的存储单元。

3.2 指向指针的指针作为函数参数

指针变量作为函数的参数可以让被调函数访问主调函数的存储单元, 修改主调函数中变量的值。同理, 使用指向指针的指针变量作为函数参数, 可以在被调函数中修改主调函数中指针变量的值。

下面以单链表的初始化函数[10]13 来说明指向指针的指针作为函数形式参数时的分析过程。初始化函数定义如下:

该函数的形式参数为一个指向指针的指针变量, 在函数中动态申请存储空间并得到地址, 通过这个指针变量的指向将该地址传回主调函数。函数中的node为自定义结构体类型[10]12。

主调函数中调用initiatesl函数的相关部分定义如下:

上面列出了两条语句, 第一条定义了一个基类型为node类型的指针变量, 第二条是对initiatesl函数的调用, 实现链表初始化。

如果仅仅用node *p定义了指针变量p, 此时系统会在运行时为p分配存储空间, 但p中还没有一个明确的地址值, 指针变量p也就没有一个明确的指向, 所以这时候是不能使用指针变量p的指向来访问内存单元的。而通过initiatesl (&p) 调用, 将p的地址作为函数调用的实参传递到被调函数中, 将被调函数中分配的存储空间的地址赋值给变量p, 使p有一个明确的指向, 在后面的程序中就可以使用这个指针变量来访问申请的node类型的存储空间了。函数调用时变量存储空间的分配和参数值的使用情况如图5 所示。

程序执行过程的具体分析如下:

(1) 图5 (a) 表示定在main函数中义了指针变量p, 系统为p分配了存储单元, 但存储单元中没有一个确定的地址值。

(2) 当程序执行到函数调用时, 系统会为initiatesl函数的形式参数h分配存储单元, 其大小仍然为一个指针变量的大小, 并将实际参数p的地址200 传递给h, 如图5 (b) 所示。

(3) malloc函数申请分配一个node类型所需要的存储空间, 并将空间的首地址208 强制转换为指向node类型的指针, 然后赋值给变量h所指向的存储单元。此时变量h的值为200, 所以其指向其实就是p所在的存储单元。所以经过这个赋值之后, 变量p就有了一个明确的地址值208, 如图5 (c) 所示。

(4) 初始化函数结束之后, 变量h作为形式参数, 属于动态局部变量, 所以它所占用的存储空间被释放, 如图5 (d) 所示。

此时, 通过调用initiatesl函数, main函数中的变量p已经指向了initiatesl函数中申请的node类型的存储空间, 可以通过*p访问那片存储空间了。

4 结语

上一篇:管网清洗技术——TRIC清洗技术下一篇:《反转地球》