想旅行的钥匙 · 如何用folium在地图上画点 - CSDN文库· 5 月前 · |
耍酷的炒饭 · 6 MySQL视图 多表连接(内连接 ...· 1 年前 · |
直爽的围巾 · QList与QVariant互相转换_var ...· 1 年前 · |
会开车的熊猫 · Springboot ...· 1 年前 · |
俊秀的冲锋衣 · MySQL逗号分割字符串转行,转列,json ...· 1 年前 · |
本文实例讲述了C++实现string存取二进制数据的方法,分享给大家供大家参考。具体方法分析如下:
一般来说,STL的string很强大,用起来也感觉很舒服,这段时间在代码中涉及到了用string存取二进制数据的问题,这里记录一下,以供以后参考。
首先提一下STL中string的参考资料:
http://www.cplusplus.com/reference/string/string/
,不懂的朋友可以看下。
在数据传输中,二进制数据的buffer一般用系统预设的大数组进行存储,而不是STL的string等,比如:
const int max_length = 1024 * 1024;
unsigned char data[max_length];
因为二进制数据中可能会包含0x00(即:’\0’),刚好是字符串结束标志……
如果我们的代码是如下写的:
char data[max_length];
size_t length = sockClient.read_some(boost::asio::buffer(data), ec);
string strData(data);
我只能说,这个处理字符串应该没问题,如果是二进制的话,会被string的构造函数给截断一部分,导致strData和data的数据不一致。
其实一个简单的demo就可以说明问题,比如如下代码:
#include <string>
#include <iostream>
using namespace std;
int main()
char data[] = {'A','b',0x00,'c','d'};
string str1(data),str2(data,sizeof(data));
cout<<str1<<endl;
cout<<str1.size()<<endl;
cout<<str2<<endl;
cout<<str2.size()<<endl;
return 0;
运行效果如下:
Ab
2
Abcd
5
从运行结果不难发现,采用str2的那种方式就可以保证string中的数据和原始data中的数据一致。这是因为采用不同的构造函数不同,导致结构完全不一样,这个可以从我前面给出的网址中去查看具体的构造函数说明加以理解。这里我们回到前面的那个问题,如果想保存二进制的话,我们应该如下操作:
char data[max_length];
size_t length = sockClient.read_some(boost::asio::buffer(data), ec);
string strData(data,length);
如果要取出数据的话,也简单(这个还以socket数据收发为例):
......
// deal with strData
......
boost::asio::write(sockClient, boost::asio::buffer(strData.c_str(),strData.length()));
这里的strData.c_str()即为数据,strData.length()即为要发送的数据长度(当然也可以使用strData.size()来操作)。
当然,我们这里用string来存取二进制数据,也只是为了操作方便,感觉这个不是太好,应该会有很多朋友不提倡这种做法的,这里提供一个思路,大家觉得好就采用,觉得不好就一笑了之,呵呵……
希望本文所述对大家的C++程序设计有所帮助。
转载自:http://www.jb51.net/article/55960.htm
本文实例讲述了C++实现string存取二进制数据的方法,分享给大家供大家参考。具体方法分析如下: 一般来说,STL的string很强大,用起来也感觉很舒服,这段时间在代码中涉及到了用string存取二进制数据的问题,这里记录一下,以供以后参考。 首先提一下STL中string的参考资料:http://www.cplusplus.com/reference/string/string/ ,不懂的
代码如下: 代码如下:<SPAN xss=removed> char* p = “1010110001100”; int n = 0; for(int i=0;i<strlen xss=removed>
您可能感兴趣的文章:C++ 十进制转换为二进制的实例代码C++实现string存取二进制数据的方法C++实现读入二进制数并转换为十进制输出详解C++编程中对二进制文件的读写操作C++二进制翻转实例分析C++中几种将整数
一、PHP简介
PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的WEB站点。本教程并不想让你完全了解这种语言,只是能使你尽快加入开发动态web站点的行列。我假定你有一些HTML(或者HTML编辑器)的基本知识和一些编程思想。
PHP是能让你生成动态网页的工具之一。PHP网页文件被当作一般HTML网页文件来处理并且在编辑时你可以用编辑HTML的常规方法编写PHP。
PHP代表:超文本预处理器(PHP: Hypertext Preprocessor)。PHP是完全免费的,不用花钱,你可以从PHP官方站点(http://www.php.net)自由下载。PHP遵守GNU公共许可(GPL),在这一许可下诞生了许多流行的软件诸如Linux和Emacs。你可以不受限制的获得源码,甚至可以从中加进你自己需要的特色。PHP在大多数Unix平台,GUN/Linux和微软Windows平台上均可以运行。怎样在Windows环境的PC机器或Unix机器上安装PHP的资料可以在PHP官方站点上找到。安装过程很简单。
如果你的机器解决了2000问题,那么PHP也一样没有千年虫问题!
1.1 历史
三年前,Rasmus Lerdorf为了创建他的在线简历而创造了"个人主页工具"(Personal Home Page Tools)。这是一种非常简单的语言。其后越来越多的人们注意到了这种语言并对其扩展提出了各种建议。在许多人的无私奉献下以及这种语言本身的源代码自由性质,它演变成为一种特点丰富的语言,而且现在还在成长中。
PHP虽然很容易学习,但是速度上比mod_perl(植入web服务器的perl模块)慢。现在有了可以与mod_perl速度想媲美的被称作Zend的新引擎,而PHP4就可以充分利用这个引擎。PHP4还处在BETA测试阶段。Andy Gutmans和Zeev Suraki是Zend的主要作者。可以去Zend站点(http://www.zend.com)了解更多。
PHP的应用在个人性质的web工程中增长显著。根据Netcraft在1999年10月的报告,有931122个域和321128个IP地址利用PHP技术。
1.2 PHP的先进之处
应用PHP有许多好处。当然已知的不利之处在于PHP由于是开放源码项目,没有什么商业支持,并且由此而带来的执行速度缓慢(直到PHP4之前)。但是PHP的邮件列表很是有用而且除非你正在运行像Yahoo!或者Amazon.com这样的极受欢迎的站点,你不会感觉出PHP的速度与其他的有什么不同。最起码我就没有感觉出来!好了,让我们来看看PHP有那些优点:
- 学习过程
我个人更喜欢PHP的非常简单的学习过程。与Java和Perl不同,你不必把头埋进100多页的文档中努力学习才可以写出一个象样的程序。只要了解一些基本的语法和语言特色,你就可以开始你的PHP编码之旅了。之后你在编码过程中如果遇到了什么麻烦,还可以再去翻阅相关文档。
PHP的语法与C,Perl,ASP或者JSP。对于那些对上述之一的语言较熟悉的人来说,PHP太简单了。相反的,如果你对PHP了解较多,那么你对于其他几种语言的学习都很简单了。
你只需要30分钟就可以将PHP的核心语言特点全部掌握,你可能已经非常了解HTML,甚至你已经知道怎样用编辑设计软件或者手工来制作好看的WEB站点。由于PHP代码能够无障碍的添加进你的站点,在你设计和维护站点的同时,你可以很轻松的加入PHP使得你的站点更加具有动态特性。
- 数据库连接
PHP可以编译成具有与许多数据库相连接的函数。PHP与MySQL是现在绝佳的组合。你还可以自己编写外围的函数取间接存取数据库。通过这样的途径当你更换使用的数据库时,可以轻松的更改编码以适应这样的变化。PHPLIB就是最常用的可以提供一般事务需要的一系列基库。
- 可扩展性
就像前面说的那样,PHP已经进入了一个高速发展的时期。对于一个非程序员来说为PHP扩展附加功能可能会比较难,但是对于一个PHP程序员来说并不困难。
- 面向对象编程
PHP提供了类和对象。基于web的编程工作非常需要面向对象编程能力。PHP支持构造器、提取类等。
- 可伸缩性
传统上网页的交互作用是通过CGI来实现的。CGI程序的伸缩性不很理想,因为它为每一个正在运行的CGI程序开一个独立进程。解决方法就是将经常用来编写CGI程序的语言的解释器编译进你的web服务器(比如mod_perl,JSP)。PHP就可以以这种方式安装,虽然很少有人愿意这样以CGI方式安装它。内嵌的PHP可以具有更高的可伸缩性。
- 更多特点
PHP的开发者们为了更适合web编程,开发了许多外围的流行基库,这些库包含了更易用的层。你可以利用PHP连接包括Oracle,MS-Access,Mysql在内的大部分数据库。你可以在苍蝇上画图,编写程序下载或者显示e-mail。你甚至可以完成网络相关的功能。最好的是,你可以选择你的PHP安装版本需要哪些功能。引用Nissan的Xterra的话来说就是PHP可以做到你想让它做到的一切而且无所不能!
1.3 竞争对手:ASP,mod_perl,JSP
我当然不清楚ASP/JSP能做些什么。不过明确的是编写那样的代码有多简单,购买它们会有多昂贵以及它们需要多么昂贵和强大的硬件。如果你有什么中立的观点(比如说没有被SUN和Microsoft的百万美金所影响),请顺便通知我。
据我所知,JSP基于Java,因此Java程序员可以轻松开始编码。ASP只是一个一般的引擎,具有支持多种语言的能力,不过默认的并且是最常用的还是VBScript。
mod_perl与Perl一样强大,只是更快一些。
二、PHP入门
PHP站点的在线教程已经很棒了。在那里还有一些其他教程的链接。而本文的该部分将让你对PHP熟悉一点。我不可能做到没有任何遗漏,我的目的只在于能让你迅速开始你的PHP编程。
2.1 首要条件
你首先必须要有一个正在工作着的支持PHP的web服务器。我假定在你的服务器上所有PHP文件的扩展名为.php3。
2.2 PHP的安装
生成一个名为test.php3的文件,含有以下内容:
然后在你的浏览器中打开此文件。看看这个页面你就知道你的PHP安装使用的选项了。
2.3 语法
就像前面提到的一样,你可以混合编写你的PHP代码和HTML代码。因此你必须有办法将两者区别开来。以下就是你可以采用的几种方法。你可以选用其中一种你最适应的并且就这样坚持这种方法!
从HTML中分离
以下是可以使用的方法:
. . .
与Perl和C一样,在PHP中用(;)来分隔语句。那些从HTML中分离出来的标志也表示语句的结束。
PHP支持C,C++和Unix风格的注释方式:
/* C,C++风格多行注释 */
// C++风格单行注释
# Unix风格单行注释
Hello,World!
通过我们已经学过的知识,你可以编写一个最简单的程序输出一个也许是程序世界中最有名的词语:
2.4 数据类型
PHP支持整数、浮点数、字符串、数组和对象。变量类型通常不由程序员决定而由PHP运行过程决定(真是好的解脱!)。但是类型也可以被函数cast或者settype()明确的设定。
数值类型可以是整数或是浮点数。你可以用以下的语句来为一个数值赋值:
$a = 1234; # 十进制数
$a = -123; # 负数
$a = 0123; # 八进制数 (等于十进制数的83)
$a = 0x12; # 十六进制数(等于十进制数的18)
$a = 1.234; # 浮点数"双精度数"
$a = 1.2e3; # 双精度数的指数形式
字符串可以由单引号或双引号引出的字段定义。注意不同的是被单引号引出的字符串是以字面定义的,而双引号引出的字符串可以被扩展。反斜杠(\)可以被用来分割某些特殊字符。举例如下:
$first = 'Hello';
$second = "World";
$full1 = "$first $second"; # 产生 Hello World
$full2 = '$first $second';# 产生 $first $second
可以将字符和数字利用运算符号连接起来。字符被转化成数字,利用其最初位置。在PHP手册中有详细的例子。
数组与哈希表
数组与哈希表以同样的方法被支持。怎样运用取决于你怎样定义它们。你可以用list()或者array()来定义它们,也可以直接为数组赋值。数组的索引从0开始。虽然我在这里没有说明,但是你一样可以轻易的使用多维数组。
// 一个包含两个元素的数组
$a[0] = "first";
$a[1] = "second";
$a[] = "third"; // 添加数组元素的简单方法
// 现在$a[2]被赋值为"third"
echo count($a); // 打印出3,因为该是数组有3个元素
// 用一个语句定义一个数组并赋值
$myphonebook = array (
"sbabu" => "5348",
"keith" => "4829",
"carole" => "4533"
// 噢,忘了教长吧,让我们添加一个元素
$myphonebook["dean"] = "5397";
// 你定义的carale元素错了,让我们更正它
$myphonebook["carole"] => "4522"
// 我还没有告诉你怎样使用数组的相似支持方式吗?让我们看一看
echo "$myphonebook[0]"; // sbabu
echo "$myphonebook[1]"; // 5348
其他一些对数组或哈希表有用的函数包括sort(),next(),prev()和each()。
使用new语句产生一个对象:
class foo
function do_foo ()
echo "Doing foo.";
$bar = new foo;
$bar->do_foo();
改变变量类型
在PHP手册中提到:"PHP不支持(也不需要)直接在声明变量时定义变量类型;变量类型将根据其被应用的情况决定。如果你为变量var赋值为一个字符串,那么它变成了一个字符串。如果你又为它赋了整数值,那么它就变成了整数。"
$foo = "0"; // $foo是字符串(ASCII 48)
$foo++; // $foo是字符串"1" (ASCII 49)
$foo += 1; // $foo现在是整数(2)
$foo = $foo + 1.3; // $foo是一个双精度数(3.3)
$foo = 5 + "10 Little Piggies"; // $foo是一个整数(15)
$foo = 5 + "10 Small Pigs"; // $foo是一个整数(15)
如果想要强行转换变量类型,可以使用与C语言相同的函数settype()。
2.5 变量与常量
可能你已经注意到,变量都有一个美元符号($)的前缀。所有变量都是局部变量,为了使得定义的函数中可以使用外部变量,使用global语句。而你要将该变量的作用范围限制在该函数之内,使用static语句。
$g_var = 1 ; // 全局范围
function test()
global $g_var; // 这样就可以声明全局变量了
更先进一些的是变量的变量表示。请参考PHP手册。这在有时会显得很有用。
PHP内置了许多已定义的变量。你也可以用define函数定义你自己的常量,比如define("CONSTANT","value")。
2.6 运算符
PHP具有C,C++和Java中的通常见到的运算符。这些运算符的优先权也是一致的。赋值同样使用"="。
算术和字符
以下只有一种运算符是有关字符的:
$a + $b :加
$a - $b :减
$a * $b :乘
$a / $b :除
$a % $b :取模(余数)
$a . $b :字符串连接
逻辑和比较
逻辑运算符有:
$a || $b :或
$a or $b :或
$a && $b :与
$a and $b :与
$a xor $b :异或 (当$a或$b为true时为true,两者一样时为false)
! $a :非
比较运算符有:
$a == $b :相等
$a != $b :不等
$a < $b :小于
$a $b :大于
$a >= $b :大于等于
与C一样PHP也有三重运算符(?:)。位操作符在PHP同样存在。
就和C以及Java一样!
2.7 控制流程结构
PHP有着与C一样的流程控制。我将在下面大概介绍。
if, else, elseif, if(): endif
if (表达式一)
. . .
elseif (表达式二)
. . .
. . .
// 或者像Python一样
if (表达式一) :
. . .
. . .
elseif (表达式二) :
. . .
else :
. . .
endif ;
Loops. while, do..while, for
while (表达式)
. . .
. . .
while (表达式);
for (表达式一; 表达式二; 表达式三)
. . .
//或者像Python一样
while (expr) :
. . .
endwhile ;
switch
switch是对多重if-elseif-else结构的最好的替换:
switch ($i)
case 0:
print "i equals 0";
case 1:
print "i equals 1";
case 2:
print "i equals 2";
break, continue
break中断当前的循环控制结构。
continue被用来跳出剩下的当前循环并继续执行下一次循环。
require, include
就像C中的#include预处理一样。你在require中指定的那个文件将替代其在主文件中的位置。在有条件的引用文件时,可以使用include()。这样就使得你可以将复杂的PHP文件分割成多个文件并且在不同需要时分别引用它们。
2.8 函数
你可以像以下的例子一样定义自己的函数。函数的返回值可以是任何数据类型:
function foo (变量名一, 变量名二, . . . , 变量名n)
echo "Example function.\n";
return $retval;
所有PHP代码都可以出现在函数定义中,甚至包括对其他函数和类的定义。函数必须在引用之前定义。
2.9 类
利用类模型建立类。可以参考PHP手册中对类的详细解释。
class Employee
var $empno; // 员工人数
var $empnm; // 员工姓名
function add_employee($in_num, $in_name)
$this->empno = $in_num;
$this->empnm = $in_name;
function show()
echo "$this->empno, $this->empnm";
return;
function changenm($in_name)
$this->empnm = $in_name;
$sbabu = new Employee;
$sbabu->add_employee(10,"sbabu");
$sbabu->changenm("babu");
$sbabu->show();
三、从实例入手
PHP的许多特点与其他软件或者工具有关。利用迄今为止我们所学到的PHP知识,我们可以试着建立一个简单交互的网站。利用这一过程我们又可以学到不少东西。好吧,我们现在开始专注于一个典型个人网站的建设。
3.1 计划一个站点
一般一个个人站点包括一个欢迎页面、一个留言本页面、一个书签链接页面、一个计数器、联系信息,甚至还有照片集和一些音乐文件等等。让我们从一个标题页面、一个联系信息页面和一个简历页面开始。我们同样需要标准的通用的页面头部和底部。
标题页面--front.html
这里我们有一个非常简单的html文件:
我的个人主页--欢迎
3.2 HTML到PHP
从上面你可以看出,每个页面有相同的头部和底部。像上面那样每个页面都写入相同的信息在工作量少的时候还可以,但是想象一下当有100多页面且你需要全部更改其头部或底部时你要花费多大精力?一页一页的手工更改是一件多么冗长无趣的事情啊!所以我们应该为这些页面编写PHP的头部和底部文件,之后我们只要在每个HTML页面中引用它们就行了。我们将把这些include文件放在一个叫include的子目录下。下面我们就把这些站点的通用内容写进文件中。
全站通用变量设定:common.inc
// 全站通用变量
$MyEmail = "phptalk@tnc.org";
$MyEmailLink = "$MyEmail";
$MyName = "PHP Talk";
$MySiteName = $MyName."'s Home Page";
通用页面头部:header.inc
现在你就可以猜出这样安排的好处了。如果你想改动页面的头部或者底部,你只需要改动相应的文件就可以了。如果你要修改你的e-mail地址甚至你的名字,只要修改common.inc文件就行了。另外值得注意的是你可以把具有任何文件名或者文件扩展名的文件包含进你的文件中,你甚至可以包含其他站点上的文件。
3.3 计数器
让我们在首页上加上一个计数器。这个例子已经被讲过多次了,但是还是有利于演示怎样读写文件以及创建自己的函数。counter.inc包含以下代码:
然后我们更改front.php3文件以显示这个计数器:
include("include/counter.inc");
// 我把计数值放在文件counter.txt中,读出并输出
printf ("%06d \n",
get_hitcount("counter.txt"));
include("include/footer.inc");
看看我们的新front.php3
3.4 反馈表单
让我们再添加一个反馈表单以便你的浏览者填写并e-mail给你。举例来说我们用一种很简单的方法实现它,我们只需要两个页面:一个为浏览者提供输入表单;一个获得表单数据并处理、mail给你。
PHP中获取表单数据是很简单的。当一个表单被发送后,表单中所包含的各个元素被赋上了相应的值,而这样就可以像引用一般变量一样使用了。
在process_form.php3中,变量$mytext就被赋予了输入的值--非常简单!同样的,你可以从列表框、多选框、单选框、按钮等表单元素中取得变量值。你唯一要做的就是为表单中的每一个元素取名以便将来可以引用。
根据这个方法,我们可以生成一个简单的包含三个元素的表单:姓名、e-mail地址和留言。当浏览者发送表单后,处理该表单的PHP页面(sendfdbk.php3)读取数据,检查姓名是否为空,最后将数据mail给你。
表单:form.php3
// 这个浏览者真是不想透露姓名啊!
echo "Hello ? Your name is supposed to be replaced with
your actual name!";
// 输出一段礼貌的感谢语
echo "
Hello, $name.
Thank you for your feedback. It is greatly appreciated.
Thanking you
$MyName
$MyEmailLink
// 最后mail出去
mail($MyEmail, "Feedback.","
Name : $name
E-mail : $email
Comment : $comment
include("include/footer.inc");
3.5 简单的站内搜索引擎
PHP可以调用外部程序。在Unix环境下我们可以利用程序grep实现一个简单的搜索引擎。我们可以做的稍微复杂一些:使用一个页面既输出一个表单供用户输入搜索字串又输出查询结果。
<FORM ACTION="" METHOD="POST">
<INPUT TYPE="text" NAME="searchstr" value=""
SIZE="20" MAXLENGTH="30">
if ( ! empty($searchstr) )
// empty()用来检查查询字串是否为空
// 如果不为空,调用grep查询
echo "\n";
// 调用grep对所有文件进行大小写非敏感模式的查询
$cmdstr = "grep -i $searchstr *";
$fp = popen( $cmdstr, "r" ); // 执行命令并输出管道
$myresult = array(); // 存储查询结果
while( $buffer = fgetss ($fp, 4096))
// grep返回这样格式: 文件名:匹配字串出现行数
// 因此我们利用函数split()分离处理数据
list($fname, $fline) = split(":",$buffer, 2);
// 我们只输出第一次匹配的结果
if ( !defined($myresult[$fname]))
$myresult[$fname] = $fline;
// 现在我们将结果存储在数组中,下面就可以处理并输出了
if ( count($myresult) )
echo "\n";
while(list($fname,$fline) = each($myresult))
echo "
$fname : $fline \n";
echo "\n";
// 如果没有查询结果
echo "Sorry. Search on $searchstr
returned no results.\n";
pclose($fp);
PHP_SELF是PHP内建的变量。包含当前文件名。
fgets()按行读取文件,最多4096(指定)字符长度。
fgetss()与fgets()相似,只是解析输出的HTML标记。
split()有一个参数是2,因为我们只需要把输出分成两部分。另外需要省略":"。
each()是一个数组操作函数,用来更方便的遍历整个数组。
popen()、pclose()与fopen()、fclose()的功能很相似,只是增加了管道处理。
请注意以上的代码并不是实现一个搜索引擎的好办法。这只是有助于我们更好学习PHP而举出的一个例子而已。理想的情况是你应该建立一个包含关键字的数据库然后进行搜索
四、与数据库链接
通过PHP你可以轻松的连接到数据库,请求数据并将其显示在你的web站点中,甚至修改数据库中的数据。MySQL是一种很流行的数据库,并且在互联网中有许多有关PHP与MySQL的教程。MySQL是免费的,这一点也许就吸引了不少人。由于其广泛应用,我就不想在这里赘述MySQL的使用方法了。Oracle被大量在企业应用中采用,因此我们就利用Oracle来介绍PHP与数据库的连接。我们当然不会提及Oracle数据库的设计原理,原因是这已经超出了我们的讨论范围。
PHP提供了两套函数与Oracle连接,分别是ORA_和OCI函数。其中ORA_函数略显陈旧。OCI函数更新据说更好一些。两者的使用语法几乎相差无几。如前所述,你的PHP安装选项应该可以支持两者的使用。
想获得更多有关在Microsoft Windows平台上安装支持PHP3的Apache服务器的知识以及更多有关Oracle数据库的知识,请查阅以下URL:www.csoft.net/~vsbabu/articles/oraphp.html。
4.1 连接
if ($conn=Ora_Logon("user@TNSNAME","password"))
echo "SUCCESS ! Connected to database\n";
echo "Failed :-( Could not connect to database\n";
Ora_Logoff($conn);
phpinfo();
以上代码使用TNSNAME(在你的tnsnames.ora文件中指明)定义的Oracle数据库名称、用户名称和密码连接数据库。在成功连接的基础上,ora_logon函数返回一个非零的连接ID并储存在变量$conn中。
4.2 查询
假设与数据库已经连接就绪,下面我们就来实际的应用对数据库的查询。下面的代码演示了一个连接并查询的典型例子:
* 连接数据库并执行查询
function printoraerr($in_cur)
// 检查Oracle是否出错
// 如果存在错误则显示
// 当指针被激活时每次请求Oracle后调用该函数
if(ora_errorcode($in_cur))
echo "Oracle code - ".ora_error($in_cur)."\n";
return;
/** 主程序 */
if (!($conn=ora_logon("user@TNSNAME","password")))
echo "Connection to database failed\n";
exit;
echo "Connected as connection - $conn\n";
echo "Opening cursor ...\n";
$cursor=ora_open($conn); printoraerr($cursor);
echo "Opened cursor - $cursor\n";
$qry="select user,sysdate from dual";
echo "Parsing the query $qry ...\n";
ora_parse($cursor,$qry,0); printoraerr($cursor);
echo "Query parsed \n";
echo "Executing cursor ...\n";
ora_exec($cursor); printoraerr($cursor);
echo "Executed cursor\n";
echo "Fetching cursor ...\n";
while(ora_fetch($cursor))
$user=ora_getcolumn($cursor,0); printoraerr($cursor);
$sysdate=ora_getcolumn($cursor,1); printoraerr($cursor);
echo " row = $user, $sysdate \n";
echo "Fetched all records\n";
echo "Closing cursor ...\n";
ora_close($cursor);
echo "Closed cursor\n";
echo "Logging off from oracle... \n";
ora_logoff($conn);
echo "Logged off from oracle \n";
(译者注:以上代码段缺少注释,请读者参考PHP Manual的Oracle数据库函数部分)
4.3 显示结果
以下代码演示了怎样查询数据库并将结果输出:
function printoraerr($in_cur, $conn)
// 检查Oracle是否出错
// 如果存在错误则显示
// 当指针被激活时每次请求Oracle后调用该函数
// If it encountered an error, we exit immediately
if(ora_errorcode($in_cur))
echo "Oracle code - ".ora_error($in_cur)."n";
ora_logoff($conn);
exit;
return;
function exequery($w_qry,$conn)
$cursor=ora_open($conn); printoraerr($cursor,$conn);
ora_parse($cursor,$w_qry,0); printoraerr($cursor,$conn);
ora_exec($cursor); printoraerr($cursor,$conn);
$numrows=0;
$w_numcols=ora_numcols($cursor);
// 显示头部
echo "
for ($i=0;$i<$w_numcols;$i++)
$align=(ora_columntype($cursor,$i)=="NUMBER")?"RIGHT":"LEFT";
echo "\t".ora_columnname($cursor,$i)."\n";
echo "\n";
while(ora_fetch($cursor))
echo "\n";
for ($i=0;$i<$w_numcols;$i++)
$align=(ora_columntype($cursor,$i)=="NUMBER")?"RIGHT":"LEFT";
if(ora_columntype($cursor,$i)=="LONG")
echo "".
ora_getcolumn($cursor,$i)."\n";
echo "".ora_getcolumn($cursor,$i)."\n";
printoraerr($cursor,$conn);
$numrows++;
echo "\n";
if ($numrows==0)
echo "Query returned no records
echo "\n";
echo "Count\n";
echo "$numrows\n";
echo "\n";
echo "\n";
ora_close($cursor);
return;
// 主程序
if(!($conn=ora_logon("user@SID","password")))
echo "Error: Cannot connect to database\n";
exit;
$qry="SELECT
deptno \"Dept\"
,empno \"Emp\"
,empnm \"Name\"
,salary \"Salary\"
employee
ORDER BY 1,2";
exequery($qry);
ora_logoff($conn);
(译者注:以上代码段缺少注释,请读者参考PHP Manual的Oracle数据库函数部分)
4.4 基于HTTP的Oracle登录
将以下代码加在PHP页面代码之前以确认Oracle登录。注意你必须正确设定$ SID。
if(!isset($PHP_AUTH_USER))
Header("WWW-authenticate: basic realm=\"$SID\"");
Header("HTTP/1.0 401 Unauthorized");
$title="Login Instructions";
echo "
You are not authorized to enter the site
exit;
if (!($conn=ora_logon("$PHP_AUTH_USER@$SID",$PHP_AUTH_PW)))
Header("WWW-authenticate: basic realm=\"$SID\"");
Header("HTTP/1.0 401 Unauthorized");
$title="Login Instructions";
echo "
You are not authorised to enter the site
exit;
五、其它功能
5.1 生成图像
PHP可以操作处理图像。如果你已经安装了GD库,你甚至可以利用PHP生成图像。
(译者注:以上代码段缺少注释,请读者参考PHP Manual的图像处理函数部分)
这段代码在其他页面中通过以下标记调用,然后以上的那段button.php3代码取得text值并在另外取得的图像文件中加上该值--在以上的代码中该图像文件是images/button1.gif--最后输出到浏览器。假如你想在表单域中使用图像按钮,但是又不希望在每次按钮上的文字改变后不得不重新生成新的图像,就可以利用这样简单的方法动态生成图像文件。
5.2 Cookies
PHP支持基于HTTP的cookies。在需要时你可以像使用一般变量一样方便的使用cookie。Cookies是浏览器保存于客户端的一些信息片段,由此你可以知道是否一台特定PC上的任何人都访问过你的站点,浏览者者在你的站点上的踪迹等等。使用cookies的典型例子就是对浏览者偏好的甄别。Cookies由函数setcookie()设定。与输出HTTP标头的函数header()一样,setcookie()必须在任何实际内容杯输出到浏览器之前调用。以下是一个简单例子:
if (empty($VisitedBefore))
// 如果没有设定cookie,为cookie赋上当前时间值
// 函数中的最后一个参数声明了该cookie保存的时间
// 在这个例子中是1年
// time()函数返回自1970年1月1日以来的以秒数计的时间
SetCookie("VisitedBefore",time(), time()+(60*60*24*365));
// 欢迎浏览者再次光临
echo "Hello there, welcome back";
// 读取cookie并判断
if ( (time() - $VisitedBefore) >= "(60*60*24*7)" )
echo "Why did you take a week to come back. You should be here more often!? ";
5.3 基于HTTP验证
基于HTTP验证当PHP以CGI模式运行时不能实现。我们可以使用函数header()发送HTTP标头强制验证,客户端浏览器则弹出供输入用户名和密码的对话框。这两个变量被储存在$PHP_AUTH_USER和$PHP_AUTH_PW中,你可以使用这两个变量验证合法并允许进入。以下的例子通过用户名称/密码对为tnc/nature的验证一名用户的登录:
事实上再实际引用中不大可能如上面使用代码段明显的用户名称/密码对,而是利用数据库或者加密的密码文件存取它们。
5.4 文件上传
你可以利用PHP实现文件的功能,注意客户端的浏览器应该是Netscape3以上或者IE3以上。以下就是该功能的简单演示:
( upload.html ):
Upload Your File
$upload_dir = "/local/uploads";
$local_file = "$upload_dir/$the_time";
if ( file_exists ( '$local_file' ) )
$seq = 1;
while ( file_exists ( "$upload_dir/$the_time$seq" ) ) { $seq++; }
$local_file = "$upload_dir/$the_time$seq";
rename ( $uploadfile, $local_file );
display_page ();
function display_page ()
// 这里是你的页面内容
php3 Receiving Script
if ( $error_msg ) { echo "$error_msg"; }
if ( $sendit )
do_upload ();
elseif ( $cancelit )
header ( "Location: $some_other_script" );
exit;
some_other_func ();
5.5 常用函数
我们简单来看看一些常用的函数。
array - 生成数组
count - 数组元素个数
sort - 数组排序,另有其他几种排序函数可供使用
list - 列出数组元素
each - 返回下一个key/value对
current - 返回当前数组元素
next,prev - 传回当前数组元素前后指针
日期和时间
checkdate - 验证日期/时间格式
date - 生成日期/时间格式
time - 当前时间信息
strftime - 格式化日期/时间
目录、文件系统
chdir - 改变目录
dir - 目录类别
opendir, readdir, closedir - 开启、读取、关闭目录
fopen, fclose - 开启、关闭文件
fgets, fgetss - 逐行读取内容
file - 将整个文件读入一个数组变量中
正则表达式
ereg - 匹配正则表达式
eregi - 大小写非敏感匹配正则表达式
ereg_replace -匹配正则表达式并替换
eregi_replace -大小写非敏感匹配正则表达式并替换
split - 依规则切开字符串并以数组形势存储
AddSlashes - 加上斜杠后使用字符串
echo - 输出一个或多个字符串
join, implode - 将数组元素合并为字符串
htmlentities, htmlspecialchars - 将HTML特殊字符转换为HTML标记形式
split - 依规则切开字符串并以数组形势存储
5.6 扩展我们的范例主页
我们将使用以上提到的一些函数和思想为我们的范例主页添加更多的动态内容。我们可以在每个页面的顶部加上导航栏,同时使得当前页自动的不被链接显示;同时还可以添加一个用户验证表单以便上传音乐、图像等文件并自动更新页面。
实际上就是在footer.inc文件中加上一段代码。假设你的web站点中所有后缀为.php3的文件都会出现在导航栏中,以下就是被存为include/navbar.inc的代码:
/* 输出该导航栏,链接所有除当前页的站内.php3文件 */
# 读取目录
$d = dir("./");
echo " | \n";
while($entry = $d->read())
// 忽略无文件情况
if ( !is_file($entry) )
continue;
/* 将文件名与扩展名分开。由于.是正则表达式特殊字符,应该用\引出 */
list($filenm, $fileext) = split("\.",$entry, 2);
// 忽略非.php3文件情况
if( $fileext != "php3" )
continue;
/* 现在我们已经把.php3文件都选出,下面搜寻文件中的第一行(标题)
类似$title="something";
并将以上标题内容分开,用作链接文字 */
$linknm = "";
$fp=fopen($entry,"r");
while($buffer=fgets($fp, 4096))
$buffer = trim($buffer);
// 我们已经把每个文件的标题放在文件的第一行以便搜索
// 但是当你改变变量名称时可能会带来大麻烦
if (ereg("title *= *\"", $buffer))
/* 我们已经取得了标题内容并可以在此基础上
进行去除空格等处理。
必须以PHP代码方式处理,比如$title = "blah blah" */
eval($buffer);
// 然后将链接文字显示为标题文字
$linknm = $title;
break;
fclose($fp);
if ( $entry == basename($PHP_SELF) )
echo "$linknm";
echo "$linknm";
echo " | ";
$d->close();
echo " \n";
照片收藏夹
我们将引用基于HTTP的验证、文件系统函数和文件上传功能维护放置图像文件的目录。
同时我们需要建立一个可以列出在该目录下所有照片的页面。
include("include/common.inc");
// 我们在这里再做一次用户验证
if(!isset($PHP_AUTH_USER))
Header("WWW-Authenticate: Basic realm=\"$MySiteName\"");
Header("HTTP/1.0 401 Unauthorized");
echo "Sorry, you are not authorized to upload files\n";
exit;
if ( !($PHP_AUTH_USER==$MyName && $PHP_AUTH_PW==$MyPassword ) )
// 如果是错误的用户名称/密码对,强制再次认证
Header("WWW-Authenticate: Basic realm=\"My Realm\"");
Header("HTTP/1.0 401 Unauthorized");
echo "ERROR : $PHP_AUTH_USER/$PHP_AUTH_PW is invalid.";
exit;
if ( $cancelit )
// 当浏览者按下"取消"按钮则转向首页面
header ( "Location: front_2.php3" );
exit;
function do_upload () {
global $userfile, $userfile_size, $userfile_name, $userfile_type;
global $local_file, $error_msg;
global $HTTP_REFERER;
if ( $userfile == "none" ) {
$error_msg = "You did not specify a file for uploading.";
return;
if ( $userfile_size > 2000000 )
$error_msg = "Sorry, your file is too large.";
return;
// Wherever you have write permission below...
$upload_dir = "photos";
$local_file = "$upload_dir/$userfile_name";
if ( file_exists ( $local_file ) ) {
$error_msg = "Sorry, a file with that name already exists";
return;
// 你还可以由此检查文件名称/类型对以确定是何种文件:gif,jpg,mp3…
rename($userfile, $local_file);
echo "The file is uploaded\n";
echo "Go Back\n";
$title = "Upload File";
include("include/header.inc");
if (empty($userfile) || $userfile=="none")
// 输出以下表单
<FORM ACTION="" ENCTYPE="multipart/form-data" METHOD=POST>
Here are some of our family photos. This PHP script can really
be made better, by splitting into multiple pages.
read())
if (is_file("photos/$entry"))
echo "\n";
$d->close();
另外,你可以在文件上传的表单中加上一个输入元素去描述该上传的文件。这个元素将被存储在文件中,然后被以上的照片图库的那段代码所读出并显示出来。
六、网络资源
你可以通过web上的众多资源更多的了解PHP3。许多邮件列表和书籍对你都非常有用。
6.1 站点
PHP的爆炸性流行使得一夜之间出现了很多基于PHP的站点,其中不少站点有在线教程、范例代码、技巧和提示等内容。
http://www.phpuser.com - PHP中文用户,也就是这里了
http://www.phpx.com - 中国PHP联盟
http://www.phpsite.net - PHP专门站
http://www.phpchina.com - PHP CHINA
http://www.cpcw.com/netschool/homepage/cgi/ - 电脑报网页陶吧
http://www.php.net/ - PHP官方站点
http://www.devshed.com/ - 极好的教程
http://px.sklar.com - 代码交换
http://www.phpbuilder.com/ - 教程、专栏和邮件列表档案
http://www.weberdev.com/ - 文章和代码
http://www.phpwizard.net/ - 提示与技巧
http://www.iometrics.com/php/phplist.php3/ - IOMetrics scripts的档案
http://www.e-gineer.com/phpkb/ - PHP知识库
6.2 邮件列表
你可以在PHP官方站点的"支持"栏目内登记获得以下的邮件列表。值得注意的是这些都是高流量流表,一般每天会有100份e-mail。
php3@lists.php.net - 主要的列表
php-dev@lists.php.net - 主要针对开发者
php-list@exp.com.cn - 本站的邮件列表,与论坛相通
6.3 引人注目的工程
一些基于PHP的工程已经发展得比较完善。其中一些更出色更引人注目的是:
http:// phplib.netuse.de - PHPLib,一整套PHP函数库
http://www.phorum.org - Phorum是一个很完善的BBS系统
http://www.fishcartsql.org - FishCartSQL是一个电子商务解决方案
http://www.midgard-project.org - Midgard是一个网络应用开发平台
AspJpeg 1、AspJpeg是一款功能强大的基于Microsoft IIS环境的图片处理组件,网络上对其进行详细和深入介绍的中文文章并不多,即使有一般也只是牵涉到图片缩略图和图片水印,这与其为英文版本有着密切的关系。
AspJpeg可以使用很少的代码在您的ASP/ASP.Net应用程序上动态的创建高质量的缩略图象,支持的图象格式有:JPEG, GIF, BMP, TIFF, PNG。
AspJpeg主要可以做到:生成缩略图片、生成水印图片、图片合并、图片切割、数据库支持、安全码技术
ASPJPEG是一款功能相当强大的图象处理组件,用它可以轻松地做出图片的缩略图和为图片加上水印功能。
安装SN:09268-26217-40710
2、AspJpeg功能摘要
支持JPEG, GIF, BMP, TIFF 和 PNG 格式图片. 输出格式始终为 JPEG
源图片可以来源于磁盘、内存、或者记录集(数据库)
缩略图片可以保存到磁盘、内存、或者HTTP流
支持三种更改大小方式: nearest-neighbor, bilinear, and bicubic.
可以在图片之上添加图片或者文字.
支持画中画
支持复制,反转,旋转,锐化,灰度调节.
可以调节压缩比率,以得到最佳输出效果和大小.
从Jpeg图片中抽取EXIF 和 IPTC数据.
CMYK-RGB转换
Read/write access to individual pixels of an image. (从图象中对任意象素进行读/写存取。)
3、AspJpeg系统需求
Windows 95/98/NT/2000/XP/2003, and
IIS 4.0+ and ASP/ASP.NET, or
Visual Basic 5.0+, or
Visual C++ 5.0+, or
any development environment supporting COM.
4、AspJpeg安装
全新安装:
在AspJpeg安装过程中输入序列号即可,如果安装位置磁盘格式为NTFS,则可能出现访问权限问题,需手工设置安装目录对Everyone有访问权限。
更新安装:
如果之前有装过其它版本的AspJpeg组件,则需要先卸载原来的组件,再进行新版本的安装。
先停止IIS
Net Stop iisadmin /y
卸载旧版组件
regsvr32 /u Path/aspjpeg.dl(Path为安装路径)
重启IIS
Net Start w3svc
然后再进行全新安装或复制AspJpeg.dll文件到安装目录进行手工安装:
regsvr32 Path/aspjpeg.dll(Path为安装路径)
如果在正常安装过程中没有输入序列号或手工安装则必须在注册表中加入以下项,为方便起见您可以直接将以下代码保存为.reg文档并导入注册表:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Persits Software\AspUpload3\RegKey]
@="21764-40765-60456"
5、如何创建一个AspJpeg实例?
Set Jpeg = Server.CreateObject("Persits.Jpeg")
6、如何查看到期时间(是否注册成功)?
Set Jpeg = Server.CreateObject("Persits.Jpeg")
Response.Write Jpeg.Expires
注册成功则到期时间为:9999-9-9
否则为:安装日期加1个月期限
7、如何用AspJpeg组件生成图片缩略图?
Set Jpeg = Server.CreateObject("Persits.Jpeg") '创建实例
Path = Server.MapPath("../images/apple.jpg") '处理图片路径
Jpeg.Open Path '打开图片
'调整宽度和高度为原来的50%
Jpeg.Width = Jpeg.OriginalWidth / 2
Jpeg.Height = Jpeg.OriginalHeight / 2
Jpeg.Save Server.MapPath("apple_small.jpg") '保存图片到磁盘
Jpeg.Close:Set Jpeg = Nothing
8、如何用AspJpeg组件生成图片水印?
Set Jpeg = Server.CreateObject("Persits.Jpeg")
Jpeg.Open Server.MapPath("images/dodge_viper.jpg")
开始写文字
Jpeg.Canvas.Font.Color = &000000'' red 颜色
Jpeg.Canvas.Font.Family = "Courier New" 字体
Jpeg.Canvas.Font.Bold = True 是否加粗
打印坐标x 打印坐标y 需要打印的字符
以下是对图片进行边框处理
Jpeg.Canvas.Pen.Color = &H000000'' black 颜色
Jpeg.Canvas.Pen.Width = 2 画笔宽度
Jpeg.Canvas.Brush.Solid = False 是否加粗处理
Jpeg.Canvas.Bar left, top, right, bottom ' 左,上,右,下
Jpeg.Save Server.MapPath("images/dodge_viper_framed.jpg") 保存
9、如何用AspJpeg组件进行图片合并?
AspJpeg 1.3+ enables you to place images on top of each other via the method DrawImage. To use this method, you must create two instances of the AspJpeg objects and populate both of them with images via calls to Open (or OpenBinary). When calling Canvas.DrawImage, the 2nd instance of AspJpeg is passed as an argument to this method, along with the X and Y offsets (in pixels):
使用该方法,您必需创建两个AspJpeg实例对象
Set Jpeg1 = Server.CreateObject("Persits.Jpeg")
Set Jpeg2 = Server.CreateObject("Persits.Jpeg")
Jpeg1.Open Server.MapPath("t.jpg")
Jpeg2.Open Server.MapPath("t1.jpg")
Jpeg1.Canvas.DrawImage 10, 10, Jpeg2 ' optional arguments omitted
jpeg1.save Server.mappath("tt.jpg")
10、如何用AspJpeg组件进行图片切割?
AspJpeg 1.1+ is also capable of cutting off edges from, or cropping, the resultant thumbnails via the method Crop(x0, y0, x1, y1). The size of the cropped image is specified by the coordinates of the upper-left and lower-right corners within the resultant thumbnail, not the original large image.
Set Jpeg = Server.CreateObject("Persits.Jpeg")
Jpeg.Open Server.MapPath("t.jpg")
jpeg.Crop 20, 30, jpeg.Width - 20, jpeg.Height - 10
jpeg.save Server.mappath("tt.jpg")
Response.write("<img src=tt.jpg>")
11、如何用AspJpeg组件创建安全码?
创建安全码原理上和创建水印差不多。
function make_randomize(max_len,w_n) 'max_len 生成长度,w_n:0 可能包含字母,1:只为数字
randomize
for intcounter=1 to max_len
whatnext=int((1-0+1)*rnd+w_n)
if whatnext=0 then
upper=122
lower=97
upper=57
lower=48
end if
strnewpass=strnewpass & chr(int((upper-lower+1)*rnd)+lower)
make_randomize=strnewpass
end function
'生成安全码的图片。
random_num=make_randomize(4,1) ''生成4位数字的安全码
session("random_num")=random_num '为么调用session,没有session的安全码是完全没有意义的。呵呵 .
Set Jpeg = Server.CreateObject("Persits.Jpeg") '调用组件
Jpeg.Open Server.MapPath("t.jpg") '打开准备的图片
Jpeg.Canvas.Font.Color = &HFFFFFF
Jpeg.Canvas.Font.Family = "Arial Black"
Jpeg.Canvas.Font.Bold = false
Jpeg.Canvas.PrintText 0, -2, random_num
jpeg.save Server.MapPath("tt.jpg") '保存
<img src="tt.jpg" border="0" align="absmiddle">
12、如何让AspJpeg组件支援数据库?
图片存进数据库只能以二进制数据保存,这里即利用AspJpeg的Binary方法,下面以两个AspJpeg用户手册上的代码为例,具体请参考AspJpeg用户手册:
Opening Images from Memory
<% ' Using ADO, open database with an image blob
strConnect = "DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" & Server.MapPath("../db/aspjpeg.mdb")
Set rs = Server.CreateObject("adodb.recordset")
SQL = "select image_blob from images2 where id = " & Request("id")
rs.Open SQL, strConnect, 1, 3
Set Jpeg = Server.CreateObject("Persits.Jpeg")
' Open image directly from recordset
Jpeg.OpenBinary rs("image_blob").Value
' Resize
jpeg.Width = Request("Width")
' Set new height, preserve original aspect ratio
jpeg.Height = jpeg.OriginalHeight * jpeg.Width / jpeg.OriginalWidth
Jpeg.SendBinary
rs.Close
Output to Memory
Set rs = Server.CreateObject("adodb.recordset")
rs.Open "images", strConnect, 1, 3
rs.AddNew
rs("image_blob").Value = Jpeg.Binary
rs.Update
更多方法介绍:
Canvas.Line(Left, Top, Right, Bottom)
画一条直线
Canvas.Ellipse(Left, Top, Right, Bottom)
画出一个椭圆
Canvas.Circle(X, Y, Radius)
画出一个圆
Canvas.Bar(Left, Top, Right, Bottom)
画出一个长方形,上面有代码介绍了
Canvas.Font.ShadowColor
文字阴影颜色
Canvas.Font.ShadowXOffset As Long
阴影X坐标设定
Canvas.Font.ShadowYOffset As Long
Y坐标设定
Canvas.Font.BkMode As String
std::ifstream ifs("D:\\urexdump.dat",std::ios::binary|std::ios::in);
std::string content( (std::istreambuf_iterator(ifs) ),
(std::i
C语言程序的构成
与C++、Java相比,C语言其实很简单,但却非常重要。因为它是C++、Java的基础。不把C语言基础打扎实,很难成为程序员高手。
一、C语言的结构
先通过一个简单的例子,把C语言的基础打牢。
/* clang01_1.c */
#include <stdio.h>
int main(void)
printf("这是劝学网的C语言教...
二进制文件不是以ASCII代码存放数据的,它将内存中数据存储形式不加转换地传送到磁盘文件,因此它又称为内存数据的映像文件。因为文件中的信息不是字符数据,而是字节中的二进制形式的信息,因此它又称为字节文件。
对二进制文件的操作也需要先打开文件,用完后要关闭文件。在打开时要用ios::binary指定为以二进制形式传送和存储。二进制文件除了可以作为输入文件或输出文件外,还可以是既能输入又能输出的
1-2 面向对象的编程语言有哪些特点?
面向对象的编程语言与以往各种编程语言有根本的不同,它设计的出发点就是为了能更直接的描述客观世界中存在的事物以及它们之间的关系。面向对象的编程语言将客观事物看作具有属性和行为的对象,通过抽象找出同一类对象的共同属性(静态特征)和行为(动态特征),形成类。通过类的继承与多态可以很方便地实现代码重用,大大缩短了软件开发周期,并使得软件风格统一。因此,面向对象的编程语言使程序能够比较直接地反问题域的本来面目,软件开发人员能够利用人类认识事物所采用的一般思维方法来进行软件开发。C++语言是目前应用最广的面向对象的编程语言。
1-3 什么是结构化程序设计方法?这种方法有哪些优点和缺点?
结构化程序设计的思路是:自顶向下、逐步求精;其程序结构是按功能划分为若干个基本模块;各模块之间的关系尽可能简单,在功能上相对独立;每一模块内部均是由顺序、选择和循环三种基本结构组成;其模块化实现的具体方法是使用子程序。结构化程序设计由于采用了模块分解与功能抽象,自顶向下、分而治之的方法,从而有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和维护。
虽然结构化程序设计方法具有很多的优点,但它仍是一种面向过程的程序设计方法,它把数据和处理数据的过程分离为相互独立的实体。当数据结构改变时,所有相关的处理过程都要进行相应的修改,每一种相对于老问题的新方法都要带来额外的开销,程序的可重用性差。
由于图形用户界面的应用,程序运行由顺序运行演变为事件驱动,使得软件使用起来越来越方便,但开发起来却越来越困难,对这种软件的功能很难用过程来描述和实现,使用面向过程的方法来开发和维护都将非常困难。
1-4 什么是对象?什么是面向对象方法?这种方法有哪些特点?
从一般意义上讲,对象是现实世界中一个实际存在的事物,它可以是有形的,也可以是无形的。对象是构成世界的一个独立单位,它具有自己的静态特征和动态特征。面向对象方法中的对象,是系统中用来描述客观事物的一个实体,它是用来构成系统的一个基本单位,由一组属性和一组行为构成。
面向对象的方法将数据及对数据的操作方法放在一起,作为一个相互依存、不可分离的整体--对象。对同类型对象抽象出其共性,形成类。类中的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口,与外界发生关系,对象与对象之间通过消息进行通讯。这样,程序模块间的关系更为简单,程序模块的独立性、数据的安全性就有了良好的保障。通过实现继承与多态性,还可以大大提高程序的可重用性,使得软件的开发和维护都更为方便。
面向对象方法所强调的基本原则,就是直接面对客观存在的事物来进行软件开发,将人们在日常生活中习惯的思维方式和表达方式应用在软件开发中,使软件开发从过分专业化的方法、规则和技巧中回到客观世界,回到人们通常的思维。
1-5 什么叫做封装?
封装是面向对象方法的一个重要原则,就是把对象的属性和服务结合成一个独立的系统单位,并尽可能隐蔽对象的内部细节。
1-6 面向对象的软件工程包括哪些主要内容?
面向对象的软件工程是面向对象方法在软件工程领域的全面应用,它包括面向对象的分析(OOA)、面向对象的设计(OOD)、面向对象的编程(OOP)、面向对象的测试(OOT)和面向对象的软件维护(OOSM)等主要内容。
1-7 简述计算机内部的信息可分为几类?
计算机内部的信息可以分成控制信息和数据信息二大类;控制信息可分为指令和控制字两类;数据信息可分为数值信息和非数值信息两类。
1-8 什么叫二进制?使用二进制有何优点和缺点?
二进制是基数为2,每位的权是以2 为底的幂的进制,遵循逢二进一原则,基本符号为0和1。采用二进制码表示信息,有如下几个优点:1.易于物理实现;2.二进制数运算简单;3.机器可靠性高;4.通用性强。其缺点是它表示数的容量较小,表示同一个数,二进制较其他进制需要更多的位数。
1-9 请将以下十进制数值转换为二进制和十六进制补码:
(1)2 (2)9 (3)93
(4)-32 (5)65535 (6)-1
(1) (2)10 = (10)2 = (2)16
(2) (9)10 = (1001)2 = (9)16
(3) (93)10 = (1011101)2 = (5D)16
(4) (-32)10 = (11100000)2 = (E0)16
(5) (65535)10 = (11111111 11111111)2 = (FFFF)16
(6) (-1)10 = (11111111 11111111)2 = (FFFF)16
1-10 请将以下数值转换为十进制:
(1)(1010)2 (2)(10001111)2 (3)(01011111 11000011)2
(4)(7F)16 (5)(2D3E)16 (6)(F10E)16
(1)(1010)2 = (10)10
(2)(10001111)2 = (143)10
(3)(01011111 11000011)2 = (24515)10
(4)(7F)16 = (127)10
(5)(2D3E)16 = (11582)10
(6)(F10E)16 = (61710)10
1-11 简要比较原码、反码、补码等几种编码方法。
原码:将符号位数字化为 0 或 1,数的绝对值与符号一起编码,即所谓"符号──绝对值表示"的编码。
正数的反码和补码与原码表示相同。
负数的反码与原码有如下关系:
符号位相同(仍用1表示),其余各位取反(0变1,1变0)。
补码由该数反码的最末位加1求得。
第 二 章 C++简单程序设计
2-1 C++语言有那些主要特点和优点?
C++语言的主要特点表现在两个方面,一是全面兼容C,二是支持面向对象的方法。C++是一个更好的C,它保持了C的简洁、高效、接近汇编语言、具有良好的可读性和可移植性等特点,对C的类型系统进行了改革和扩充,因此C++比C更安全,C++的编译系统能检查出更多的类型错误。 C++语言最重要的特点是支持面向对象。
2-2 下列标识符哪些是合法的?
Program, -page, _lock, test2, 3in1, @mail, A_B_C_D
Program, _lock, test2, A_B_C_D是合法的标识符,其它的不是。
2-3 例2.1中每条语句的作用是什么?
#include
void main(void)
cout<<"Hello!\n";
cout<<"Welcome to c++!\n";
#include //指示编译器将文件iostream.h中的代码
//嵌入到该程序中该指令所在的地方
void main() //主函数名,void 表示函数没有返回值
{ //函数体标志
cout<<"Hello!\n"; //输出字符串Hello!到标准输出设备(显示器)上。
cout<<"Welcome to c++!\n"; //输出字符串Welcome to c++!
在屏幕输出如下:
Hello!
Welcome to c++!
2-4 使用关键字const而不是#define语句的好处有哪些?
const定义的常量是有类型的,所以在使用它们时编译器可以查错;而且,这些变量在调试时仍然是可见的。
2-5 请写出C++语句声明一个常量PI,值为3.1416;再声明一个浮点型变量a,把PI的值赋给a。
const float PI = 3.1416;
float a = PI;
2-6 在下面的枚举类型中,Blue的值是多少?
enum COLOR { WHITE, BLACK = 100, RED, BLUE, GREEN = 300 };
Blue = 102
2-7 注释有什么作用?C++中有哪几种注释的方法?他们之间有什么区别?
注释在程序中的作用是对程序进行注解和说明,以便于阅读。编译系统在对源程序进行编译时不理会注释部分,因此注释对于程序的功能实现不起任何作用。而且由于编译时忽略注释部分,所以注释内容不会增加最终产生的可执行程序的大小。适当地使用注释,能够提高程序的可读性。在C++中,有两种给出注释的方法:一种是延用C语言方法,使用"/*"和"*/"括起注释文字。另一种方法是使用"//",从"//"开始,直到它所在行的行尾,所有字符都被作为注释处理。
2-8 什么叫做表达式?x = 5 + 7是一个表达式吗?它的值是多少?
任何一个用于计算值的公式都可称为表达式。x = 5 + 7是一个表达式,它的值为12。
2-9 下列表达式的值是多少?
1. 201 / 4
2. 201 % 4
3. 201 / 4.0
1. 50
3. 50.25
2-10 执行完下列语句后,a、b、c三个变量的值为多少?
a = 30;
b = a++;
c = ++a;
a:32 ; b:30 ; c:32;
2-11 在一个for循环中,可以初始化多个变量吗?如何实现?
在for循环设置条件的第一个";"前,用,分隔不同的赋值表达式。
for (x = 0, y = 10; x < 100; x++, y++)
2-12 执行完下列语句后,n的值为多少?
int n;
for (n = 0; n < 100; n++)
n的值为100
2-13 写一条for语句,计数条件为n从100到200,步长为2;然后用while和do…while语句完成同样的循环。
for循环:
for (int n = 100; n <= 200; n += 2);
while循环:
int x = 100;
while (n <= 200)
n += 2;
do…while循环:
int n = 100;
n += 2;
} while(n y)
x = y;
else // y > x || y == x
y = x;
2-17 修改下面这个程序中的错误,改正后它的运行结果是什么?
#include
void main()
int i
int j;
i = 10; /* 给i赋值
j = 20; /* 给j赋值 */
cout << "i + j = << i + j; /* 输出结果 */
return 0;
#include
int main()
int i;
int j;
i = 10; // 给i赋值
j = 20; /* 给j赋值 */
cout << "i + j = " << i + j; /* 输出结果 */
return 0;
程序运行输出:
i + j = 30
2-18 编写一个程序,运行时提示输入一个数字,再把这个数字显示出来。
#include
int main()
int i;
cout <> i;
cout << "您输入一个数字是" << i << endl;
return 0;
程序运行输出:
请输入一个数字:5
您输入一个数字是5
2-19 C++有哪几种数据类型?简述其值域。编程显示你使用的计算机中的各种数据类型的字节数。
#include
int main()
cout << "The size of an int is:\t\t" << sizeof(int) << " bytes.\n";
cout << "The size of a short int is:\t" << sizeof(short) << " bytes.\n";
cout << "The size of a long int is:\t" << sizeof(long) << " bytes.\n";
cout << "The size of a char is:\t\t" << sizeof(char) << " bytes.\n";
cout << "The size of a float is:\t\t" << sizeof(float) << " bytes.\n";
cout << "The size of a double is:\t" << sizeof(double) << " bytes.\n";
return 0;
程序运行输出:
The size of an int is: 4 bytes.
The size of a short int is: 2 bytes.
The size of a long int is: 4 bytes.
The size of a char is: 1 bytes.
The size of a float is: 4 bytes.
The size of a double is: 8 bytes.
2-20 打印ASCII码为32~127的字符。
#include
int main()
for (int i = 32; i<128; i++)
cout << (char) i;
return 0;
程序运行输出:
!"#$%G'()*+,./0123456789:;?@ABCDEFGHIJKLMNOP_QRSTUVWXYZ[\]^'abcdefghijklmnopqrstuvwxyz~s
2-21 运行下面的程序,观察其输出,与你的设想是否相同?
#include
int main()
unsigned int x;
unsigned int y = 100;
unsigned int z = 50;
x= y - z;
cout << "Difference is: " << x;
x = z - y;
cout << "\nNow difference is: " << x <<endl;
return 0;
程序运行输出:
Difference is: 50
Now difference is: 4294967246
注意,第二行的输出并非 -50,注意x、y、z的数据类型。
2-22 运行下面的程序,观察其输出,体会i++与++i的差别。
#include
int main()
int myAge = 39; // initialize two integers
int yourAge = 39;
cout << "I am: " << myAge << " years old.\n";
cout << "You are: " << yourAge << " years old\n";
myAge++; // postfix increment
++yourAge; // prefix increment
cout << "One year passes...\n";
cout << "I am: " << myAge << " years old.\n";
cout << "You are: " << yourAge << " years old\n";
cout << "Another year passes\n";
cout << "I am: " << myAge++ << " years old.\n";
cout << "You are: " << ++yourAge << " years old\n";
cout << "Let's print it again.\n";
cout << "I am: " << myAge << " years old.\n";
cout << "You are: " << yourAge << " years old\n";
return 0;
程序运行输出:
I am 39 years old
You are 39 years old
One year passes
I am 40 years old
You are 40 years old
Another year passes
I am 40 years old
You are 41 years old
Let's print it again
I am 41 years old
You are 41 years old
2-23 什么叫常量?什么叫变量?
所谓常量是指在程序运行的整个过程中其值始终不可改变的量,除了用文字表示常量外,也可以为常量命名,这就是符号常量;在程序的执行过程中其值可以变化的量称为变量,变量是需要用名字来标识的。
2-24 变量有哪几种存储类型?
变量有以下几种存储类型:
auto存储类型:采用堆栈方式分配内存空间,属于一时性存储,其存储空间可以被若干变量多次覆盖使用;
register存储类型:存放在通用寄存器中;
extern存储类型:在所有函数和程序段中都可引用;
static存储类型:在内存中是以固定地址存放的,在整个程序运行期间都有效。
2-25 写出下列表达式的值:
1. 2 < 3 && 6 < 9
2. ! ( 4 5) || (6 > 2
2. -1
2-28 编写一个完整的程序,实现功能:向用户提问"现在正在下雨吗?",提示用户输入Y或N。若输入为Y,显示"现在正在下雨。"; 若输入为N,显示"现在没有下雨。";否则继续提问"现在正在下雨吗?"
#include
#include
void main()
char flag;
while(1)
cout <> flag;
if ( toupper(flag) == 'Y')
cout << "现在正在下雨。";
break;
if ( toupper(flag) == 'N')
cout << "现在没有下雨。";
break;
程序运行输出:
现在正在下雨吗?(Yes or No):x
现在正在下雨吗?(Yes or No):l
现在正在下雨吗?(Yes or No):q
现在正在下雨吗?(Yes or No):n
现在没有下雨。
现在正在下雨吗?(Yes or No):y
现在正在下雨。
2-29 编写一个完整的程序,运行时向用户提问"你考试考了多少分?(0~100)",接收输入后判断其等级,显示出来。规则如下:
#include
void main()
int i,score;
cout <> score;
if (score>100 || score<0)
cout << "分数值必须在0到100之间!";
i = score/10;
switch (i)
case 10:
case 9:
cout << "你的成绩为优!";
break;
case 8:
cout << "你的成绩为良!";
break;
case 7:
case 6:
cout << "你的成绩为中!";
break;
default:
cout << "你的成绩为差!";
程序运行输出:
你考试考了多少分?(0~100):85
你的成绩为良!
2-30 (1)实现一个简单的菜单程序,运行时显示"Menu: A(dd) D(elete) S(ort) Q(uit), Select one:"提示用户输入,A表示增加,D表示删除,S表示排序,Q表示退出,输入为A、D、S时分别提示"数据已经增加、删除、排序。"输入为Q时程序结束。要求使用if … else语句进行判断,用break、continue控制程序流程。
#include
#include
void main()
char choice,c;
while(1)
cout <> c;
choice = toupper(c);
if (choice == 'A')
cout << "数据已经增加. " << endl;
continue;
else if (choice == 'D')
cout << "数据已经删除. " << endl;
continue;
else if (choice == 'S')
cout << "数据已经排序. " << endl;
continue;
else if (choice == 'Q')
break;
程序运行输出:
Menu: A(dd) D(elete) S(ort) Q(uit), Select one:a
数据已经增加.
Menu: A(dd) D(elete) S(ort) Q(uit), Select one:d
数据已经删除.
Menu: A(dd) D(elete) S(ort) Q(uit), Select one:s
数据已经排序.
Menu: A(dd) D(elete) S(ort) Q(uit), Select one:q
(2)实现一个简单的菜单程序,运行时显示"Menu: A(dd) D(elete) S(ort) Q(uit), Select one:"提示用户输入,A表示增加,D表示删除,S表示排序,Q表示退出,输入为A、D、S时分别提示"数据已经增加、删除、排序。"输入为Q时程序结束。要求使用Switch语句。
#include
#include
void main()
char choice;
while(1)
cout <> choice;
switch(toupper(choice))
case 'A':
cout << "数据已经增加. " << endl;
break;
case 'D':
cout << "数据已经删除. " << endl;
break;
case 'S':
cout << "数据已经排序. " << endl;
break;
case 'Q':
exit(0);
break;
default:
程序运行输出:
Menu: A(dd) D(elete) S(ort) Q(uit), Select one:a
数据已经增加.
Menu: A(dd) D(elete) S(ort) Q(uit), Select one:d
数据已经删除.
Menu: A(dd) D(elete) S(ort) Q(uit), Select one:s
数据已经排序.
Menu: A(dd) D(elete) S(ort) Q(uit), Select one:q
2-31 用穷举法找出1~100间的质数,显示出来。分别使用while,do-while,for循环语句实现。
使用while循环语句:
#include
#include
void main()
int i,j,k,flag;
i = 2;
while(i <= 100)
flag = 1;
k = sqrt(i);
j = 2;
while (j <= k)
if(i%j == 0)
flag = 0;
break;
if (flag)
cout << i << "是质数." << endl;
使用do…while循环语句:
#include
#include
void main()
int i,j,k,flag;
i = 2;
flag = 1;
k = sqrt(i);
j = 2;
if(i%j == 0)
flag = 0;
break;
}while (j <= k);
if (flag)
cout << i << "是质数." << endl;
}while(i <= 100);
使用for循环语句:
#include
#include
void main()
int i,j,k,flag;
for(i = 2; i <= 100; i++)
flag = 1;
k = sqrt(i);
for (j = 2; j <= k; j++)
if(i%j == 0)
flag = 0;
break;
if (flag)
cout << i << "是质数." << endl;
程序运行输出:
2是质数.
3是质数.
5是质数.
7是质数.
11是质数.
13是质数.
17是质数.
19是质数.
23是质数.
29是质数.
31是质数.
37是质数.
41是质数.
43是质数.
47是质数.
53是质数.
59是质数.
61是质数.
67是质数.
71是质数.
73是质数.
79是质数.
83是质数.
89是质数.
97是质数.
2-32 比较Break语句与Continue语句的不同用法。
Break使程序从循环体和switch语句内跳出,继续执行逻辑上的下一条语句,不能用在别处;
continue 语句结束本次循环,接着开始判断决定是否继续执行下一次循环;
2-33 定义一个表示时间的结构体,可以精确表示年、月、日、小时、分、秒;提示用户输入年、月、日、小时、分、秒的值,然后完整地显示出来。
源程序见"实验指导"部分实验二
2-34 在程序中定义一个整型变量,赋以1~100的值,要求用户猜这个数,比较两个数的大小,把结果提示给用户,直到猜对为止。分别使用while、do…while语句实现循环。
//使用while语句
#include
void main() {
int n = 18;
int m = 0;
while(m != n)
cout <> m;
if (n > m)
cout << "你猜的值太小了!" << endl;
else if (n < m)
cout << "你猜的值太大了!" << endl;
cout << "你猜对了!" << endl;
//使用do…while语句
#include
void main() {
int n = 18;
int m = 0;
cout <> m;
if (n > m)
cout << "你猜的值太小了!" << endl;
else if (n < m)
cout << "你猜的值太大了!" << endl;
cout << "你猜对了!" << endl;
}while(n != m);
程序运行输出:
请猜这个数的值为多少?(0~~100):50
你猜的值太大了!
请猜这个数的值为多少?(0~~100):25
你猜的值太大了!
请猜这个数的值为多少?(0~~100):10
你猜的值太小了!
请猜这个数的值为多少?(0~~100):15
你猜的值太小了!
请猜这个数的值为多少?(0~~100):18
你猜对了!
2-35 定义枚举类型weekday,包括Sunday到Saturday七个元素在程序中定义weekday类型的变量,对其赋值,定义整型变量,看看能否对其赋weekday类型的值。
#include
enum weekday
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
void main()
int i;
weekday d = Thursday;
cout << "d = " << d << endl;
i = d;
cout << "i = " << i << endl;
d = (weekday)6;
cout << "d = " << d << endl;
d = weekday( 4 );
cout << "d = " << d << endl;
程序运行输出:
d = 4
i = 4
d = 6
d = 4
2-36口袋中有红、黄、蓝、白、黑五种颜色的球若干个,每次从口袋中取出三个不同颜色的球,问有多少种取法。
#include
using namespace std;
int main()
enum color{red,yellow,blue,white,black};
enum color pri;
int n,loop,i,j,k;
char c;
for(i=red;i<=black;i++)
for(j=red;j<=black;j++)
if(i!=j)
for(k=red;k<black;k++)
if((k!=i)&&(k!=j))
n=n+1;
cout.width(4);
cout<<n;
for(loop=1;loop<=3;loop++)
switch(loop)
case 1: pri=(enum color)i; break;
case 2: pri=(enum color)j; break;
case 3: pri=(enum color)k; break;
default: break;
switch(pri)
case red:cout<<"red";break;
case yellow:cout<<"yellow";break;
case blue:cout<<"blue";break;
case white:cout<<"white";break;
case black:cout<<"black";break;
default: break;
cout<<endl;
cout<<"total:"<<n<<endl;
2-37输出九九算表
#include
#include
using namespace std;
int main()
int i,j;
for(i=1;i<5;i++)
cout<<setw(4)<<i;
cout<<endl;
cout<<endl;
for(i=1;i<5;i++)
for(j=1;j<5;j++)
cout<<setw(4)<<(i*j);
cout<<endl;
第三章 函数
3-1 C++中的函数是什么?什么叫主调函数,什么叫被调函数,二者之间有什么关系?如何调用一个函数?
一个较为复杂的系统往往需要划分为若干子系统,高级语言中的子程序就是用来实现这种模块划分的。C和C++语言中的子程序就体现为函数。调用其它函数的函数被称为主调函数,被其它函数调用的函数称为被调函数。一个函数很可能既调用别的函数又被另外的函数调用,这样它可能在某一个调用与被调用关系中充当主调函数,而在另一个调用与被调用关系中充当被调函数。
调用函数之前先要声明函数原型。按如下形式声明:
类型标识符 被调函数名 (含类型说明的形参表);
声明了函数原型之后,便可以按如下形式调用子函数:
函数名(实参列表)
3-2 观察下面程序的运行输出,与你设想的有何不同?仔细体会引用的用法。
#include
int main()
int intOne;
int &rSomeRef; = intOne;
intOne = 5;
cout << "intOne:\t\t" << intOne << endl;
cout << "rSomeRef:\t" << rSomeRef << endl;
int intTwo = 8;
rSomeRef = intTwo; // not what you think!
cout << "\nintOne:\t\t" << intOne << endl;
cout << "intTwo:\t\t" << intTwo << endl;
cout << "rSomeRef:\t" << rSomeRef << endl;
return 0;
程序运行输出:
intOne: 5
rSomeRef: 5
intOne: 8
intTwo: 8
rSomeRef: 8
3-3 比较值调用和引用调用的相同点与不同点。
值调用是指当发生函数调用时,给形参分配内存空间,并用实参来初始化形参(直接将实参的值传递给形参)。这一过程是参数值的单向传递过程,一旦形参获得了值便与实参脱离关系,此后无论形参发生了怎样的改变,都不会影响到实参。
引用调用将引用作为形参,在执行主调函数中的调用语句时,系统自动用实参来初始化形参。这样形参就成为实参的一个别名,对形参的任何操作也就直接作用于实参。
3-4 什么叫内联函数?它有哪些特点?
定义时使用关键字 inline的函数叫做内联函数;
编译器在编译时在调用处用函数体进行替换,节省了参数传递、控制转移等开销;
内联函数体内不能有循环语句和switch语句;
内联函数的定义必须出现在内联函数第一次被调用之前;
对内联函数不能进行异常接口声明;
3-5 函数原型中的参数名与函数定义中的参数名以及函数调用中的参数名必须一致吗?
不必一致,所有的参数是根据位置和类型而不是名字来区分的。
3-6 重载函数时通过什么来区分?
重载的函数的函数名是相同的,但它们的参数的个数和数据类型不同,编译器根据实参和形参的类型及个数的最佳匹配,自动确定调用哪一个函数。
3-7 编写函数,参数为两个unsigned short int型数,返回值为第一个参数除以第二个参数的结果,数据类型为short int;如果第二个参数为0,则返回值为-1。在主程序中实现输入输出。
#include
short int Divider(unsigned short int a, unsigned short int b)
if (b == 0)
return -1;
return a/b;
typedef unsigned short int USHORT;
typedef unsigned long int ULONG;
int main()
USHORT one, two;
short int answer;
cout <> one;
cout <> two;
answer = Divider(one, two);
if (answer > -1)
cout << "Answer: " << answer;
cout << "Error, can't divide by zero!";
return 0;
程序运行输出:
Enter two numbers.
Number one:8
Number two:2
Answer: 4
3-8 编写函数把华氏温度转换为摄氏温度,公式为:C = (F - 32) * 5/9; 在主程序中提示用户输入一个华氏温度,转化后输出相应的摄氏温度。
源程序见"实验指导"部分实验三
3-9 编写函数判断一个数是否是质数,在主程序中实现输入、输出。
#include
#include
int prime(int i); //判一个数是否是质数的函数
void main()
int i;
cout <> i;
if (prime(i))
cout << i << "是质数." << endl;
cout << i << "不是质数." << endl;
int prime(int i)
int j,k,flag;
flag = 1;
k = sqrt(i);
for (j = 2; j <= k; j++)
if(i%j == 0)
flag = 0;
break;
if (flag)
return 1;
return 0;
程序运行输出:
请输入一个整数:1151
1151是质数.
3-10 编写函数求两个整数的最大公约数和最小公倍数。
#include
#include
int fn1(int i,int j); //求最大公约数的函数
void main()
int i,j,x,y;
cout <> i ;
cout <> j ;
x = fn1(i,j);
y = i * j / x;
cout << i << "和" << j << "的最大公约数是:" << x << endl;
cout << i << "和" << j << "的最小公倍数是:" << y << endl;
int fn1(int i, int j)
int temp;
if (i < j)
temp = i;
i = j;
j = i;
while(j != 0)
temp = i % j;
i = j;
j = temp;
return i;
程序运行输出:
请输入一个正整数:120
请输入另一个正整数:72
120和72的最大公约数是:24
120和72的最小公倍数是:360
3-11 什么叫作嵌套调用?什么叫作递归调用?
函数允许嵌套调用,如果函数1调用了函数2,函数2再调用函数3,便形成了函数的嵌套调用。
函数可以直接或间接地调用自身,称为递归调用。
3-12 在主程序中提示输入整数n,编写函数用递归的方法求1 + 2 + … + n的值。
#include
#include
int fn1(int i);
void main()
int i;
cout <> i ;
cout << "从1累加到" <<i << "的和为:" << fn1(i) << endl;
int fn1(int i)
if (i == 1)
return 1;
return i + fn1(i -1);
程序运行输出:
请输入一个正整数:100
从1累加到100的和为:5050
3-13 编写递归函数GetPower(int x, int y)计算x的y次幂, 在主程序中实现输入输出。
#include
long GetPower(int x, int y);
int main()
int number, power;
long answer;
cout <> number;
cout <> power;
answer = GetPower(number,power);
cout << number << " to the " << power << "th power is " <<answer <2;
fib(1) = fib(2) = 1;观察递归调用的过程。
源程序见"实验指导"部分实验三
3-15 用递归的方法编写函数求n阶勒让德多项式的值,在主程序中实现输入、输出;
#include
float p(int n, int x);
void main()
int n,x;
cout <> n;
cout <> x;
cout << "n = " << n << endl;
cout << "x = " << x << endl;
cout << "P" << n << "(" << x << ") = " << p(n,x) << endl;
float p(int n, int x)
if (n == 0)
return 1;
else if (n == 1)
return x;
return ((2*n-1)*x*p(n-1,x) - (n-1)*p(n-2,x)) /n ;
程序运行输出:
请输入正整数n:1
请输入正整数x:2
n = 1
x = 2
P1(2) = 2
请输入正整数n:3
请输入正整数x:4
n = 3
x = 4
P3(4) = 154
第 四 章 类
4-1 解释public和private的作用,公有类型成员与私有类型成员有些什么区别?
公有类型成员用public关键字声明,公有类型定义了类的外部接口;私有类型的成员用private关键字声明,只允许本类的函数成员来访问,而类外部的任何访问都是非法的,这样,私有的成员就整个隐蔽在类中,在类的外部根本就无法看到,实现了访问权限的有效控制。
4-2 protected关键字有何作用?
protected用来声明保护类型的成员,保护类型的性质和私有类型的性质相似,其差别在于继承和派生时派生类的成员函数可以访问基类的保护成员。
4-3 构造函数和析构函数有什么作用?
构造函数的作用就是在对象被创建时利用特定的值构造对象,将对象初始化为一个特定的状态,使此对象具有区别于彼对象的特征,完成的就是是一个从一般到具体的过程,构造函数在对象创建的时候由系统自动调用。
析构函数与构造函数的作用几乎正好相反,它是用来完成对象被删除前的一些清理工作,也就是专门作扫尾工作的。一般情况下,析构函数是在对象的生存期即将结束的时刻由系统自动调用的,它的调用完成之后,对象也就消失了,相应的内存空间也被释放。
4-4 数据成员可以为公有的吗?成员函数可以为私有的吗?
可以,二者都是合法的。数据成员和成员函数都可以为公有或私有的。但数据成员最好定义为私有的。
4-5 已知class A中有数据成员int a,如果定义了A的两个对象A1、A2,它们各自的数据成员a的值可以不同吗?
可以,类的每一个对象都有自己的数据成员。
4-6 什么叫做拷贝构造函数?拷贝构造函数何时被调用?
拷贝构造函数是一种特殊的构造函数,具有一般构造函数的所有特性,其形参是本类的对象的引用,其作用是使用一个已经存在的对象,去初始化一个新的同类的对象。在以下三种情况下会被调用:在当用类的一个对象去初始化该类的另一个对象时;如果函数的形参是类对象,调用函数进行形参和实参结合时;如果函数的返回值是类对象,函数调用完成返回时;
4-7 拷贝构造函数与赋值运算符(=)有何不同?
赋值运算符(=)作用于一个已存在的对象;而拷贝构造函数会创建一个新的对象。
4-8 定义一个Dog 类,包含的age、weight等属性,以及对这些属性操作的方法。实现并测试这个类。
#include
class Dog
public:
Dog (int initialAge = 0, int initialWeight = 5);
~Dog();
int GetAge() { return itsAge;} // inline!
void SetAge (int age) { itsAge = age;} // inline!
int GetWeight() { return itsWeight;} // inline!
void SetWeight (int weight) { itsAge = weight;} // inline!
private:
int itsAge, itsWeight;
Dog::Dog(int initialAge, int initialWeight)
itsAge = initialAge;
itsWeight = initialWeight;
Dog::~Dog() //destructor, takes no action
int main()
Dog Jack(2,10);
cout << "Jack is a Dog who is " ;
cout << Jack.GetAge() << " years old and";
cout << Jack.GetWeight() << " pounds weight.\n";
Jack.SetAge(7);
Jack.SetWeight(20);
cout << "Now Jack is " ;
cout << Jack.GetAge() << " years old and";
cout << Jack.GetWeight() << " pounds weight.";
return 0;
程序运行输出:
Jack is a Dog who is 2 years old and 10 pounds weight.
Now Jack is 7 years old 20 pounds weight.
4-9 设计并测试一个名为Rectangle的矩形类,其属性为矩形的左下角与右上角两个点的坐标,能计算矩形的面积。
#include
class Rectangle
public:
Rectangle (int top, int left, int bottom, int right);
~Rectangle () {}
int GetTop() const { return itsTop; }
int GetLeft() const { return itsLeft; }
int GetBottom() const { return itsBottom; }
int GetRight() const { return itsRight; }
void SetTop(int top) { itsTop = top; }
void SetLeft (int left) { itsLeft = left; }
void SetBottom (int bottom) { itsBottom = bottom; }
void SetRight (int right) { itsRight = right; }
int GetArea() const;
private:
int itsTop;
int itsLeft;
int itsBottom;
int itsRight;
Rectangle::Rectangle(int top, int left, int bottom, int right)
itsTop = top;
itsLeft = left;
itsBottom = bottom;
itsRight = right;
int Rectangle::GetArea() const
int Width = itsRight-itsLeft;
int Height = itsTop - itsBottom;
return (Width * Height);
int main()
Rectangle MyRectangle (100, 20, 50, 80 );
int Area = MyRectangle.GetArea();
cout << "Area: " << Area << "\n";
return 0;
程序运行输出:
Area: 3000
Upper Left X Coordinate: 20
4-10 设计一个用于人事管理的People(人员)类。考虑到通用性,这里只抽象出所有类型人员都具有的属性:number(编号)、sex(性别)、birthday(出生日期)、id(身份证号)等等。其中"出生日期"定义为一个"日期"类内嵌子对象。用成员函数实现对人员信息的录入和显示。要求包括:构造函数和析构函数、拷贝构造函数、内联成员函数、带缺省形参值的成员函数、聚集。
本题用作实验四的选做题,因此不给出答案。
4-11 定义一个矩形类,有长、宽两个属性,有成员函数计算矩形的面积
#include
class Rectangle
public:
Rectangle(float len, float width)
Length = len;
Width = width;
~Rectangle(){};
float GetArea() { return Length * Width; }
float GetLength() { return Length; }
float GetWidth() { return Width; }
private:
float Length;
float Width;
void main()
float length, width;
cout <> length;
cout <> width;
Rectangle r(length, width);
cout << "长为" << length << "宽为" << width << "的矩形的面积为:"
<< r.GetArea () << endl;
程序运行输出:
请输入矩形的长度:5
请输入矩形的宽度:4
长为5宽为4的矩形的面积为:20
4-12 定义一个"数据类型" datatype类,能处理包含字符型、整型、浮点型三种类型的数据,给出其构造函数。
#include
class datatype{
enum{
character,
integer,
floating_point
} vartype;
union
char c;
int i;
float f;
public:
datatype(char ch) {
vartype = character;
c = ch;
datatype(int ii) {
vartype = integer;
i = ii;
datatype(float ff) {
vartype = floating_point;
f = ff;
void print();
void datatype::print() {
switch (vartype) {
case character:
cout << "字符型: " << c << endl;
break;
case integer:
cout << "整型: " << i << endl;
break;
case floating_point:
cout << "浮点型: " << f << endl;
break;
void main() {
datatype A('c'), B(12), C(1.44F);
A.print();
B.print();
C.print();
程序运行输出:
字符型: c
整型: 12
浮点型: 1.44
4-13 定义一个Circle类,有数据成员半径Radius,成员函数GetArea(),计算圆的面积,构造一个Circle的对象进行测试。
#include
class Circle
public:
Circle(float radius){ Radius = radius;}
~Circle(){}
float GetArea() { return 3.14 * Radius * Radius; }
private:
float Radius;
void main()
float radius;
cout <> radius;
Circle p(radius);
cout << "半径为" << radius << "的圆的面积为:" << p.GetArea ()
<< endl;
程序运行输出:
请输入圆的半径:5
半径为5的圆的面积为:78.5
4-14 定义一个tree类,有成员ages,成员函数grow(int years)对ages加上years,age()显示tree对象的ages的值。
#include
class Tree {
int ages;
public:
Tree(int n=0);
~Tree();
void grow(int years);
void age();
Tree::Tree(int n) {
ages = n;
Tree::~Tree() {
age();
void Tree::grow(int years) {
ages += years;
void Tree::age() {
cout << "这棵树的年龄为" << ages << endl;
void main()
Tree t(12);
t.age();
t.grow(4);
程序运行输出:
这棵树的年龄为12
这棵树的年龄为16
第 五 章 C++程序的基本结构
5-1 什么叫做作用域?有哪几种类型的作用域?
作用域讨论的是标识符的有效范围,作用域是一个标识符在程序正文中有效的区域。C++的作用域分为函数原形作用域、块作用域(局部作用域)、类作用域和文件作用域.
5-2 什么叫做可见性?可见性的一般规则是什么?
可见性是标识符是否可以引用的问题;
可见性的一般规则是:标识符要声明在前,引用在后,在同一作用域中,不能声明同名的标识符。对于在不同的作用域声明的标识符,遵循的原则是:若有两个或多个具有包含关系的作用域,外层声明的标识符如果在内层没有声明同名标识符时仍可见,如果内层声明了同名标识符则外层标识符不可见。
5-3 下面的程序的运行结果是什么,实际运行一下,看看与你的设想有何不同。
#include
void myFunction();
int x = 5, y = 7;
int main()
cout << "x from main: " << x << "\n";
cout << "y from main: " << y << "\n\n";
myFunction();
cout << "Back from myFunction!\n\n";
cout << "x from main: " << x << "\n";
cout << "y from main: " << y << "\n";
return 0;
void myFunction()
int y = 10;
cout << "x from myFunction: " << x << "\n";
cout << "y from myFunction: " << y << "\n\n";
程序运行输出:
x from main: 5
y from main: 7
x from myFunction: 5
y from myFunction: 10
Back from myFunction!
x from main: 5
y from main: 7
5-4 假设有两个无关系的类Engine和Fuel,使用时,怎样允许Fuel成员访问Engine中的私有和保护的成员?
class fuel;
class engine
friend class fuel;
private;
int powerlevel;
public;
engine(){ powerLevel = 0;}
void engine_fn(fuel &f);
class fuel
friend class engine;
private;
int fuelLevel;
public:
fuel(){ fuelLevel = 0;}
void fuel_fn( engine &e);
5-5 什么叫做静态数据成员?它有何特点?
类的静态数据成员是类的数据成员的一种特例,采用static关键字来声明。对于类的普通数据成员,每一个类的对象都拥有一个拷贝,就是说每个对象的同名数据成员可以分别存储不同的数值,这也是保证对象拥有自身区别于其它对象的特征的需要,但是静态数据成员,每个类只要一个拷贝,由所有该类的对象共同维护和使用,这个共同维护、使用也就实现了同一类的不同对象之间的数据共享。
5-6 什么叫做静态函数成员?它有何特点?
使用static关键字声明的函数成员是静态的,静态函数成员属于整个类,同一个类的所有对象共同维护,为这些对象所共享。静态函数成员具有以下两个方面的好处,一是由于静态成员函数只能直接访问同一个类的静态数据成员,可以保证不会对该类的其余数据成员造成负面影响;二是同一个类只维护一个静态函数成员的拷贝,节约了系统的开销,提高程序的运行效率。
5-7 定义一个Cat类,拥有静态数据成员HowManyCats,记录Cat的个体数目;静态成员函数GetHowMany(),存取HowManyCats。设计程序测试这个类,体会静态数据成员和静态成员函数的用法。
#include
class Cat
public:
Cat(int age):itsAge(age){HowManyCats++; }
virtual ~Cat() { HowManyCats--; }
virtual int GetAge() { return itsAge; }
virtual void SetAge(int age) { itsAge = age; }
static int GetHowMany() { return HowManyCats; }
private:
int itsAge;
static int HowManyCats;
int Cat::HowManyCats = 0;
void TelepathicFunction();
int main()
const int MaxCats = 5;
Cat *CatHouse[MaxCats]; int i;
for (i = 0; i<MaxCats; i++)
CatHouse[i] = new Cat(i);
TelepathicFunction();
for ( i = 0; i<MaxCats; i++)
delete CatHouse[i];
TelepathicFunction();
return 0;
void TelepathicFunction()
cout << "There are " << Cat::GetHowMany() << " cats alive!\n";
程序运行输出:
There are 1 cats alive!
There are 2 cats alive!
There are 3 cats alive!
There are 4 cats alive!
There are 5 cats alive!
There are 4 cats alive!
There are 3 cats alive!
There are 2 cats alive!
There are 1 cats alive!
There are 0 cats alive!
5-8 什么叫做友元函数?什么叫做友元类?
友元函数是使用friend关键字声明的函数,它可以访问相应类的保护成员和私有成员。友元类是使用friend关键字声明的类,它的所有成员函数都是相应类的友元函数。
5-9 如果类A是类B的友元,类B是类C的友元,类D是类A的派生类,那么类B是类A的友元吗?类C是类A的友元吗?类D是类B的友元吗?
类B不是类A的友元,友元关系不具有交换性;
类C不是类A的友元,友元关系不具有传递性;
类D不是类B的友元,友元关系不能被继承。
5-10 静态成员变量可以为私有的吗?声明一个私有的静态整型成员变量。
可以,例如:
private:
static int a;
5-11 在一个文件中定义一个全局变量n,主函数main(),在另一个文件中定义函数fn1(),在main()中对n赋值,再调用fn1(),在fn1()中也对n赋值,显示n最后的值。
#include
#include "fn1.h"
int n;
void main()
n = 20;
fn1();
cout << "n的值为" <<n;
// fn1.h文件
extern int n;
void fn1()
n=30;
程序运行输出:
n的值为30
5-12 在函数fn1()中定义一个静态变量n,fn1()中对n的值加1,在主函数中,调用fn1()十次,显示n的值。
#include
void fn1()
static int n = 0;
cout << "n的值为" << n <<endl;
void main()
for(int i = 0; i i =+10; }
void Y::g(X* x) { x->i ++; }
class Z {
public:
void f(X* x) { x->i += 5; }
#endif // MY_X_Y_Z_H
程序运行输出:无
5-14 定义Boat与Car两个类,二者都有weight属性,定义二者的一个友元函数totalWeight(),计算二者的重量和。
#include
class Boat;
class Car
private:
int weight;
public:
Car(int j){weight = j;}
friend int totalWeight(Car &aCar;, Boat &aBoat;);
class Boat
private:
int weight;
public:
Boat(int j){weight = j;}
friend int totalWeight(Car &aCar;, Boat &aBoat;);
int totalWeight(Car &aCar;, Boat &aBoat;)
return aCar.weight + aBoat.weight;
void main()
Car c1(4);
Boat b1(5);
cout << totalWeight(c1, b1) << endl;
程序运行输出:
第 六 章 数组、指针与字符串
6-1 数组A[10][5][15]一共有多少个元素?
10×5×15 = 750 个元素
6-2 在数组A[20]中第一个元素和最后一个元素是哪一个?
第一个元素是A[0],最后一个元素是A[19]。
6-3 用一条语句定义一个有五个元素的整型数组,并依次赋予1~5的初值。
int IntegerArray[5] = { 1, 2, 3, 4, 5 };
或:int IntegerArray[] = { 1, 2, 3, 4, 5 };
6-4 已知有一个数组名叫oneArray,用一条语句求出其元素的个数。
nArrayLength = sizeof(oneArray) / sizeof(oneArray[0]);
6-5 用一条语句定义一个有5×3个元素的二维整型数组,并依次赋予1~15的初值。
int theArray[5][3] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
或:int theArray[5][3] = { {1,2,3}, {4,5,6}, {7,8,9}, {10,11,12},{13,14,15} };
6-6 运算符*和&的作用是什么?
*称为指针运算符,是一个一元操作符,表示指针所指向的对象的值;&称为取地址运算符,也是一个一元操作符,是用来得到一个对象的地址。
6-7 什么叫做指针?指针中储存的地址和这个地址中的值有何区别?
指针是一种数据类型,具有指针类型的变量称为指针变量。指针变量存放的是另外一个对象的地址,这个地址中的值就是另一个对象的内容。
6-8 定义一个整型指针,用new语句为其分配包含10个整型元素的地址空间。
int *pInteger = new int[10];
6-9 在字符串”Hello,world!”中结束符是什么?
是NULL字符。
6-10 定义一个有五个元素的整型数组,在程序中提示用户输入元素值,最后再在屏幕上显示出来。
#include
int main()
int myArray[5];
int i;
for ( i=0; i<5; i++)
cout << "Value for myArray[" << i <> myArray[i];
for (i = 0; i<5; i++)
cout << i << ": " << myArray[i] << "\n";
return 0;
程序运行输出:
Value for myArray[0]: 2
Value for myArray[1]: 5
Value for myArray[2]: 7
Value for myArray[3]: 8
Value for myArray[4]: 3
6-11 引用和指针有何区别?何时只能使用指针而不能使用引用?
引用是一个别名,不能为NULL值,不能被重新分配;指针是一个存放地址的变量。当需要对变量重新赋以另外的地址或赋值为NULL时只能使用指针。
6-12 声明下列指针:float类型变量的指针pFloat,char类型的指针pString和struct customer型的指针prec。
float *pfloat;
char *pString;
struct customer *prec;
6-13 给定float类型的指针fp,写出显示fp所指向的值的输出流语句。
cout << "Value == " << *fp;
6-14 程序中定义一个double类型变量的指针。分别显示指针占了多少字节和指针所指的变量占了多少字节。
double *counter;
cout << "\nSize of pointer == "sizeof(counter);
cout << '\nSize of addressed value == "<<sizeof(*counter);
6-15 const int * p1 和 int * const p2的区别是什么?
const int * p1 声明了一个指向整型常量的指针p1,因此不能通过指针p1来改变它所指向的整型值;int * const p2声明了一个指针型常量,用于存放整型变量的地址,这个指针一旦初始化后,就不能被重新赋值了。
6-16 定义一个整型变量a,一个整型指针p,一个引用r,通过p把a的值改为10,通过r把a的值改为5
void main()
int a;
int *p = &a;
int &r = a;
*p = 10;
r = 5;
6-17 下列程序有何问题,请仔细体会使用指针时应避免出现这个的问题。
#include
int main()
int *p;
*pInt = 9;
cout << "The value at p: " << *p;
return 0;
指针p没有初始化,也就是没有指向某个确定的内存单元,它指向内存中的一个随机地址,给这个随机地址赋值是非常危险的。
6-18 下列程序有何问题,请改正;仔细体会使用指针时应避免出现的这个问题。
#include
int Fn1();
int main()
int a = Fn1();
cout << "the value of a is: " << a;
return 0;
int Fn1()
int * p = new int (5);
return *p;
此程序中给*p分配的内存没有被释放掉。
#include
int* Fn1();
int main()
int *a = Fn1();
cout << "the value of a is: " << *a;
delete a;
return 0;
int* Fn1()
int * p = new int (5);
return p;
6-19 声明一个参数为整型,返回值为长整型的函数指针;声明类A的一个成员函数指针,其参数为整型,返回值长整型。
long (* p_fn1)(int);
long ( A::*p_fn2)(int);
6-20 实现一个名为SimpleCircle的简单圆类,其数据成员int *itsRadius为一个指向其半径值的指针,设计对数据成员的各种操作,给出这个类的完整实现并测试这个类。
#include
class SimpleCircle
public:
SimpleCircle();
SimpleCircle(int);
SimpleCircle(const SimpleCircle &);
~SimpleCircle() {}
void SetRadius(int);
int GetRadius()const;
private:
int *itsRadius;
SimpleCircle::SimpleCircle()
itsRadius = new int(5);
SimpleCircle::SimpleCircle(int radius)
itsRadius = new int(radius);
SimpleCircle::SimpleCircle(const SimpleCircle & rhs)
int val = rhs.GetRadius();
itsRadius = new int(val);
int SimpleCircle::GetRadius() const
return *itsRadius;
int main()
SimpleCircle CircleOne, CircleTwo(9);
cout << "CircleOne: " << CircleOne.GetRadius() << endl;
cout << "CircleTwo: " << CircleTwo.GetRadius() << endl;
return 0;
}程序运行输出:
CircleOne: 5
CircleTwo: 9
6-21 编写一个函数,统计一个英文句子中字母的个数,在主程序中实现输入、输出。
#include
#include
int count(char *str)
int i,num=0;
for (i=0; str[i]; i++)
if ( (str[i]>='a' && str[i]='A' && str[i]<='Z') )
num++;
return num;
void main()
char text[100];
cout << "输入一个英语句子:" << endl;
gets(text);
cout << "这个句子里有" << count(text) << "个字母。" << endl;
程序运行输出:
输入一个英语句子:
It is very interesting!
这个句子里有19个字母。
6-22 编写函数int index(char *s, char *t),返回字符串t 在字符串s中出现的最左边的位置,如果在s中没有与t匹配的子串,就返回-1。
#include
int index( char *s, char *t)
int i,j,k;
for(i = 0; s[i] != '\0'; i++)
for(j = i, k = 0; t[k] != '\0' && s[j] == t[k]; j++, k++)
if (t[k] =='\0')
return i;
return -1;
void main()
int n;
char str1[20],str2[20];
cout <> str1;
cout <> str2;
n = index(str1,str2);
if (n > 0)
cout << str2 << "在" << str1 << "中左起第" << n+1
<< "个位置。"<<endl;
cout << str2 << "不在" << str1 << "中。" << endl;
程序运行输出:
输入一个英语单词:abcdefgh
输入另一个英语单词:de
de在abcdefghijk中左起第4个位置。
6-23 编写函数reverse(char *s)的倒序递归程序,使字符串s倒序。
#include
#include
void reverse(char *s, char *t)
char c;
if (s < t)
c = *s;
*s = *t;
*t = c;
reverse(++s, --t);
void reverse( char *s)
reverse(s, s + strlen(s) - 1);
void main()
char str1[20];
cout <> str1;
cout << "原字符串为:" << str1 << endl;
reverse(str1);
cout << "倒序反转后为:" << str1 << endl;
程序运行输出:
输入一个字符串:abcdefghijk
原字符串为:abcdefghijk
倒序反转后为:kjihgfedcba
6-24 设学生人数N=8,提示用户输入N个人的考试成绩,然后计算出平均成绩,显示出来。
#include
#include
#define N 8
float grades[N]; //存放成绩的数组
void main()
int i;
float total,average;
//提示输入成绩
for(i = 0; i < N; i++ )
cout << "Enter grade #" <<(i +1) <> grades[i];
total = 0;
for (i = 0; i < N; i++)
total += grades[i];
average = total / N;
cout << "\nAverage grade: " << average << endl;
程序运行输出:
Enter grade #1: 86
Enter grade #2: 98
Enter grade #3: 67
Enter grade #4: 80
Enter grade #5: 78
Enter grade #6: 95
Enter grade #7: 78
Enter grade #8: 56
Average grade: 79.75
6-25 设计一个字符串类MyString,具有构造函数、析构函数、拷贝构造函数,重载运算符+、=、+=、[],尽可能地完善它,使之能满足各种需要。(运算符重载功能为选做,参见第8章)
#include
#include
class MyString
public:
MyString();
MyString(const char *const);
MyString(const MyString &);
~MyString();
char & operator[](unsigned short offset);
char operator[](unsigned short offset) const;
MyString operator+(const MyString&);
void operator+=(const MyString&);
MyString & operator= (const MyString &);
unsigned short GetLen()const { return itsLen; }
const char * GetMyString() const { return itsMyString; }
private:
MyString (unsigned short); // private constructor
char * itsMyString;
unsigned short itsLen;
MyString::MyString()
itsMyString = new char[1];
itsMyString[0] = '\0';
itsLen=0;
MyString::MyString(unsigned short len)
itsMyString = new char[len+1];
for (unsigned short i = 0; i<=len; i++)
itsMyString[i] = '\0';
itsLen=len;
MyString::MyString(const char * const cMyString)
itsLen = strlen(cMyString);
itsMyString = new char[itsLen+1];
for (unsigned short i = 0; i<itsLen; i++)
itsMyString[i] = cMyString[i];
itsMyString[itsLen]='\0';
MyString::MyString (const MyString & rhs)
itsLen=rhs.GetLen();
itsMyString = new char[itsLen+1];
for (unsigned short i = 0; i<itsLen;i++)
itsMyString[i] = rhs[i];
itsMyString[itsLen] = '\0';
MyString::~MyString ()
delete [] itsMyString;
itsLen = 0;
MyString& MyString::operator=(const MyString & rhs)
if (this == &rhs;)
return *this;
delete [] itsMyString;
itsLen=rhs.GetLen();
itsMyString = new char[itsLen+1];
for (unsigned short i = 0; i itsLen)
return itsMyString[itsLen-1];
return itsMyString[offset];
char MyString::operator[](unsigned short offset) const
if (offset > itsLen)
return itsMyString[itsLen-1];
return itsMyString[offset];
MyString MyString::operator+(const MyString& rhs)
unsigned short totalLen = itsLen + rhs.GetLen();
MyString temp(totalLen);
for (unsigned short i = 0; i<itsLen; i++)
temp[i] = itsMyString[i];
for (unsigned short j = 0; j<rhs.GetLen(); j++, i++)
temp[i] = rhs[j];
temp[totalLen]='\0';
return temp;
void MyString::operator+=(const MyString& rhs)
unsigned short rhsLen = rhs.GetLen();
unsigned short totalLen = itsLen + rhsLen;
MyString temp(totalLen);
for (unsigned short i = 0; i<itsLen; i++)
temp[i] = itsMyString[i];
for (unsigned short j = 0; j<rhs.GetLen(); j++, i++)
temp[i] = rhs[i-itsLen];
temp[totalLen]='\0';
*this = temp;
int main()
MyString s1("initial test");
cout << "S1:\t" << s1.GetMyString() << endl;
char * temp = "Hello World";
s1 = temp;
cout << "S1:\t" << s1.GetMyString() << endl;
char tempTwo[20];
strcpy(tempTwo,"; nice to be here!");
s1 += tempTwo;
cout << "tempTwo:\t" << tempTwo << endl;
cout << "S1:\t" << s1.GetMyString() << endl;
cout << "S1[4]:\t" << s1[4] << endl;
s1[4]='x';
cout << "S1:\t" << s1.GetMyString() << endl;
cout << "S1[999]:\t" << s1[999] << endl;
MyString s2(" Another myString");
MyString s3;
s3 = s1+s2;
cout << "S3:\t" << s3.GetMyString() << endl;
MyString s4;
s4 = "Why does this work?";
cout << "S4:\t" << s4.GetMyString() << endl;
return 0;
程序运行输出:
S1: initial test
S1: Hello World
tempTwo: ; nice to be here!
S1: Hello World; nice to be here!
S1[4]: o
S1: Hellx World; nice to be here!
S1[999]: !
S3: Hellx World; nice to be here! Another myString
S4: Why does this work?
6-26 编写一个3×3矩阵转置的函数,在main()函数中输入数据。
#include
void move (int matrix[3][3])
int i, j, k;
for(i=0; i<3; i++)
for (j=0; j<i; j++)
k = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = k;
void main()
int i, j;
int data[3][3];
cout << "输入矩阵的元素" << endl;
for(i=0; i<3; i++)
for (j=0; j<3; j++)
cout << "第" << i+1 << "行第" << j+1
<> data[i][j];
cout << "输入的矩阵的为:" << endl;
for(i=0; i<3; i++)
for (j=0; j<3; j++)
cout << data[i][j] << " ";
cout << endl;
move(data);
cout << "转置后的矩阵的为:" << endl;
for(i=0; i<3; i++)
for (j=0; j<3; j++)
cout << data[i][j] << " ";
cout << endl;
程序运行输出:
输入矩阵的元素
第1行第1个元素为:1
第1行第2个元素为:2
第1行第3个元素为:3
第2行第1个元素为:4
第2行第2个元素为:5
第2行第3个元素为:6
第3行第1个元素为:7
第3行第2个元素为:8
第3行第3个元素为:9
输入的矩阵的为:
1 2 3
4 5 6
7 8 9
转置后的矩阵的为:
1 4 7
2 5 8
3 6 9
6-27 编写一个矩阵转置的函数,矩阵的维数在程序中由用户输入。
#include
void move (int *matrix ,int n)
int i, j, k;
for(i=0; i<n; i++)
for (j=0; j<i; j++)
k = *(matrix + i*n + j);
*(matrix + i*n + j) = *(matrix + j*n + i);
*(matrix + j*n + i) = k;
void main()
int n, i, j;
int *p;
cout <> n;
p = new int[n*n];
cout << "输入矩阵的元素" << endl;
for(i=0; i<n; i++)
for (j=0; j<n; j++)
cout << "第" << i+1 << "行第" << j+1
<> p[i*n + j];
cout << "输入的矩阵的为:" << endl;
for(i=0; i<n; i++)
for (j=0; j<n; j++)
cout << p[i*n + j] << " ";
cout << endl;
move(p, n);
cout << "转置后的矩阵的为:" << endl;
for(i=0; i<n; i++)
for (j=0; j<n; j++)
cout << p[i*n + j] << " ";
cout << endl;
程序运行输出:
请输入矩阵的维数:3
输入矩阵的元素
第1行第1个元素为:1
第1行第2个元素为:2
第1行第3个元素为:3
第2行第1个元素为:4
第2行第2个元素为:5
第2行第3个元素为:6
第3行第1个元素为:7
第3行第2个元素为:8
第3行第3个元素为:9
输入的矩阵的为:
1 2 3
4 5 6
7 8 9
转置后的矩阵的为:
1 4 7
2 5 8
3 6 9
6-28 定义一个Employee类,其中包括表示姓名、街道地址、城市和邮编等属性,包括chage_name()和display()等函数;display()使用cout语句显示姓名、街道地址、城市和邮编等属性,函数change_name()改变对象的姓名属性,实现并测试这个类。
#include
#include
class Employee
private:
char name[30];
char street[30];
char city[18];
char zip[6];
public:
Employee(char *n, char *str, char *ct, char *z);
void change_name(char *n);
void display();
Employee::Employee (char *n,char *str,char *ct, char *z)
strcpy(name, n);
strcpy(street, str);
strcpy(city, ct);
strcpy(zip, z);
void Employee::change_name (char *n)
strcpy(name, n);
void Employee::display ()
cout << name << " " << street << " ";
cout << city << " "<< zip;
void main(void)
Employee e1("张三","平安大街3号", "北京", "100000");
e1.display();
cout << endl;
e1.change_name("李四");
e1.display();
cout << endl;
程序运行输出:
张三 平安大街3号 北京 100000
李四 平安大街3号 北京 100000
第 七 章 继承与派生
7-1 比较类的三种继承方式public公有继承、protected保护继承、private私有继承之间的差别。
不同的继承方式,导致不同访问属性的基类成员在派生类中的访问属性也有所不同:
公有继承,使得基类public(公有)和protected(保护)成员的访问属性在派生类中不变,而基类private(私有)成员不可访问。
私有继承,使得基类public(公有)和protected(保护)成员都以private(私有)成员身份出现在派生类中,而基类private(私有)成员不可访问。
保护继承中,基类public(公有)和protected(保护)成员都以protected(保护)成员身份出现在派生类中,而基类private(私有)成员不可访问。
7-2 派生类构造函数执行的次序是怎样的?
派生类构造函数执行的一般次序为:调用基类构造函数;调用成员对象的构造函数;派生类的构造函数体中的内容。
7-3 如果在派生类B已经重载了基类A的一个成员函数fn1(),没有重载成员函数fn2(),如何调用基类的成员函数fn1()、fn2()?
调用方法为: A::fn1();
fn2();
7-4 什么叫做虚基类?有何作用?
当某类的部分或全部直接基类是从另一个基类派生而来,这些直接基类中,从上一级基类继承来的成员就拥有相同的名称,派生类的对象的这些同名成员在内存中同时拥有多个拷贝,我们可以使用作用域分辨符来唯一标识并分别访问它们。我们也可以将直接基类的共同基类设置为虚基类,这时从不同的路径继承过来的该类成员在内存中只拥有一个拷贝,这样就解决了同名成员的唯一标识问题。
虚基类的声明是在派生类的定义过程,其语法格式为:
class 派生类名:virtual 继承方式 基类名
上述语句声明基类为派生类的虚基类,在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。声明了虚基类之后,虚基类的成员在进一步派生过程中,和派生类一起维护一个内存数据拷贝。
7-5 定义一个Shape基类,在此基础上派生出Rectangle和Circle,二者都有GetArea()函数计算对象的面积。使用Rectangle类创建一个派生类Square。
#include
class Shape
public:
Shape(){}
~Shape(){}
virtual float GetArea() { return -1; }
class Circle : public Shape
public:
Circle(float radius):itsRadius(radius){}
~Circle(){}
float GetArea() { return 3.14 * itsRadius * itsRadius; }
private:
float itsRadius;
class Rectangle : public Shape
public:
Rectangle(float len, float width): itsLength(len), itsWidth(width){};
~Rectangle(){};
virtual float GetArea() { return itsLength * itsWidth; }
virtual float GetLength() { return itsLength; }
virtual float GetWidth() { return itsWidth; }
private:
float itsWidth;
float itsLength;
class Square : public Rectangle
public:
Square(float len);
~Square(){}
Square::Square(float len):
Rectangle(len,len)
void main()
Shape * sp;
sp = new Circle(5);
cout << "The area of the Circle is " <GetArea () << endl;
delete sp;
sp = new Rectangle(4,6);
cout << "The area of the Recta
public static void main(String[] args) {
String str = "abc";
String binary = toBinary(str);
System.out.println(binary);
pub...
ofstream out("test.binary",ios::binary|ios::out);
ifstream in("test.binary",ios::binary|ios::in);
这两个是设定文件的读取和写入的方式,这
想旅行的钥匙 · 如何用folium在地图上画点 - CSDN文库 5 月前 |