c语言基本概念试题

2024-05-06

c语言基本概念试题(通用8篇)

篇1:c语言基本概念试题

数组基本概念

变量类型 变量名;

元素类型 数组名[元素个数]; // 数组中保存的每一个数据,我们称之为元素

特点:数组只能存放同一种数据类型

数组中的每一个元素都有一个索引号,索引号从0开始部分初始化, 没有赋值的元素默认是0

数组[]中的元素个数只能写整型常量,以及返回值是整型常量的表达式通过变量定义数组,如果没有对数组进行初始化,里面存放的是一些垃圾数据(随机值)在定义数组的同时进行初始化,可以省略元素个数,会自动的计算出数组元素的个数要想给数组一次性赋值(给数组中所有的元素赋值)只能在定义数组的同时,如果定义完数组之后,就不能再对数组进行一次性的赋值数组的地址就是它第0个元素的地址,数组名就是数组的地址

数组元素存放值是从小到大, 而每一个元素存值又是从大到小

因为内存寻址从大到小,所以先定义的数组的地址比后定义的地址大

基本数据类型作位函数参数是值传递

数组作为函数参数传递的是地址

当数组作为函数参数传递的时候,可以省略[]中的元素个数

当数组作为函数参数传递的时候,会自动转换成“指针类型”

而指针在当前操作系统mac 下占用8个字节

想在函数中动态计算数组的元素个数是不行的,因为指针类型占8个字节

选择排序原理:依次选择数组中过的每一个元素和其他元素进行比较

当内循环执行完一次(比较完一次), 最值出现在头角标上

冒泡排序原理: 冒泡排序是拿相邻的两个元素进行比较

特点: 内循环执行完一次(完整的比较完一次),最值出现在尾角标上,

 

C语言数组基本概念理解

 

篇2:c语言基本概念试题

(B)一个C函数可以单独作为一个C程序文件存在(C)C程序可以由一个或多个函数组成(D)C程序可以由多个程序文件组成

4、以下叙述中错误的是_____________。(标准答案:A)

(A)C语言中的每条可执行语句和非执行语句最终都将被转换成二进制的机器指令(B)C语言源程序经编译后生成后缀为.obj的目标程序

(C)用C语言编写的程序称为源程序,它以ASCII代码形式存放在一个文本文件中(D)C程序经过编译、连接步骤之后才能形成一个真正可执行的二进制机器指令文件

13、以下叙述中错误的是A(A)使用三种基本结构构成的程序只能解决简单问题(B)C语言是一种结构化程序设计语言(C)结构化程序由顺序、分支、循环三种基本结构组成(D)结构化程序设计提倡模块化的设计方法

18、以下关于结构化程序设计的叙述中正确的是

(A)一个结构化程序必须同时由顺序、分支、循环三种结构组成(B)有三种基本结构构成的程序只能解决小规模的问题(C)在C语言中,程序的模块化是利用函数实现的(D)结构化程序使用goto语句会很便捷 标准答案:C

19、对于一个正常运行的C程序,以下叙述中正确的是C(A)程序的执行总是从main函数开始,在程序的最后一个函数中结束(B)程序的执行总是从程序的第一个函数开始,在main函数结束(C)程序的执行总是从main函数开始

(D)程序的执行总是从程序的第一个函数开始,在程序的最后一个函数中结束

33、以下选项中关于程序模块化的叙述错误的是

(A)把程序分成若干相对独立的模块,可便于编码和测试

(B)可采用自顶向下、逐步细化的设计方法把若干独立模块组装成所要求的程序(C)把采用分成若干相对独立、功能单一的模块,可便于重复使用这些模块

(D)可采用自顶向上、逐步细化的设计方法把若干独立模块组装成所要求的程序 标准答案:D

34、以下叙述中正确的是

(A)C程序中的注释只能出现在程序的开始位置和语句的后面(B)C程序书写格式严格,要求一行内只能写一个语句(C)用C语言编写的程序只能放在一个程序文件中(D)C程序书写格式自由,一个语句可以写在多行上 标准答案:D

36、以下关于C语言数据类型使用的叙述中错误的是(A)若只处理“真”和“假”两种逻辑值,应使用逻辑类型(B)若要保存带有多位小数的数据,可使用双精度类型(C)整数类型表示的自然数是准确无误差的

(D)若要处理“人员信息”等含有不同类型的相关数据,应自定义结构体类型

38、以下叙述中错误的是

(A)C程序在运行过程中所有计算都以二进制方式进行(B)C程序在运行过程中所有计算都以十进制方式进行

篇3:指针:C语言的重要概念和特色

一、指针的概念

一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。

指针的引入为系统存取数据提供“直接访问”和“间接访问”。有两个上锁且放着物品的盒子A、B, 如果你有A盒子的钥匙, 则可以直接打开A盒子将物品取出;如果你有B盒子的钥匙, 而A盒子的钥匙在B盒子中, 要想取出A盒子中的物品, 则必须先打开B盒子, 取出A盒子的钥匙, 再打开A盒子将物品取出。上面两种情况就是直接访问和间接访问的概念。所谓间接访问, 是先访问存放变量地址的存储单元, 得到该变量的地址, 再对变量内容进行访问。通过指针变量实现对变量的访问方式, 称为间接访问。首先要定义一个指针变量, 然后将一个同类型变量的地址赋给该指针变量 (这时我们称指针变量指向该变量) , 这样就可以进行间接访问了。间接访问的过程是:由指针变量得到变量的地址, 根据该地址找到变量的存储区, 再对该存储区的内容进行存取, 从而实现了对变量的间接访问。

指针变量是一种特殊变量。系统为指针变量分配一块连续存储单元不是供其存储数据, 而是存储内存地址。因此, 指针变量是存储内存地址的变量。在计算机中, 把内存区划分为一个一个的存储单元, 每个单元为一个字节 (8位) , 它们都有一个编号, 这个编号就是内存地址。

注意:1.程序中定义的每个数据在编译后都占有各自的内存区。

2.数据所占有的存储单元个数是由其类型决定的。

3.首地址:第1个单元的地址。

4.表示地址的数与整数的区别。

二、指针的类型

1.指向简单变量的指针。指针所指的数据类可以是简单的数据类型。

例: (1) ;意思是指针变量指向整型变量i

(2) ;意思是指针变量指向字符型变量a

(3) ;意思是指针变量指向数组a

2.指向数组的指针。指针所指的数组既可以是一维数组, 也可以是多维数组。

分析:指针p指向了a数组的首地址, 通过p++来访问a数组的每一元素。

3.指针数组。指针数组是一种特殊的数组, 它每个元素的类型都是指针类型 (即地址) , 其他与一般数组相同。当把某个量的地址放入某元素中后, 即可通过该元素对相应的量进行间接访问。数组元素是由指针变量组成的一种指针。定义如:;指针数组p包含两个元素, 每个元素指向一个整型数据。

分析:指针p是一个数组, 通过for循环语句给指针数组中的每个指针变量赋值, p[1]的初值为数组a的第二行的首地址, 便是元素a[1][1], 因此程序输出7。

4.指向指针的指针。指针变量指向的是指针的地址, 定义如:。

分析:指针数组p的各个指针变量指向数组a的各行首地址, q指向指针数组p的首地址, 等同p[1], 如此, 便等同也就是a[1][1], 因此程序输出7。

5.指向函数的指针。指针变量指向函数的首地址, 然后通过该指针变量调用该函数。定义如:。

分析:表示定义了一个指向函数的指针变量, 函数名max代表了函数的入口地址, 执行p=max后, p指向了函数max, 便是通过p调用函数max。

6.指向文件的指针。C语言对文件的操作并不是直接通过文件名进行的, 而是根据文件名生成一个文件指针, 通过该指针来对文件进行操作.定义如:; (fp为文件指针, 此时fp不指向任何文件) 。

7.指向字符串的指针。字符串是一种特殊的一维数组, 所以上节中介绍的方法同样适用于对字符串的访问。字符串的特殊性在于:字符串的末尾是结束标志′�′, 所以访问字符串时常用结束标志进行判断。

三、指针应用中常见的错误

1.把数据赋给指针变量。指针在使用前必须进行初始化, 赋予指针的值必须是地址。

正确语句:p=&i;“&”是一个取变量地址的运算符。

2.指针常量运算错误。程序中常量指针是不能被修改的。典型有数组名指针常量和指向字符串常量的指针。

3.指针赋值类型不一致。

正确语句:p=a[0];或p=&a[0][0];如果一定把a赋值给p, 必须先将a转换成整型指针类型。如:;。特别是将多维数组赋值给指针变量时应注意类型匹配。

摘要:指针是C语言的精髓部分, 也是C语言的重要特色。本文从指针的基本概念出发, 结合具体实例对指针的类型进行了分析, 并对指针在实际应用中常出现的问题进行了归类总结, 希望对初学者理解“指针”这一重要概念有所帮助。

篇4:c语言基本概念试题

通过这样一个系统,学生可以随时随地进行C语言练习,巩固基础知识,强化专业素养;并且,系统可自动生成C语言试题,减轻教师工作量;在考试结束后,系统能够自动阅卷并评分,提高试题评测的准确性;综上所述,C语言试题生成系统将提高高校的办公效率和学生的专业素养。

关键词:C语言;自动组卷;在线评测;B/S

中图分类号:TP311.52 文献标识码:A 文章编号:1674-7712 (2014) 18-0180-01

一、国内外现状

国内外学者一直都热衷于试题生成系统的研究,试题生成系统主要包含两大主要模块:自动组卷模块和在线考试模块。自动组卷模块在长时间内一直有学者进行深入探讨,但至今并没有一个很好的算法方案。主要问题存在于采用经典的数学方法很难解决试卷生成过程中的收敛过程。目前的自动组卷模块根据其所使用的策略大致可以分成五类:基于随机抽取的自动组卷;基于深度与广度搜索算法的自动组卷;基于遗传算法的自动组卷;基于项目反应理论的自适应测试;基于数据挖掘和知识发现的自动组卷理论。而在线考试模块中,已经出现了ACM/ICPC的源代码在线评测系统,并且在实际应用当中效果显著,明显增加了阅卷人的效率。然而其要求运行环境苛刻,并且不具备通用性(只能应用于源代码的测试),所以并不能很好的应用于国内的科学教育工作当中。我国国内也自主开发了一些在线评测系统,如等级考试的机试测试部分,这就是一个很好的应用实例,每年有数千万的考生参加,也取得了显著的应用效果。然而由于其源代码的封闭性,致使其无法应用于其他高等院校。并且其系统当中也存在一定的效率问题。

二、系统概要设计

试题生成系统采用的是模块化的设计思想,不同的模块化对应不同的功能,而不同的用户对系统有不一样的功能需求。但总体来说本系统的功能需求主要包含包括几方面:(1)对用户的管理。用户在进入系统时,必须要注册用户,登录基本信息。在这项功能里,要实现对用户的有效管理,本系统的用户包括对学生、管理员、老师。在系统的数据库中队用户的基本信息进行记录管理。(2)对试题管理。系统在对试题管理的过程中,系统可以随机分组试题,自动合成试卷,或者通过手工调整、编辑试卷和打印试卷等。老师还可以添加试题,删除试题,修改试题,查询试题。系统会及时更新试题库,对试题库进行备份,同时对不同的试题内容和属性进行分类管理。(3)在线评测。学生在登录系统后,确认考试信息等内容,系统就会及时生成试题,考生就可以进行在线考试,不受地域的影响。而在选择套题的同时,系统会根据考生选择的套题,而列出考试范围的试题,给出每门课程合适的题型,而考生必须在规定的时间来完成。(4)成绩的管理。考生考试完成后,成绩将会保存在数据库中。而老师或者学生在考试完成后,通过任何一台联网的计算机上就可以通过网页方就可以查询学生的成绩。

总的来说,无论是学生考试还是教管理员对系统的管理还是老师对试题的添加修改,都能够随时随地通过网络完成对应的工作。

三、系統的功能模块

通过对系统的需求分析,明确了本系统需要包含题库管理、试卷管理、考试管理、成绩管理、学生管理、教师管理以及系统维护等七个模块。

四、核心模块详细设计

自动组卷模块是本系统设计的重中之重,其只需要教师进行简单的操作,便可根据教师设定的参数完成自动组卷,并且,在组卷之后还能够提示教师修正题目,对不适合的题目进行修改。在整个执行过程中,首先判定用户是否拥有操作权限,若没有操作权限则自动终止;若有操作权限,则要求用户输入试卷生成时用到的必要信息,如试卷难度、试卷类型等,以及各种类型题目的数量,然后根据上述信息构成Z(X),U(X)数组,使用随机选择法生成所要求的试题;在生成试题之后,询问用户是否需要进行修改,若需要进行修改,则根据输入的题目编号,修改对应的题目信息。如此往复直到节后,最后将相关信息分别存入试卷信息表和试题试卷对应表中。

五、结束语

系统的实现有效地提高了教学的管理,大大提高了工作效率。在系统的详细设计开发过程中,采用模块化的设计,采用当前比较流行的ASP技术,AJAX技术,基于B/S应用体系结构,让系统更加健壮和灵活,能够适应系统的不断变化和发展。一个良好的试题生成系统它所涉及的内容和知识面较多,由于时间和条件的限制,系统仍存在一定的问题,今后的研究着重从以下几个方面着手:(1)完善试题生成系统各模块的功能的实现。(2)更深入地完善试题生成的算法,让试题的调度更加合理科学。(3)更深入地学习考试数据的备份与恢复。(4)由于在本系统中主观题还需要老师亲自去修改,在以后的研究方向中需要进一步研究主观题的网上考试的自动打分。(5)在题库的建设问题上做更深入的研究。(6)进一步的做好系统的安全工作,防止黑客的入侵导致试题的泄露。(7)考试的监控系统,功能还有待完善,例如能够实现人脸识别等功能。

参考文献:

[1]谭浩强.C程序设计(第三版)[M].北京:清华大学出版社,2007:10-12.

[2]宋云娴,白鹏.智能教学系统设计与实现[M].北京:电子工业出版社,1995:60-90.

[3]师书恩.信息技术教学应用[M].北京:高等教育出版社,2004:19-35.

[4]郑玉.基于Web的计算机辅助教学系统[J].电子工程师,2001(02):15-17.

[5]深泉,胡宁静.数据库设计和自动组卷中的几个问题[J].湘潭大学自然科学学报,2002(03):27-31.

篇5:如何透彻理解C语言中指针的概念

强大的指针功能是C语言区别于众多高级语言的一个重要特征。C语言指针的功能强大,使用灵活多变,可以有效地表示复杂的数据结构、动态分配内存、高效地使用数组和字符串、使得调用函数时得到多个返回值。而它的应用远不限于此。初学者对于指针的概念总是感到无所适从,有时觉得“自己懂了,为什么编译器就是不懂呢”,常有茫然和无助的感觉。

学好指针的关键在于深入了解内存地址的空间可以理解为一个一维线性空间,内存的编址和寻址方法,以及指针在使用上的一些规定。事实上,指针就是方便我们对内存地址直接进行操作的,是为程序员服务的,我们只要抓住指针想要帮助我们解决什么问题这个核心,就可以轻松地理解它的工作原理。

什么是指针,指针有什么作用

指针就是指向一个特定内存地址的一个变量。简化了的内存空间模型是按照从0到某一个数(比如1048575=1M-1)的一维线性空间,其中的每一个数对应一个存储单元,即1个字节。指针有两个属性:指向性和偏移性。指向性指的是指针一定要有一个确定的指向,偏移性则是体现指针重要应用的方面,即指针可以按程序员的要求向前或向后偏移。

指针的应用往往与数组联系在一起,为了方便说明问题,不妨从数组开始解释指针的偏移。数组就是许多的变量,它的一个重要特征就是在内存空间中连续地存放,而且是按下标顺序存放。比如我们定义一个有100个变量的一维整型数组,它一定从内存的某一个存储单元开始按数组下标顺序存放,连续占用100*4=400字节。当我们定义一个数组时,系统就会自动为它分配一个指针,这个指针指向数组的首地址。(在本文剩余部分的论述中,不加区分地使用“指向数组的首地址”与“指向数组的第一个元素”这两种说法,事实上这两种说法也是一致的。)

为了让系统了解每一次指针偏移的单位,也为了方便程序员进行指针偏移(让程序员记住一个整形变量占用4字节,一个字符型变量占用1字节„„等等是很麻烦的),不用每次去计算要偏移多少个字节,C语言引入了指针的基类型的概念。基类型的作用就是让系统了解某个指针每次偏移的字节数。比如,对于一个字符型指针,它每次偏移(比如ptr=ptr+1)所起到的作用就是让指针偏移1字节;而对于一个整型指针,它每次偏移就应该是4字节。这样操作数组时就带来了方便。比如对于一个指向某个整型数组起始存储单元(称为首地址)的指针ptr,ptr=ptr+1就表示将该指针指向这个数组的下一个元素的存储单元,即向后移动4字节,而不仅仅是移动一个存储单元(即移动1字节)。

&()、*()、和[ ]运算符的意义

在本文中,将&()、*()和[ ]都看成是运算符。这样可以方便理解这三个概念。简单地说,&()将某个标识符(比如变量)转化为其在内存空间中的地址,而*()是产生一个对应于某个地址的标识符,[ ]就更复杂一点,ptr[i]表示将ptr这个指针虚拟地按其基类型进行i个单位的后移,再进行*(ptr)运算。但这是一个虚拟的后移,即ptr[i]并不改变ptr的指向,只是将其后移i个单位并取*()运算的结果算出来了而已。要改变指针的指向,我们只能通过类似于ptr=ptr+i这样的语句来实现。

实际中,我们往往不愿意经常改变指针的指向,因为指针的移动虽然是自由的,但移动后往往会“移不回来”,因为我们可能无法清楚地确定指针的偏移量。后面我们将看到,对于用指针来表示的数组,其元素的引用和赋值是完全可以不用改变指向这个数组的首地址的指针指向的,而一旦要改变这个指针的指向,问题就会变得复杂一些,我们在后面有一个关于程序的命令行参数处理例子专门介绍这个问题。

指针类型和系统自动分配的指针

指针可以指向几乎所有我们感兴趣的程序设计要素:函数、数组、结构体、链表节点等等。其中不同函数间往往并不存在严格的线性关系。链表节点可以根据算法需要在逻辑上(或物理上)不按线性连续存储。但数组、结构体的共同特征就是它们在物理上都是线性连续存储的。只要指针指向了它们的首地址,就可以通过简单的偏移来访问各个它们的元素。指针的偏移性在这两种数据结构中发挥着至关重要的作用。这时,我们再回想基类型的定义目的,就会有更深层次的认识了。对于一个数组或结构体,它的基类型长度应当是其元素的长度(这里的长度即指在内存空间中占用的字节数),而不再限于定义为某种简单数据类型的长度。

在我们定义数组和函数时,系统都会为其自动分配一个指向其首地址的指针。其中,指针在数组中的应用是最频繁的,也是最基础的。对于一个数组,其名称就是一个指针变量,亦即假如我们定义“int a[10];”的同时就定义了“int *a=a;”(这只是为了说明问题,这样的语句显然是不合法的)。

数组应用中典型的二级指针

设定一个指向指针的指针,即设定一个二级指针。一般认为,指针不宜超过二级,否则会大大增加逻辑错误出现的可能性。因此,下面详细解释数组二级指针的实现方法及原理。在此基础上理解指针的其它相关概念是非常简单的。

刚才一直提及指针的基类型,以及对它的正确理解方法。请在阅读下面论述的过程中不断地考虑“我们所提到的每个指针的基类型是什么”这个问题。

首先我们先要对二维数组进行重新定义,即将一个M*N的二维数组定义为有M个元素的一维数组,它的每个元素都是一个具有N个元素的一维数组。这种理解方式对于以前学习过Basic、Pascal等语言的程序员来说比较难以接受,因为它们更容易直观地将其理解为一张二维表。事实上,二维数组在内存中的线性存储是这样实现的:把每一行看作它的一个元素,然后按照一维数组的按下标顺序排列的原则以每一行为单位进行排列。而对于每一行,也还是按照一维数组按下标顺序排列的原则进行排列。也就是说,我们可以按行优先的方式将数组的数字逐个“填入”内存空间。或者也可以说,多维数组在内存中的排列方式是递归定义的。

既然如此,当我们定义 “int a[10][10];”的时候,a是什么样的指针呢?是的,a就是一个二级指针。它的基类型是有10个元素的一维数组,不再是整型变量了。它所指向的是一维数组指针(第一行的数组指针)。当我们执行a=a+1的时候,a将指向二维数组第二行的数组指针,而不是第一行的第二个元素,因为基类型的长度决定了a+1跨越了一整行。

因此,我们要得到数组a的(i,j)位置上的元素的值,应该按照下面的步骤来进行:

1、a+i,这表示将a指针移到第i行的首地址。

2、*(a+i),这表示将第i行的首地址转化为第i行的标识符,前面已经述及,*()运算符的作用就是将地址转化为标识符。但*(a+i)不是第i行的第一个元素而是一个指针,这个指针的基类型已经变成了整型变量,不再是有10个元素的一维数组了。或许你要说,第i行的首地址不就是第i行第一个元素的地址吗?那么*(a+i)不就是第i行第一个元素的值了?首先,我们可以肯定*(a+i)不是第i行第一个元素的值,但第i行的首地址的确就是第i行第一个元素的地址。前面对*()运算符的说明只是一个表面现象,下面的说法可以辅助你理解*()运算符的真正本质:*()将指针还原为其所指,而不是简单地将地址变成这个地址所存储的值。*()将地址变成这个地址所存储的值这样的说法只对一级指针是正确的。对于二级指针,*()只是将二级指针还原为其所指,即还原为一级指针。物理上“第i行的首地址同时就是第i行第一个元素的地址”这一事实,是容易导致混淆的根本原因。但只我们要从逻辑的角度出发,就可以较为轻松地理解这个问题。

3、*(a+i)+j,这表示将一级指针向后偏移j个单位,要注意*(a+i)这个指针已经是一个以整型变量为基类型的指针了。这时*(a+i)+j是一个偏移后的一级指针,它的值是a[i][j]元素的地址,亦即它所指的就是a[i][j]元素。

4、*(*(a+i)+j),将一级指针还原为其所指,即得到了a[i][j]元素的值。

理解了以上的概念,将会对指针有全新的认识,而对于二级以上的指针和其它类型的指针,原理也都是类似的。对指针的更深入理解只有在编程的实践中得到。从算法设计的角度来看,使用指针对数组进行遍历等操作可降低时间复杂度,因为指针按照基类型偏移1个单位的效率很高。

一维指针数组中的二级指针

透彻地理解下面这段程序对于进一步理解指针的原理是很有裨益的。下面是一个将系统分配的指针(即数组名指针)进行偏移的例子:

main(int argc,char *argv[]){ while(argc>1){ ++argv;printf(“%sn”,*argv);--argc;} }

粗略地看,不难发现这个程序的作用就是将其命令行参数(不包括第一个程序路径及文件名参数)逐个输出。但其中却用到了二级指针,究竟是也不是,我们从细节入手分析。

首先,argv是一个指针数组,它的每个元素所指向的是每个命令行参数字符串的首地址。比如,我们的参数是“abc def”,那么argv[1]和argv[2]所指向的就分别是字符串“abc”和“def”的首地址(注意argv[0]指向的是程序路径及文件名字符串的首地址)。

那么,第四行的++argv是什么意思呢?我们知道,一个数组的名称就是一个指针,在没有被改动的情况下,它指向这个数组的首地址。++argv的作用就是将argv这个指针(数组名)按照其基类型宽度向后移动一个单位,如果原来argv所指向的是argv这个数组的首地址,那么执行以后它将指向其第二个元素(即argv[1])。也就是说,这个程序改动了数组名(本身也就是一个指针)的指向,不断将其后移。

理解到这里,你可能已经初步感到问题并不像看上去那么简单了。下面的一句“printf(“%sn”,*argv)”更是有意义了。你会不会觉得奇怪呢?因为printf(“%s”,ptr)或者puts(ptr)所需要的参数都是指针。既然argv已经是指针,又为什么要在前面再加上一个“*”运算符呢?原因如下:argv确实是指针,但它所指的argv这个数组自己的某一个元素(因为我们已经分析过,argv这个指针是从自己的第一个元素argv[0]的地址开始不断地后移的)。这看起来和一个指向字符串的指针char *ptr=”string content”是类似的。但我们在输出ptr指针所指的字符串时是使用printf(“%s”,ptr)而不是printf(“%s”,*ptr)来输出的。那如果我们的这句话是“printf(“%sn”,argv)”会怎样呢?程序运行后得到的是一堆乱码。那这堆乱码是什么呢?这堆乱码实际上是argv这个在不断向后移动的指针的所指,即argv数组的元素的地址(如&argv[1],&argv[2]等),也即指向某个命令行参数字符串的首地址的指针的地址。如果能理解到这一点,就会知道为什么我们说这个短短的程序中用到了二级指针了。既然argv只是argv这个数组的某个元素的地址,那么加上一个“*”运算符对其进行间接访问,即可得到argv数组的元素的值,这个值是一个指针,它指向某个命令行参数字符串的首地址。因此这个语句的意义也就大白于天下了。事实上,这个语句还可以等价地写为“printf(“%sn”,argv[0])”,因为对于一个指针来说,*(ptr)运算与ptr[0]运算是无条件等价的。不要认为这个语句等价为“printf(“%sn”,argv[i])”(i是循环变量),因为argv这个数组名指针本身已经在后移了,不能用i再次进行后移。

虽然理解起来显得复杂,但程序本身却短小精悍,可以作为处理命令行参数的一般方法来使用。这也从一个侧面证明了指针可以大大简化某些程序设计过程。

由此我们可以总结出,所有指针数组中都包含了二级指针。第一次由指针数组名指向其元素,第二次由其元素再次指向其它的程序设计要素(本例中是字符串的首地址)。

使用字符串常量的一个常见错误分析

这个部分留给具有一定编程经验的读者。如果要使用C语言编写复杂应用程序,下面的知识是必须的。

在指针应用中容易导致错误的一种常见行为就是对字符串常量进行更改。程序员在对字符串常量进行引用、修改时,一定要特别注意C语言对于字符串常量的处理方法。否则,容易导致十分隐蔽的错误。这些错误往往集中在熟悉Pascal等编程语言的程序员身上,并在OOP编程中出现。

当程序中包含了几个字符串常量时,这些常量是在程序入口一次性分配内存的,而不是在每次执行某个函数时开辟一块新的内存区域来存放的。下面用一个具体的错误例子来说明这个问题。

这是一个C++ Builder 6下的例子。某个窗体的代码中包含下面两个事件函数:

void __fastcall TfrmMain::btnSearchClick(TObject *Sender){ nmuMain->InputString = edtKeyword->Text;char* query = new char[ nmuMain->Encode.Length()+ 1 ];strcpy(query, nmuMain->Encode.c_str());HTTPGet(strcat(“http://&q=”,query));} void __fastcall TfrmMain::nmhMainSuccess(CmdType Cmd){ frmMain->pnlStatus->Caption = “Successfully retrieved data from Google.”;AddResults();}

在btnSearchClick事件中,请注意strcat(str1,str2)这个函数。它的作用是将str2连接到str1的后面。这将导致str1变长。这会导致两种后果:

第一,当用户再次点击btnSearch时,HTTPGet()函数的参数将变成“http://&q=”和第一次用户提供query,以及第二次用户提供的query。为什么呢?因为字符串常量“http://&q=”变长了,它已经包含了第一次用户提供的query。

第二,第二个事件函数中的“Successfully retrieved data from Google.”将被替换成用户输入的query。这又是为什么呢?前已述及,两个字符串常量是在程序入口一次性分配内存的。它们是在内存中连续存放的。因此第一个字符串常量变长自然就替换了后面字符串常量。如果没有意识到这一点,很可能导致难以预料的严重系统错误。事实上,C语言对字符串的起始和结束的判定严格遵守下面的简单规律:根据字符串名称(是一个指针)指向的地址来确定字符串的起始,根据“”转义符来确定字符串的中止。

为了修正上面的错误,应该避免对字符串常量进行修改。即将第一个事件函数改为:

void __fastcall TfrmMain::btnSearchClick(TObject *Sender){ nmuMain->InputString = edtKeyword->Text;char* query = new char[ nmuMain->Encode.Length()+ 1 ];char searchstr[41];char* original_searchstr = “http://&q=”;strcpy(searchstr, original_searchstr);strcpy(query, nmuMain->Encode.c_str());HTTPGet(strcat(searchstr,query));}

此外,在OOP编程中,应该注意两个有关字符串的问题。第一,如果采用 char* str = new char[ length ];来分配内存空间,虽然可以实现根据带有变量的表达式length“动态”分配内存空间的效果,比如length=i+j-1;但这种分配也是一次性的,即下一次执行该语句时,不会再次分配新的内存区域。第二,C++ Builder从Pascal的VCL中引入了AnsiString类型,这是一种指针类型。应注意绝大部分C++ Builder的函数中如果要求AnsiString作参数(不论是要求地址还是指针),都应提供字符串名称来作参数。典型的例子包括str.pos(str1),ShowMessage(str),以及上面的例子中出现的几种情况。

最后,所有C++ Builder程序员不应忘记VCL是用Pascal语言编写的。遇到一些难以解决或解释的问题时,不妨从Pascal语言的编程思想入手,一般很快就能想到解决办法(绝大部分Object Pascal函数都有对应的C语言版本)。这种方法对于学过Delphi的程序员来说是特别有用的。

篇6:C语言指针笔试题

那么要是想让程序跳转到绝对地址是0×100000去执行,应该怎么做?

答案:*((void (*)( ))0×100000 ) ( );

首先要将0×100000强制转换成函数指针,即:

(void (*))0×100000

然后再调用它:

*((void (*))0×100000);

用typedef可以看得更直观些:

typedef void(*) voidFuncPtr;

*((voidFuncPtr)0×100000);

12. 分析下面的程序:

void GetMemory(char p,int num)

{ //p,指向指针的指针,*p,p指向的指针(即str),p,最终的对象,str指向的单元

*p=(char *)malloc(num); //申请空间首地址付给传入的被p指向的指针,即str

}

int main

{

char *str=NULL;

GetMemory(&str,100); //传入指针变量本身的地址

strcpy(str,”hello”);

free(str);

if(str!=NULL)

{

strcpy(str,”world”);

}

printf(“n str is %s”,str); 软件开发网

getchar;

}

问输出结果是什么?

答案:输出str is world。

free 只是释放的str指向的内存空间,它本身的值还是存在的.所以free之后,有一个好的习惯就是将str=NULL.

此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,

尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。

这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。

当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存 前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的。

13.char a[10];

strlen(a)为什么等于15?

#include “stdio.h”

#include “string.h”

void main

{

char aa[10];

printf(“%d”,strlen(aa));

}

答案:sizeof和初不初始化,没有关系;

strlen和初始化有关。

14.char (*str)[20];/*str是一个数组指针,即指向数组的指针.*/

char *str[20];/*str是一个指针数组,其元素为指针型数据.*/

15.

#include

#include

#include

#include

#include

#include

typedef struct AA

{

int b1:5;

int b2:2;

}AA;

void main

{

AA aa;

char cc[100];

strcpy(cc,”0123456789abcdefghijklmnopqrstuvwxyz”);

memcpy(&aa,cc,sizeof(AA));

cout << aa.b1 <

cout << aa.b2 <

}

输出结果是多少?

答案:-16和1

篇7:C语言测试题

a. 静态链表既有顺序存储的优点,又有动态链表的优点。所以,它存取表中第i个元素的时间与i无关。

b. 静态链表中能容纳的元素个数的最大数在表定义时就确定了,以后不能增加。

c. 静态链表与动态链表在元素的插入、删除上类似,不需做元素的移动。

d. 静态链表就是一直不发生变化的链表。

(2)在双向链表指针p的结点前插入一个指针q的结点操作是______。

a. p->Llink=q; q->Rlink=p; p->Llink->Rlink=q; q->Llink=q;

b. p->Llink=q; p->Llink->Rlink=q; q->Rlink=p; q->Llink=p->Llink;

c. q->Rlink=p; q->Llink=p->Llink; p->Llink->Rlink=q; p->Llink=q;

d. q->Llink=p->Llink; q->Rlink=q; p->Llink=q; p->Llink=q;

(3)下面说法正确的是______。

a. 顺序存储结构的主要缺点是不利于插入或删除操作;

b. 线性表采用链表存储时,结点和结点内部的存储空间可以是不连续的;

c. 顺序存储方式插入和删除时效率太低,因此它不如链式存储方式好;

d. 顺序存储方式只能用于存储线性结构。

(4)下面说法正确的是______。

a. 线性表只能用顺序存储结构实现。

b. 为了很方便的插入和删除数据,可以使用双向链表存放数据。

c. 顺序存储方式的优点是存储密度大,且插入、删除运算效率高。

d. 链表是采用链式存储结构的线性表,进行插入、删除操作时,在链表中比在顺序存储结构中效率高。

(5)下面说法正确的是_________。

a. 数据元素是数据的最小单位。

b. 队列逻辑上是一个下端口和上端能增加又能减少的线性表。

c. 任何一个递归过程都可以转换成非递归过程。

篇8:c语言基本概念试题

C语言是一门语法灵活,应用广泛的高级程序设计语言。但是由于授课对象都是初次接触程序设计的大学低年级学生,加之有些教科书内容抽象化,概念难以理解,在实际教学中反映出诸多问题。函数是C语言程序设计的重点及难点,学生对函数的参数及其传递方式很难理解和掌握。针对这种情况,结合多年的实践教学经验,将内存概念引入函数调用,帮助学生理解C语言中函数参数的传递方式,在教学实践中取得了较好的效果。

1 基本概念

函数是C语言程序设计的基本构件。一个C语言程序由一个或多个函数组成,每个函数完成一定的功能。如同变量一样,函数在使用之前必须先进行定义。

引用函数的返回值或执行函数中的代码只能通过函数的调用完成,调用可以看作是一种动作,动作的实施需要有主动方和被动方,因此就有主调函数和被调函数之分,在主调函数代码中通过引用被调函数名并加以必要的参数来调用被调函数。

函数的参数主要用于主调函数向被调函数传递数值。在函数定义时,函数名后面圆括号内的参数称为形式参数,简称形参;在函数调用时,函数名后面圆括号内的参数称为实际参数,简称实参。在函数调用时,首先将实参传给形参,然后才能执行函数体。在C语言中,参数的类型不同,其参数值的传递方式也不完全相同,一般分为“值传递”方式和“地址传递”方式。

2 内存概念在函数调用中的应用

C语言程序在计算机内的运行过程中,编译系统根据变量的类型,为其分配相应的内存单元,以便存放变量的内容,内存区的每一个存储单元有一个编号,这就是“地址”,存放“地址”的变量称为指针变量,各种程序中的操作在内存中均表现为对内存相应单元的读写操作。

当普通数据类型的变量或数组元素作为参数时,属于值传递;当参数是指针变量或数组名等与地址有关的量时,属于地址传递。值传递具有单向性,而地址传递具有双向性特点。其中的难点为,两种传递方式为何不同。通过将函数调用过程中内存的变化过程动态展现,使这一难点迎刃而解。

2.1 值传递

用普通数据类型的变量或数组元素充当函数参数,函数调用开始,系统为形参开辟一临时存储区,将各实参值复制给形参,形参即得到实参值。有如下程序:

函数swap的功能是交换形式参数x、y的值。主函数main中的语句swap(a,b)是函数调用语句,调用swap函数,希望将a和b的值进行交换,但由于是值传递的,在函数swap中只对形参x和y进行交换,并没有达到对实参a和b实施交换的目的。

结合内存图分析函数调用过程:函数调用前系统只为实参a和b分配存储单元,假定其地址为1000和1004,如图1所示;函数调用时,系统根据形参x和y的类型为其临时分配存储单元,假定其地址为2000和2004,并将实参值复制到对应的形参单元中,形参即得到实参值,如图2所示;当运行swap函数时,由于实参、形参占用不同的存储单元,形参值的变化不会影响实参值,如图3所示;函数调用结束时,实参a和b的值并未交换,如图4所示,即传值方向为单向不可逆的。

2.2 地址传递

2.2.1 指针作为函数参数

实参被定义之后有自己的地址,在主调函数中虽然未给实参赋任何值,但可以抽象地将实参所拥有的地址作为它所拥有的“值”,该“值(地址)”被形参接收,形实参则具有相同的地址;函数调用结束,形参释放所占据的内存单元,实参相应地得到形参的值。有如下程序:

函数exchange中形式参数p1、p2为指针变量,函数的功能是交换指针变量的内容。主函数main调用exchange函数时,把变量a和b的地址传送给形参p1和p2,执行exchange函数时通过地址实际修改了a和b的值,达到了对实参a和b实施交换的目的。

结合内存图分析函数调用过程:函数调用前系统只为实参a和b分配存储单元,假定其地址为1000和1004,如图5所示;调用函数,在参数传递时和值传递本质上是一样的,只是这种值是一种特殊的“地址”值,即参数传递时将a和b的地址分别赋给指针变量p1和p2,使p1指向a,p2指向b,如图6所示;接着执行exchange函数的函数体,使*p1和*p2的值互换,实际上是对变量a和b的内存单元进行修改,如图7所示;所以函数调用结束时变量a和b的值已经被交换了,如图8所示,这就是地址传递的双向性的本质含义。

2.2.2 数组名作为函数参数

数组名作为函数参数,在函数调用时,把实参数组的起始地址传给形参数组,则形参数组和实参数组占用同一存储区域,对形参数组中某一元素的操作,也就是操作实参数组中的对应元素。有如下程序:

sort函数的作用是对数组元素进行排序,形式参数是一个不指出元素个数的数组和一个整型变量,主函数main调用sort函数时,把实参数组a的起始地址传送给形参数组b,这就意味着形参数组元素存放地址和实参数组元素存放地址相同,执行sort函数,对形参数组b的元素排序,实际对实参数组a的元素也进行了排序。

结合内存图分析函数调用过程:函数调用前系统只为实参数组a分配存储单元,假定其起始地址为1000,如图9所示;调用sort函数时,作为实参的a是一个数组名,对应形参是一个不指出元素个数的数组b,在这种情况下,形参和实参结合时并不把实参数组的各个元素值逐个赋给相应的形参数组,并且也不为形参数组分配相应的存储空间,而是将实参数组第一个元素的地址传送给相应的形参,即形参数组首元素(b[0])和实参数组首元素(a[0])具有同一地址,如图10所示数组b的首元素地址也是1000,同理,a[n]和b[n]指的是同一存储单元;执行sort函数,形参数组中各元素的值发生变化会使实参数组元素的值同时发生变化,如图11所示;因此,当sort函数调用结束后,实参数组a的元素已排序,如图12所示,体现了数组名作为函数参数时的双向性传递机制。

3 结束语

(1)“值传递”方式是将实参的值传递给对应的形参,形参临时分配内存单元,形参的变化不会引起实参的改变,数据传递是单向的。其形参一般是变量,实参为任何表示具体值的非地址变量,如普通变量、数组元素等。

(2)“地址传递”方式是将实参的地址传递给对应的形参,实参和形参对应的变量或数组将占用相同的内存单元,形参的变化将引起实参的改变。接受地址的形参一般是指针变量或数组名,实参可以是变量的地址、数组名或存放地址的指针变量等。

本文将内存概念引入函数调用,让学生通过直观的图示来理解较难的理论知识,该方法已在实际教学中运用并取得了较好的效果。

摘要:通过分析计算机内存管理和C语言程序执行的相互关系,将内存概念引入函数调用,对函数参数的传递方式进行了初步探讨。

关键词:C语言,实际参数,形式参数,函数调用,内存

参考文献

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

[2]郑丽英,李玉龙,等.C语言程序设计[M].北京:中国铁道出版社,2003.

[3]白中英.计算机组成原理[M].北京:科学出版社,2000.

[4]Turbo C User’s Guide[Z].Borland International,Inc,1987.

上一篇:爱国主义教育心得体会下一篇:一年级下学期体育工作总结