C++/C范文
C++/C范文(精选12篇)
C++/C 第1篇
在C/C-SiC复合材料的各种制备方法中, 先驱体浸渍裂解法 (Precursor Impregnation Pyrolysis, PIP) 是用有机先驱体如聚碳硅烷溶液浸渍低密度C/C多孔体, 在一定条件下交联固化, 然后在一定温度和气氛下裂解转化为SiC陶瓷基体, 经反复浸渍裂解最终制得致密的C/C-SiC复合材料。由于先驱体浸渍裂解法具有可制备形状复杂、近尺寸的异形构件, 并且工艺简单, 基体组成调节范围广等优点[6,7,8], 目前已逐渐成为制备C/C-SiC复合材料的主要工艺方法。但由于C/C多孔体的孔隙率、孔隙的尺寸与分布和连通性随多孔体密度不同而不同, 将直接影响浸渍裂解的效果, 从而得到不同力学、热学、抗氧化烧蚀等性能的C/C-SiC复合材料[9,10,11], 并影响其应用。所以, 探讨不同密度的C/C多孔体对C/C-SiC复合材料性能的影响, 并确定合适的多孔体密度, 对于制备高密度的C/C-SiC复合材料具有一定指导意义。
本工作用碳纤维针刺成型预制体, 采用CVI工艺制备出4种不同密度热解炭基C/C多孔体, 再以聚碳硅烷为先驱体采用PIP工艺, 制备得到C/C-SiC复合材料, 研究了不同密度C/C多孔体对后续SiC基体的致密效率以及最终C/C-SiC复合材料性能的影响。
1 实验
1.1 C/C-SiC复合材料的制备
本实验中所用的纤维为日本东丽 (Toray) 公司生产的PAN基碳纤维, 采用3K斜纹炭布和网胎交替叠层, 采用接力针刺的方法将网胎中的纤维垂直刺入炭布制成准三维结构预制体, 预制体密度为0.45g/cm3。以C3H6为气源, N2为稀释气体, 采用化学气相渗透法 (CVI) 对预制体进行增密, 在1050℃温度下致密数小时后得到4种密度的C/C复合多孔体, 为M1, M2, M3及M4。对这4种不同密度的C/C多孔体进行高温处理后, 通过PIP法以聚碳硅烷为前驱体对C/C复合材料进行增密, 加压浸渍-裂解循环致密6周期, 最高裂解温度为1500℃, 最终获得4种密度不同的C/C-SiC复合材料, 为S1, S2, S3及S4。
1.2 测试方法
(1) 采用Archimedes法测定C/C-SiC材料的开孔隙率和密度。用JSM-6460LV型扫描电子显微镜 (SEM) 观察C/C-SiC试样的弯曲断面和烧蚀表面。
(2) 采用INSTRON4505型电子万能试验机, 以三点弯曲法测定C/C-SiC材料的弯曲强度和模量, 试样尺寸55mm×10mm×4mm, 且每种材料性能测试均采用6个子样。
(3) 用氧-乙炔火焰烧蚀试样表面, 考虑到C/C-SiC复合材料长时间抗氧化的使用环境, 采用烧蚀条件:喷嘴直径为2mm, 氧乙炔枪口到试样表面中心距离为20mm。氧气流量与乙炔流量之比为2∶1, 烧蚀时间为600s。烧蚀实验前后, 分别用千分表 (分辨率:0.01mm) 和电子天平 (分辨率:0.1mg) 测试试样的烧蚀中心厚度与质量, 线烧蚀率和质量烧蚀率按照以下两式计算:
式中:R1和Rm分别为线烧蚀率和质量烧蚀率;l0和lt分别为烧蚀前后中心厚度;m0和mt分别是烧蚀前后实验质量;t为烧蚀时间。
2 结果与分析
2.1 C/C-SiC复合材料的密度与气孔率
图1是4种不同密度C/C多孔体致密成C/C-SiC复合材料时密度随致密周期的变化曲线。表1是4种C/C多孔体和相应的C/C-SiC复合材料的密度和开孔率。
从图1可以看出, 随着致密周期延长, C/C-SiC复合材料的密度不断增加。C/C多孔体密度较低, 其増密速度较快, 其中密度最低的C/C多孔体M1増密曲线斜率最大。但密度最高的C/C多孔体, 最终制得材料密度最大。这与C/C多孔体的开孔率有关, 如表1所示, 一般而言, C/C多孔体的开孔率越高, 渗入浸渍剂越多, 进而反应生成的SiC较多。同时, 高温处理使C/C中的沉积炭收缩, 基体孔隙率增大, 更有利于浸渍剂的渗入, 因为制备出的C/C-SiC复合材料的密度较高。但是, 多渗入的SiC并不能完全填充多孔体及高温处理产生的孔隙率, 因此高开孔率C/C多孔体最终得到的C/C-SiC复合材料的开孔率较高。
对S1~S4中SiC含量进行了估算, 忽略材料浸渍PCS后的体积变化, ρ1, ρ2分别代表C/C试样、C/C-SiC试样的密度, v代表试样的体积, 则SiC含量的计算公式为: (ρ2v-ρ1v) /ρ2v=1-ρ1/ρ2, 由此计算得到的相关数据也列于表1中。可见, S1中SiC含量最高, S4则最低。
2.2 C/C-SiC复合材料的弯曲性能
图2为编号为S1~S4的C/C-SiC复合材料的弯曲强度测试结果的柱状图。
由图2可见:不同C/C多孔体密度制备的C/C-SiC复合材料的弯曲强度相差较大, 初始密度为1.35g/cm3的S2材料弯曲强度最好, 初始密度为1.52g/cm3的S4材料弯曲强度最差。对比S1、S3与S4, SiC的存在可以使C/C-SiC材料具有较高的弯曲强度, 因为SiC的弯曲强度为500MPa[12,13], S1中SiC含量明显高于S3和S4, 因此其具有较高的弯曲强度。同时, 对于复合材料而言, 纤维和基体的界面是影响材料性能的关键因素, C/C-SiC复合材料弯曲断口SEM照片如图3所示。
从图3可见, “1”所示结构为碳纤维, 可观察到材料中有大量碳纤维的存在, 纤维/纤维、纤维束/纤维束之间均存在一定的孔隙。“2”所示结构为CVI-C基体, 其沿着纤维周围生长, 形成“包鞘”结构。“3”结构为块状或片状结构则为SiC基体, 它较好地填充了纤维束之间的大孔隙。其中, S2试样 (多孔体密度为1.35g/cm3) 的断口有相当多的纤维和纤维束拔出, 拔出纤维的长度达几十微米, 说明S2试样纤维和基体之间形成比较理想的结合界面, 表明C/C多孔体密度为1.35g/cm3时, 纤维和基体的界面结合较好。此时界面能有效的传递载荷, 纤维起到承载的作用。可通过纤维和基体的界面脱粘、纤维的断裂、纤维从基体拔出等耗能增韧机制, 阻止材料发生脆性断裂, 呈典型的韧性破坏, 有利于发挥碳纤维在C/C-SiC中的增韧、补强作用。这些因素使得S2试样的强度、韧性均有较大幅度的提高, 弯曲强度数值最高。同时从图3可看出, S1试样由于其C/C多孔体密度较低、沉积时间较短, 界面结合强度相对较弱;而S3和S4试样则表现为界面结合较强。
图3 C/C-SiC复合材料弯曲断口SEM照片 (a) S1; (b) S2; (c) S3; (d) S4Fig.3 SEM photographs of the bending fracture of C/C-SiC composites (a) S1; (b) S2; (c) S3; (d) S4
2.3 C/C-SiC复合材料的抗烧蚀性能
表2给出了C/C-SiC复合材料S1~S4的烧蚀结果。可见, S1~S4四种C/C-SiC复合材料经过600s的氧化烧蚀后线烧蚀率和质量烧蚀率都比较低, 其中随着C/C多孔体密度的增加, SiC含量不断降低, 材料的质量烧蚀率和线烧蚀率均增大。其中, 由C/C多孔体密度最低的M1制备的S1的线烧蚀率、质量烧蚀率和烧蚀深度均最小, 在S1~S4中表现出最好的抗烧蚀性能。
图4 (a) ~ (d) 分别是C/C-SiC复合材料S1~S4烧蚀后烧蚀中心裸露碳纤维的微观形貌。
图4材料烧蚀中心纤维形貌 (a) S1; (b) S2; (c) S3; (d) S4Fig.4 Fiber morphology of the ablation center of S1 (a) , S2 (b) , S3 (c) and S4 (d)
分别观察图4 (a) ~ (d) 中纤维形貌, 纤维表面仍附着着黏稠状物质和球状颗粒, 为SiC基体氧化后的产物。当氧乙炔焰流剧烈地冲击材料的表面, 材料与氧化性气氛 (氧气与乙炔的流量比为2∶1, 因此火焰为氧化焰) 反应[14], 氧与SiC基体首先发生氧化反应, 反应产物为SiO2, 一段时间后将形成致密的SiO2膜, 愈合了孔隙和裂纹, 形成了钝化隔离层, 可阻止氧化气体向内扩散的通道, 体现出C/C-SiC复合材料的优异的抗氧化性能。因此, S1材料中SiC含量最高, 其抗烧蚀能力越好;且SiC氧化成SiO2为增重的过程, 因此残留的氧化基体越多, 其质量烧蚀率越小。
同时从图4 (a) ~ (d) 中可观察到随着材料中SiC含量的减小, 材料中炭纤维的烧蚀更加严重。这是由于随着两种基体烧蚀和冲刷的不断进行, 横向纤维逐渐裸露出来。对于裸露的碳纤维来说, 由于端部裸露的时间最长、受到燃气的冲刷力最大, 烧蚀也越多, 从而导致碳纤维前端又细又尖, 而后端相对较粗。可见, S1中基体抗烧蚀冲刷时间较长, 纤维暴露时间较短, 表面仅出现氧化和气流冲刷的痕迹;而S4中纤维烧蚀较严重, 前端呈现典型的针状。
3 结论
(1) 对于制备C/C-SiC材料来说, 先驱体浸渍裂解法是制备高密度的复合材料有效方法。C/C多孔体密度越低, 其増密速率越快;但密度最高的C/C多孔体, 最终制得材料密度最大。高孔隙率C/C多孔体得到的C/C-SiC复合材料的孔隙率较高, SiC含量较高。
(2) C/C多孔体密度为1.35g/cm3的C/C-SiC复合材料弯曲强度最好, SiC的存在可以使C/C-SiC材料具有较高的弯曲强度, 纤维和基体的界面是影响材料性能的关键因素。S2试样纤维和基体之间形成比较理想的结合界面, 有利于发挥碳纤维在C/C-SiC中的增韧、补强作用。
(3) SiC含量越多, C/C-SiC复合材料抗烧蚀性能越好。SiC基体氧化生成致密的SiO2膜, 愈合了孔隙和裂纹, 可阻止氧化气体向内扩散的通道, 从而降低了材料的烧蚀速率。
摘要:以准三维针刺碳纤维预制体, 经化学气相渗透 (CVI) 法制备了4种密度的C/C多孔体, 利用先驱体浸渍裂解法 (PIP) 制备了C/C-SiC复合材料, 研究了C/C多孔体对C/C-SiC复合材料制备和最终性能的影响。结果表明:C/C多孔体密度越低, 最终得到的C/C-SiC复合材料开孔隙率及SiC含量较高。SiC的存在使C/C-SiC材料具有较高的弯曲强度, 纤维和基体界面也是影响弯曲强度的关键因素, 其中密度为1.35g/cm3的C/C多孔体所制备的C/C-SiC复合材料纤维和基体之间形成较好的结合界面, 其弯曲强度最大。同时, SiC含量增加可显著提高C/C-SiC复合材料的抗烧蚀性能。
C++/C 第2篇
emulator -console
* 将文件写入到模拟器的userdata.img文件中
adb push
*将一个目录拷贝到模拟器中,包括子目录
adb push
* 将一个目录从模拟器中拷出来
adb pull
* 使得模拟器可以运行arm代码.
使用GNU/ARM Linux编译器编译你的应用程序就可以了
* 在模拟器里面运行shell,需要先运行模拟器
adb shell
*运行模拟器中的一个控制台程序
adb shell
*连接模拟器的控制台
telnet localhost 5554/6/8
运行C程序
参考文献
Native C “Hello World” working in emulator
groups.google.com/group/a ... wse_thread/threa...
Native C Applications for Android
benno.id.au/blog//11/13/android-native-apps
步骤
* 下载GNU/ARM编译工具
www.codesourcery.com/gnu_toolchains/arm/download.html
* 编写c/c++代码.
* 使用GNU/ARM Linux 工具创建一个应用程序,不使用动态链接库
ex. arm-none-linux-gnueabi-g++.exe -static -o hello HelloAndroid.cpp
* 启动模拟器
$SDK_ROOT/tools/emulator.exe
* 在命令行窗口运行 abd将编译好的hello程序放入模拟器的磁盘
adb push hello /system/sbin/hello
* 让hello文件变成可执行程序,不要使用 chmod ugo+x
adb shell chmod 777 /system/sbin/hello
* 运行hello程序
adb shell
cd /system/sbin/
hello
EXAMPLE HELLO WORLD CODE
//
// HelloAndroid.cpp
//
//
#include
using std::cin;
using std::cout;
using std::endl;
class MyName
{
public:
void getname( void );
void sayhello( void );
private:
char name[ 255 ];
};
void MyName::getname( void )
{
cout << “What is your name? ”;
cin >> name;
}
void MyName::sayhello( void )
{
cout << “Welcome ” << name << “ to the world of Android” << endl;
}
MyName name;
int main( int argc, char *argv[] )
{
name.getname();
name.sayhello();
return 0;
}
Android编译本地C++程序方法
在Android平台上程序以Java形式运行在Dalvik模拟器上,但Android作为一个Linux内核系统完全可以执行Navtive C++程序,主要的步骤如下:
1.下载ARM C++交叉编译器www.codesourcery.com/gnu_toolchains/arm/portal/subscription3057
2.编写本地C++代码,如Hello Wolrd,可以使用标准库STL,
android C编程技巧 及 C/C++开发测试
,
编译的命令行如下
arm-none-linux-gnueabi-g++.exe -static -oandroid123 android123.cpp
首先运行arm-none-linux-gnueabi-g++.exe程序-static 参数代表静态库,-o为输出名称android123,最后的android123.cpp为源代码。
3.运行模拟器,用cmd在sdkTools目录夏之星 adb pushandroid123 /system/sbin/android123
4.设置访问权限,通过Linux的Shell,在cmd下设置所有用户完全控制权限adb shell chmod 777 /system/sbin/android123
5.执行这个android123程序,输入adb shell cd /system/sbin/android123即可
在android平台上测试C/C++程序及库
int main( int argc, char *argv[] )
{
name.getname();
name.sayhello();
return 0;
android平台上带有标准C库,我们可以写个C程序来试试看能不能在上面运行。。。
首先下载并安装交叉编译工具GNU/ARM Linux gcc:
www.codesourcery.com/gnu_toolchains/arm/download.html
安装时 直接解压就行了,要设置好PATH环境变量。
简单的C代码:
test.c
#include
int main()
{
int i,j;
for(i=0;i<=10;i++)
{
for(j=0;j<=i;j++)
printf(”*”);
printf(”n”);
}
return 0;
}
用刚下载的交叉编译工具编译源代码:
# arm-none-linux-gnueabi-gcc test.c -o test -static
-static选项在这里是必须的,不然android平台就不运行此程序。
这也说明了此平台上的C/C++库是不能被C/C++程序动态连接的 。
进入tools目录,用adb工具下载到android平台,放到/data/data目录。
# ./adb push test /data/data
进入/data/data目录运行程序。
# cd /data/data
# ./test
*
**
***
****
*****
******
*******
********
*********
**********
***********
ok,It’s done !
C++程序一样的方法,只不过编译器换成:arm-none-linux-gnueabi-g++
附C++示例代码:
//
// HelloAndroid.cpp
//
//
#include
using std::cin;
using std::cout;
华为C&C08交换机号码变换小结 第3篇
随着业务的需要,号码变换运用得越来越广泛,而且其灵活度的需求也越来越高,因此需要理解号码变换的各种方法的使用才能灵活运用以解决实际问题。
1.C&C08;交换机中主要的号码变换方法简介
目前CC08交换机提供的主要的号码变换方式有号码准备、特殊号码变换、主叫号码分析、号首处理、中继承载等,可单独使用亦可搭配使用,在使用各种变换(特殊号码变换除外)前,需要首先定义一个号码变换索引以供各种变换方式引用,命令为ADDDNC,号码变换类型有改号、插号、删号等。下面对几种主要的号码变换方法加以说明。
1.1号码准备
号码准备时呼叫源的一个属性,如果一个呼叫源做了号码准备,则该呼叫源中包含的本局用户或入中继拨打被叫的时候,一旦达到预收号位长,就会对所拨打的被叫做一个统一的号码变换。
号码准备常用在一些专用呼叫源上对所有被叫号码作统一号码变换,如有些智能公话,为了让终端用户可以直接拨打被叫号码而不需要先拨智能接入码,可以单独增加一个呼叫源,进行号码准备,前插智能接入码,这样用户提及后就可以直接拨打被叫号码进行呼叫。
[使用方法]
步骤一:ADD DNC,增加变换索引。
步骤二:ADDCLLSRC或MODCALLSRC(增加新的呼叫 源或修改一个已有的呼叫源),“是否号码”准备设置为“是”,“号码变换索引”填入步骤一中定义的索引。
步骤三:修改相关用户或中继群的呼叫源为步骤二中的呼叫源。
1.2特殊号码变换
CC08交换机从5K版本开始增加了特殊号码变换的功能。通过在分析表中的呼叫字冠,号首集和“是否进行特殊变换”来标识并触发一个特殊号码变换。与普通变换的一个不同之处是特殊号码变换索引表是通过ADDSPDNC的,而普通号码变换是通过ADDDNC的。在号码分析表中的“特殊变换触发”长度确定了在分析表中拨号到第几位就开始触发特殊号码变换流程,特殊变换触发长度必须大于字冠长度,小于或等于最大号长。
特殊号码变换后的被叫将进行重新分析,CCB中的号码分析和计费都是采用变换以后的号码数据进行处理的。
[使用方法]
步骤一:ADDSPDNC,输入相应的字冠,变换类型也有改号、删号、插号等。
步骤二:ADDCNACLD或MODCNACLD(新增或修改呼叫字冠)“是否进行特殊号码变换“设置为“是”,填入“特殊变换触发长度”。
1.3主叫号码分析
主叫号码分析是根据某个特定的主叫号码(字冠)呼叫一些特定的被叫号码(字冠)所触发的号码变换。呼叫源码里面的号码准备只能对号码进行变换,而主叫号码分析可以对主叫号码和被叫号码同时进行选路和计费分组以及失败处理的变换。
主叫号码分析是在查询被叫分析表后进行的处理,查看被叫号码分析表中“主叫分析”标志是否为“是”若为“是”即进行主叫分析,主叫号码分析在6K版本及以前的版本中是不对变换后的被叫号码重新分析的,如要对主叫号码分析后的被叫号码进行重新分析,就需要通过与号首处理相组合,将号首是否的重新分析标志置为“是”,8K有是否重新分析的参数可以选择。
[使用方法]
步骤一:ADDDNC,增加变换索引。
步骤二:ADDCNACLR,在数据配置时输入:呼叫字冠、号首集、呼叫源码、主叫号码、主叫号码变换索引、被叫号码变换索引,可以襀对以下参数的修改,路由选择源码、源码、计费源码、主叫用户类别、主叫号码、被叫号码。
[注意]
(1)增加主叫号码分析,必须先定义呼叫字冠,字冠*65534#为主叫号码分析通配字冠,可直接输入。
(2)主叫号码分析表中路由选择源码为65535,失败源码为255,计费源码填255时表示还是使用变换前数据进行路由分析、失败处理以及计费情况。
1.4号首处理
号首处理也可以同时实现对主叫号码和被叫号码的变换,是根据某个被叫字冠进行的主叫号码和被叫号码的变换。除可对号码进行变换外,利用号首处理数据还可以实现拨打某些字冠做号首处理进行送音,并可通过参数控制送音是否计费可以通过号首处理做一些字冠的失败处理,利用号首处理还可以改变字冠的号首,实现一些特殊需求。
做号首处理中的重新分析设置为是,将对变换后的被叫号码重新进行被叫分析流程,这样可能导致在变换中的一此属性的改变如路由选择码、计费选择码等。
[使用方法]
步骤一:ADDDNC,增加变换索引。
步骤二:ADDPFXPRO该命令实现三种功能。
(1)号码变换,主叫或被叫号码变换标志为“是”,同时输入已经定义的主叫或被叫变换索引。
(2)送音,送音标志为“送音”或“号间送音”。
(3)转失败处理失败处理标志为“是”,同时选择相应的失败原因码。
[注意]
(1)号首处理中“新号首集”一项必须在“被叫号码变换标志”为“是”时有效,该域指定被 叫号码变换后的新号首集。
(2)每条号首处理数据只能完成号码变换、送音、失败处理中的一种功能。
1.5中继群承载
中继群承载是在出中继上实现的变换,以一个中继群为单位,对从这个中继群上出去的主被叫号码进行号码变换,受到中继群上的呼叫源、号首集等参数的制约,在利用中继群承载做号码变换的时候一定要注意呼叫源和号首集的正确性。
[使用方法]
步骤一:ADDDNC增加变换索引。
步骤二:ADDTGLD增加中继群承载索引。使用步骤一中定义的索引,可以对主叫变换也可以对被叫变换或对二者同时变换;还可设置中继占用点。
步骤三:ADDTGLDIDX增加中继群承载索引通过设置中继群呼叫源码号首集呼叫字冠等参数来确定修改的范围通过步骤二中定义的承载索引号来确定进行怎样的号码变换。
[注意]ADDTGLDIDX中呼叫源指的是需要设定中继承载的主叫的呼叫源而不是该中继群的呼叫源。
2.在实际工作中号码变换应用的要点说明
在做号码变换时常常会遇到变换不的情况同时对号码变换的处理有些疑问,下面是日常经常遇到的一此细节问题的说明。
(1)6K版本号码变换长度为31位6K以前的版本号码变换长度不能超过24位。
(2)所谓变换长度不能超过8位的意思是“新号码”的长度不能超过8位但“号码变换长度”没有这个要求。
(3)如果号码变换中规定了地址性质的变换,而原号码的地址性质与新地址性质桢则不进行变换,而且对后面的号码变换也不再执行。例如ADDDNC时“地址性质指示”选择为“国内号码”而要变换的号码的地址性质如果变换前已是“国内号码”则不进行变换。日常维护中许多变换不的原因就在于此。
(4)对于插号变换,若分析表中字冠的最小号长为A,则在进行插号变换时变换起始位置必须小于等于A;对于改号或删号变换,只要变换起始位置+变换长度小于等于字冠的最小号长就可以变换成功。
(5)号首处理仍是针对主叫号码分析前的号码进行处理。
(6)被叫号码分析表中原字冠对应的属性都不需要重新分析包括业务属性、权限选择、路由选择码、最小、最大号长、振铃延迟时间、计费选择码、紧急呼叫标志、目的地等都不进行重新分析。8K版本中增加了参数控制。
C/C++程序的运行时优化研究 第4篇
关键词:运行时优化,LLVM,java虚拟机
0 引言
随着计算机体系结构和硬件技术的发展, 应用程序的代码量逐年增加, 其程序行为在整个执行期间变化复杂并支持动态扩展和更新, 甚至有些模块需要多种语言协调实现。有些程序的运行时间主要集中在几个热点之上, 而有些程序的运行时间分布比较均匀。因此, 笔者认为支持对程序整个生命期的分析和转换是最大化所有程序的效率的有效手段。整个生命期的代码优化技术包含链接时的过程间优化、装载时的与平台相关优化、运行时的动态优化和空闲时的剖析 (profiling) 指导反馈优化。
通常, 我们把优化技术分成两类:静态优化和动态优化。静态优化技术的成熟, 使得静态优化对程序性能提升的空间越来越小;而动态优化目前主要集中在对动态语言程序如Java、Ruby、Python等的优化。C/C++程序是静态编译优化的典型代表, 文章以它们为讨论对象, 在研究java虚拟机中运行时优化机制的基础上, 结合LLVM架构讨论了面向C/C++程序的运行时优化技术。
1 静态优化和动态优化
从技术角度来看, 静态和动态优化技术不应该是相对立且竞争的, 应该综合利用静态和动态编译技术的各自特点, 在面向动态环境的同时, 能获取静态编译的优势。从发展的角度来看, 随着多核技术的发展, 运行时的剖析机制和反馈指导优化技术将会成为优化技术的主流。动态编译系统通常可以分为3类:静态多版本、参数化、运行时代码生成和动态重编译。
静态多版本技术是指编译器在编译时为指定的程序段利用不同算法或不同优化技术生成多个版本, 在运行时根据输入数据集或执行环境等选择一个最佳的版本执行。Autotuner工具进一步拓展该技术, 依据运行时的数据收集, 在版本之间进行切换。静态多版本技术受限于编译时的可见信息, 并且为了避免代码爆炸, 影响指令cache性能, 编译器产生的版本有限。
为了克服静态多版本的限制, 参数化技术允许编译器产生的代码可以根据程序执行的值进行重构。该技术多用于循环转换技术, 使许多标准的循环转换可以在运行时利用参数来决定是否采用。
运行时代码生成和动态重编译[6,7]在程序执行过程中, 利用程序以往的行为和执行环境, 针对程序关键路径进行优化, 生成新的可执行代码。和静态多版本以及参数化技术相比, 运行时代码生成和动态重编译能够更有效地利用程序运行时信息优化程序, 具有更大的灵活性和可扩展性, 重新生成的代码更适应当前的执行环境。动态重编译的关键是对热路径的识别以及收益和开销的计算。多线程技术使得动态重编译的开销变得不再重要, 比如Mihai BurceaD1等提出了面向OpenMP的运行时优化系统stOMP。
Java虚拟机中的运行时优化系统, 为我们研究如何支持C/C++的运行时优化提供了很好的学习平台。下面首先介绍java虚拟机中的运行时优化技术。
2 java虚拟机中的动态优化技术
动态编译技术按照目的可以分为两大类: (1) 选择性编译:主要关注何时选择程序哪一部分代码进行动态编译和优化; (2) 基于反馈的优化:利用剖析信息来指导动态编译器来做何种级别的编译优化。
2.1 选择性编译
基于一个被广泛认同的现实:程序80%的时间消耗在20%的代码上。很多虚拟机采用了选择性动态编译, 即将编译资源主要用在那些经常被执行的代码上, 这部分代码又被称作“热点 (hotspot) ”。动态编译器通常支持两种执行方式:一种是解释执行或无优化的编译器编译执行;一种是优化编译, 对热点路径进行重点优化。动态编译器中的编译优化工作是在程序运行状态中进行, 编译开销包含在程序的运行开销中, 对热点路径代码进行重点优化使得虚拟机没有把时间浪费在编译那些不经常执行的代码上。因此, 选择哪些代码段作为热点方法进行动态优化以及怎样权衡动态优化的开销和性能收益, 是每一个动态编译器需要考虑的问题。这却又是个NP完全问题。通常用公式:ΔToverall=Tcompile-N× (Tunopt-Topt) 来评估选择优化后的性能。其中ΔToverall表示由于动态优化而导致代码段运行时间的变化, Tcompile指动态优化编译所需的时间开销以及维护新版本可执行代码的开销, Tupopt是优化之前热点的平均运行时间, Topt是热点的新优化版本的平均运行时间。ΔToverall为正数表示动态优化的开销大于收益, ΔToverall为负数意味着收益大于开销。
通过上述分析可以看出, 热点代码段主要有两类:一类是运行时间很长 (Tunopt很大) 、一类是该部分代码被经常调用 (N很大) 。
计数和采样技术是选择性优化编译中的两类主要技术。计数方法是为每一个方法设置一个计数器, 用来记录方法的调用次数, 有时也可以用来获取循环的迭代次数;采样技术是周期性的中断系统, 记录当前调用栈中栈顶的一个或多个方法。基于对计数器和采样数据的简单分析, 系统中的决策模块决定某个方法是否是需要进一步优化的热点方法。许多系统采用的启发方式就是一个简单的分水岭策略:当方法的调用次数或者执行时间达到某个临界值时, 就对该方法进行重点优化。
2.2 基于反馈的动态优化技术
动态编译器不仅可以选择对哪些代码做编译或者优化编译, 而且还可以在程序的运行中采集程序的运行信息, 主要包括程序的循环深度、程序输入以及运行环境 (操作系统、体系结构) 等。利用这些运行时信息, 动态编译器可以更好地决定采用何种编译优化策略对程序进行编译优化。动态剖析技术使得多级优化编译成为可能, 每一级采用特定的优化策略, 热点的性能更容易得到提升, 这就是基于反馈的动态优化技术 (FDO) 。
基于反馈的动态优化技术主要通过对程序插桩, 利用插入的代码收集需要的程序信息, 然后将它们按照一定的格式组织起来进行分析, 从而指导优化器进行重编译。通常采用公式ΔToverall= (Tcompile+Tmeasure) -N× (Tunopt-Topt) 来评估基于反馈的动态优化效果。其中, Tmeasure表示收集、组织、分析剖析信息需要花费的时间。从公式可以看出, Tmeasure是一个影响动态优化性能的一个主要方面, 怎样减少Tmeasure是每一个基于反馈的动态优化技术的动态优化编译器需要考虑的问题。一个主要的原则是减少插桩代码的运行时间, 通常采用的方式是对早期未经过优化编译的版本进行插桩, 当收集到所需信息后立即重编译代码并执行新版本的代码。对早期代码进行插桩的主要好处是:
(1) 插桩带来的开销相对比较小;
(2) 越早获取剖析信息就可以越早地进行高效精确的重编译。
不过, 这种方式也容易导致后续的编译策略错误, 一方面早期获得的信息可能不会准确反映程序的主要特征, 程序的运行特征在中后期可能会发生很大变化;另一方面很多有用的程序信息很难在早期未经优化的代码中获得, 例如Inline优化会改变程序结构, 导致后期程序的热点路径发生变化。
3 C/C++的runtime优化研究
对于C/C++应用程序, 通常采用静态编译器针对运行平台编译优化成可执行程序, 没有运行时优化。静态编译器对程序的静态分析, 只能得到程序的近似解, 不能符合所有情况;静态优化器也有采用剖析技术进行重编译, 其采样的输入数据有限, 有些情况下, 程序会变得更慢。因此, 如何运行时的优化来弥补静态编译的不足, 成为当前研究的热点。
3.1 支持C/C++运行时优化的LLVM编译架构
LLVM架构是美国伊利诺斯大学开发的开放源代码编译器架构, 它把程序的生命周期分成5个阶段:编译 (compile) 、连接 (link) 、加载 (install) 、运行 (run) 和空闲 (idle) 。LLVM支持程序整个生命周期的分析和优化, 其系统框架图如图1所示。
静态编译器前端生成LLVM的中间表示IR;链接器基于IR进行链接时的优化, 例如过程间优化;在程序的链接或加载阶段生成特定平台的本地码;为了支持运行时优化, LLVM的IR也保存在本地码中。本地码生成器在代码生成阶段插入轻量级的插桩指令, 用于程序运行时的热点探测, 优化器对程序热点进行运行时优化。运行时的剖析数据收集面向终端用户的程序行为, 依据剖析数据在程序的空闲阶段对程序进行优化并链接成新的可执行程序。
LLVM提供两种代码生成方式: (1) 静态生成高效的包含IR的可执行代码; (2) 生成LLVM的字节码 (bytecode) 。LLVM提供JIT动态优化器和可选的字节码解释器来运行字节码。JIT以函数为单位进行运行时的优化和代码生成。第一种代码生成策略主要面向程序空闲阶段的重编译, LLVM中的优化器依据程序执行时收集的剖析信息对IR进行重优化并链接成可执行程序。这种优化策略的问题是基于上一次运行时剖析信息的优化不一定适用于下一次程序运行。第二种方法使得程序执行过程类似于java的动态优化过程。当程序执行时, 通过剖析方法定位出程序的热点路径:静态插入的插桩代码能够定位出代码中经常执行的循环区域, 运行时的插桩库在该循环区域进一步插桩找出区域内的热点路径。当热点路径找到后, JIT拷贝该路径的LLVM的字节码, 并进行优化生成本地码后放到软件管理的路径cache中, 在原代码中插入分支指令跳转到新生成的本地码。运行时的优化对时间比较敏感, 需要计算优化后的收益大于优化的时间开销, 并且程序的第一次执行需要的时间开销比较大。
3.2 支持C/C++运行时优化存在的问题
如何发挥多核多线程的技术优势, 克服LLVM编译架构中存在的技术不足, 是研究设计支持C/C++程序运行时优化的一个难点。如何使程序从热路径的当前执行点切换到优化后新版本的执行点, 是一个值得研究的问题。目前LLVM采用在程序空闲阶段对热点或整个程序进行重编译并链接成可执行程序, 在程序下一次执行时, 调用该新版本的程序。这种策略的效果依赖于程序前后两次执行的输入集的相似度, 它和基于静态剖析技术反馈的重编译技术没有本质区别。
4 模拟测试
对静态优化和动态优化的性能比较测试, 我们用java程序SPECjvm98基准测试级进行模拟, 测试平台如图2所示:静态优化编译器采用gcc 4.2.0版本3级优化, java虚拟机采用JDK1.6缺省运行模式。
SPECjvm98基准测试级共8个应用程序, 其中_200_check用于java的功能检测不作为性能比较, _228_jack程序没有通过gcj测试, 所以我们只对其中的6个程序进行比较测试, 测试结果如表1所示。
表1虽然不能说明C/C++程序静态和动态优化的性能比较, 却很好地说明了java程序动态优化的优势, 6个程序无一例外, JDK的平均性能比gcj的静态优化性能高81.22%。
5 结束语
文献[9]通过热点路径剖析发现大约的20%路径是不连续的, 甚至不在一个虚拟内存页内, 因此, 笔者认为运行时优化的重点是对热点路径的探测和优化以提高代码的局部性和编译指导的分支预测的正确性。随着多核技术的发展, 运行时的优化开销将被忽略, 编译优化器将成为应用程序执行中的一个服务线程。对C/C++程序的运行时优化技术的研究为提高C/C++程序性能开辟了新的途径。
参考文献
[1]CHRIS LATTNER, VIKRAM Adve.LLVM:A Compilation Frame-work for Lifelong Program Analysis&Transformation.In2th Int’l Symposium on Code Generation and Optimization, 2004.
[2]MICHAEL J.Voss, Rudolf Eigenmann.High-Level Adaptive Program Optimization with ADAPT.PPOPP′01, 2001.
[3]M.BYLER, J.R.B.DAVIEW, C.HUSION, et al.Multiple Version loops.Int’l Conf on Parallel Processing, 1987.
[4]M.FRIGO, S.G.JOHNSON.The Design and Implementation of FFTW3.Proceedings of the IEEE, vol.93, no.2, 2005.
[5]RAJIV GUPTA, RASTISLAV BODIK.Adaptive loop transformations for scientic programs[J].In IEEE Symposium on Parallel and Dis-tributed Processing, October1995.
[6]R.GUPTA, R.BODIK.Adaptive Loop Transformations for Scientific Programs[J].IEEE Symp on Parallel and Distributed Processing, 1995.
[7]X.ZHANG.Z.WANG.N, GLOY, et al.System Support for Automatic profiling and Optimization[J].Proc16th ACM Symp on Operating Systems Principles, 1997.
[8]V BALA, E DUESTERWALD, S.BANERJIA, Dynamo:A Transpar-ent Runtime Optimization System.ACM SIGPLAN2000Conf on Programming Language Design and Implementation, 2000.
[9]DUANE MERRILL, KIM HAZELWOOD.Trace Fragment Selection within Method-based JVMs.In4nd Conference onVirtual Execution Environments, 2008.
C/C++面试题目 第5篇
将一个文本文件的内容按倒序打印出来
解:#include
main
#define m 20
{
char str1[m];
int i;
FILE *fp1;
if((fp1=fopen(“file2.c”,”r”))==NULL)
{
printf(“cannot open the filen”);
exit(0);
}
i=0;
While(!feof(fp1))
{
strl[i]=fgetc(fp1);
i++;
}
while(i!=0)
{
i–;
Putchar(str1[i]);
}
fclose(fp1);
C++/C 第6篇
“创建企业需要抓住机遇。在过去的几年里,软件产业孕育着新的发展机会,在中国,经济的发展与强大,尤其是西部开发的浪潮,提供了巨大的市场潜力。”陈健在谈到当初的创业时如是说。
1997年,身在澳大利亚的陈健以多年从事软件研究的经验,敏锐地认识到互联网软件对软件行业的影响,立志作一番事业的他开始了创业历程。在创业初始的探索阶段,陈健在北京代表澳洲Netec Pty.Ltd公司与日后鼎鼎大名的新浪网以“项目+技术”的模式开始了合作,同时,公司自身创办了网站“清韵书院”,从而开始了国内业务的蹒跚起步。
1998年,随着中国的改革开放进一步走向深化,西部大开发战略的初步确定,陈健进一步确立了在国内创业的决心。在对深圳、北京和西安考察后,陈健决定将公司注册在西安高新区,公司的战略发展中心从澳洲、从北京转移到了西安。“我们需要一个坚实的创业基地。”陈健后来解释道。
选择西安,陈健不仅是因为西安有着丰富的科教资源和大量的软件人才,更因为西安高新区的创业环境的优良。“归国留学人员其实是很幸运的一部分人。恩科公司一开始便获得了国家、省、市政府以及西安高新区和留学人员创业园的大力支持,这种支持是多方面的,不仅仅是在资金方面和企业创立初期,更多的是在企业的成长过程中关心企业的发展,让我们时时感受到对我们的期望和支持。”“在创业园我们可以专注于产品的开发,并得到很多企业发展方面的帮助:从创业种子资金的支持,企业发展中需要办理的各种事务,到企业发展过程中所需要的培训和咨询等等,这些都是我们每天能够切身感受到的支持,一句话,在创业园中的经历,使得我们最快和最好地发展,并且避免了许多不必要的弯路。”对于优良的创业环境和企业自身努力两者的关系,陈健形象地将其比喻为99℃+1℃。
西安恩科网络技术有限公司1998年9月诞生。在公司创立初期的技术发展方向上,恩科公司提出“网络就是软件”的理念,在1998年全球一片网络热,业内人士大都关注网站内容的时候,这一理念显得是那么“前卫”!基于这一理念,恩科公司将自身的技术发展方向定位在基于互联网技术的应用支撑软件上,而市场营销则定位在国内的政府部门、大企业和网络公司方面。1999年恩科公司获西安市科委“高新技术企业”认定。随着公司的软件主导产品互联网维护管理平台SiteMaster1.0、Sitemaster2.0的相继研发,恩科公司形成了年70万元的销售规模。企业从初期的“十来个人、七八条枪”发展到具有技术、市场等相关业务部门的创业小公司规模。
2000年初,正当恩科公司拳头产品Sitemaster3.0研发受到了后续资金匮乏的困扰之际,西安高新区为创业者推出的“种子基金”使恩科公司看到了希望。恩科公司试着向基金提出了申请,高新区运作“种子基金”的创新投资公司对恩科公司的项目进行了仔细评定审核,很快将恩科公司纳入了“种子基金”的扶持对象,同时恩科公司项目还被西安高新区管委会列为西安高新区管委会2000年重大科技成果转化项目。随即而来的50万元“种子基金”使得恩科公司平台先锋(互联网维护管理平台)SiteMaster3.0在当年的5月份得以顺利开发完成并在2000年第三届中国国际软件博览会上一举获得了金奖。
2000年的下半年,恩科公司的又一高科技软件产品互联网应用开发平台SiteDeveloper开发蓝图已经基本形成,然而公司业务的不断扩大让公司依然感到资金困乏。在西安留学人员创业园的建议和组织下,恩科公司向国家科技部的“科技型中小企业技术创新基金”提交了创新基金申报项目表,2000年10月,恩科公司继年初的“种子资金”后,又一次以自己产品的独特优势顺利获得国家创新基金80万元的无偿资助,公司发展关键时刻获得政府资金的支持,从而为恩科公司日后的发展打下了坚实的基础。2000年底,恩科公司的销售额突破百万。
2001年恩科公司逐渐走出了创业最艰苦的日子,当初珍贵的50万元种子基金也顺利退出。进入了市场扩张期的恩科公司的产品已经自成体系,恩科公司互联网应用开发平台SiteDeveloper被列为陕西省2001年重大科技产业化项目;在第四届中国国际软件博览会上恩科公司以软件产品网络办公与信息管理系统EIP/SiteOA又一次获得了金奖。三年的创业,三年的持续研发,恩科公司逐步形成了以互联网应用为特征的具有自主知识产权的Site产品体系,即平台先锋(互联网维护管理平台)SiteMaster、互联网应用开发平台SiteDeveloper、网络办公与信息管理系统EIP/SiteOA、网站远程自主建设与维护管理系统SiteASP软件、电子商务应用系统SiteBusiness。
随着企业产品开发的不断完成,恩科公司的企业规模也在高速成长。创业3年,恩科公司已成功地为包括中国惠普、中国国家图书馆、国家经贸委、西安高新区、西安留学人员创业园、陕西省经贸委、陕西机械设备进出口公司等百家客户提供了软件开发、网站建设和信息化应用系统建设等服务。公司2001年的销售额较之上年翻了数番,企业员工已近百人,已形成了自己的技术团队、市场营销团队。恩科已经从创业期的小公司发展成为一个初具规模、开始企业化发展的专业软件公司。公司在2001年进一步加强了团队建设,在创业园的协助下,恩科公司成功地吸引了长期在国外IT行业工作,具有丰富IT管理经验的留澳硕士王涛的加盟,从而使公司的团队管理迈上了一个新的台阶。
浅谈C语言及C语言学习方法 第7篇
学习的目的必须明确, 目的不明确就没办法学好, 学习起来就没有动力。
C语言是目前被广泛使用的教学语言, 功能强大、应用广泛, 集高级编程语言和汇编语言的特点于一身, 既可以用来编写系统软件, 也可以用来编写应用软件, 例如要进行一个嵌入式项目, 或者需要进行服务器端开发, 或者写一个性能相关的组件等等, C语言都是比较好的选择。目前市面上的许多软件也是在C语言的平台上开发出来的, 因而学好C语言具有很重要的意义。另外C语言是工程实践性很强的语言, 是实实在在从项目需要中产生的, 伴随着Unix的兴起而流行, 语义简明清晰, 功能强大而不臃肿, 简洁而不简单。另外也可以在C++的使用过程中使用C语言的思考方式, 汲取C语言简洁明快清晰的设计思路, 对编程设计水平会有很大的提高。
C语言简洁紧凑、灵活方便、可移植性强, 程序的书写比较自由, 语法限制不严格。C语言相比C++的优点之一就是不会在私底下产生一些莫名其妙的额外产物, 这点在编写操作系统这类要求优化性能的情景下尤为重要。C语言本身只提供必要的语言特性, 复杂一点的功能如文件处理、数学计算等等都以库函数方式提供。这种简洁性也让C语言的可移植性、便携性特别优秀, 也使得很多嵌入式系统依然使用C语言作为主要编程工作语言。
C语言运算符丰富, 功能强大, 应用广泛。它是一个比较少见的应用领域极为广泛的语言。在编程过程中可以使用各种运算符, 可以实现其他很多高级语言无法实现的功能。另外还具有强大的图形功能。
C语言程序层次分明, 设计思路清晰。结构式的语言特点使代码和数据分割, 层次清晰, 便于使用和维护。
由于C语言的这些特点和优势, 使其成为理工科必修公共课之一, 也是编程者首先学习的程序设计语言。
二、如何学习C语言
学校十分重视C语言的学习, 越来越多的人想掌握这门语言, 但是大部分人反应C语言抽象难以理解, 枯燥难以掌握, 经常是一个学期下来毫无收获, 对C语言没有一个整体的认识, 连一个简单的小程序也写不好。其实初学者认为C语言复杂难学是人之常情, 况且C语言又需要一定的数学基础, 这对高职高专的学生来讲, 是有些困难。然而只要具备一定的编程经验, 再掌握一些编程技巧, 就可以有效的提高编程的效率和能力。
1、多看书, 看懂理解基本知识
教材一般都涵盖了C语言的方方面面, 从基础知识到应用实例都讲述的非常全面, 举例也都很经典。不要小看一本教材, 其实教材是学习的第一步, 是基础, 只有从基础开始, 才能进一步提高能力, 若无基础, 何谈提高和技巧。
C语言涉及的主要内容包括:运算符及数据结构;变量的作用域和存储类别、函数及函数的调用、数组及数组的应用、指针、文件、链表等。学习C语言应首先较好的掌握好这些重点难点内容, 以及一些细节、易错的地方。这会使对C的运用更加得心应手。
2、多动手, 在实践中提升能力
C语言是实践性很强的课程。只有通过大量的上机练习, 亲自动手试一试, 才能消化理论知识, 加深对理论知识的理解, 像基本数据类型, 三种基本结构 (顺序结构、循环结构、选择结构) 这些东西不能死记硬背, 亲自动手编写一些简单实例更好。
一开始的练习, 可以亲自动手把教材上的例子打一打, 运行一下程序是否正确, 有什么问题自己亲自调试。在这一步要基本掌握C语言编程设计的基本方法, 包括新建、打开、保存、关闭C程序, 熟练地输入、编辑C程序, 养成良好的C语言编程风格。
之后可以在老师的指导下, 编写一些小程序, 这类任务一般是在第一步输入的C程序的基础上进行一些修改, 扩充其功能而来的, 相对来说是比较简单的任务, 基本是模仿教材范例编写, 举一反三。编写好之后运行一下程序, 看一看程序结果发生了什么变化, 分析结果变化的原因, 加深对所学知识点的理解。事实上这和验证教材上的实例是同步进行的, 照搬教材实例可加深知识的记忆, 改造教材实例可加深对知识的理解, 二者相辅相成, 相互促进。
无论是编写怎样的程序, 都要多调试程序, 以增强自己调试程序的能力, 教材中都有C语言初学者易犯的错误, 按照易出错的类型, 将教材中的正确的程序改成错误的程序, 运行一下程序, 看出现的错误信息提示, 并记下错误信息, 再将程序改成正确的, 运行一下程序。这样反复修改, 就能够学习C语言程序发生错误的原因和修改错误的能力。修改C语言语法错误时要注意以下两点:
(1) C语言简洁紧凑、灵活方便, 程序的书写比较自由, 语法限制不严格。因此错误信息定位不是特别精确。例如, 当提示第50行发生错误时, 如果在第50行没有发现错误, 从第50行开始往前查找错误并修改之。
(2) 一条语句错误可能会产生若干条错误信息。只要修改了这条错误, 其他错误会随之消失。一般情况下, 第一条错误信息最能反映错误的位置和类型, 所以调试程序时务必根据第一条错误信息进行修改, 修改后, 立即运行程序, 如果还有很多错误, 要一个一个地修改, 也就是说, 每修改一处错误要运行一次程序。
调试程序是一种实践性很强的事, 光纸上谈兵是永远学不会调试程序的。面对错误提示, 能快速发现错误并改正错误, 是需要实践、实践再实践积累出来的。
3、多利用网络资源
毕竟老师讲授的知识有限, 课堂学习时间有限, 自己可以在课余时间通过网络获取更多更广的资料。比如想全面了解C语言的来龙去脉, 比如想弄清很多编程术语的解释, 都可以在网上获取答案。也可以注册一个编程者论坛或是编程者联盟的账号, 当你对某个函数或者关键字不是很理解的时候, 就可以去搜索资料, 与其他程序员交流经验, 看看别人是怎么使用的。当你遇到无法调试的问题时, 可以在网上找到问题的解决方案, 获取到别人的帮助, 这对于学习非常便利。
4、多看代码, 研究典型的C语言程序
学习编程也需要大量阅读名家经典代码, 在看代码的时候, 要注意分析别人是如何运用编程技巧的, 看懂别人是如何解决问题的, 研究它的实现方法和程序设计技巧, 提高自己的程序设计能力。要对程序中数据结构的运用和算法进行总结和分析。初学编程者, 多读、多编、多模仿, 熟悉了, 便能编出自己的程序了。
C++/C 第8篇
如今打印机城市正流行瘦身风潮, 因此形体课格外受到佳能彩色学院的学生欢迎。要想保持紧凑纤巧的身材必须要经过瘦身训练。佳能LBP 7010C和7018C在这门课程上花费了大精力, 身材练就得相当小巧而灵活, 可以轻松放地在拥挤的办公桌上找到属于自己的一席之地。很显然, 这对双胞胎必然在形体课程上得到了高分。
在打印机城市里, 要想成为明星, 单靠身材还不够, 更重要的是要能打印出足够好的打印效果。因此学好化学课就显得尤为重要。今天, 佳能LBP 7010C和7018C这对双胞胎要学习的是制造出佳能S墨粉。要知道, 这可是一种相当棒的墨粉, 该类型墨粉颗粒中包裹着蜡滴微粒, 能实现无油定影, 提升彩色打印质量。另外, 这种近似球形的碳粉颗粒相比传统不规则形状的碳粉颗粒, 在实现更高彩色打印质量的同时, 可以实现碳粉的消耗量降至最低。实验室里, 俩人热火朝天地在研制, 最终收获了一流的佳能S墨粉。
体育课也是他们的选修课之一, 因为这对双胞胎都希望能让自己的打印速度更快一些。跑道上, 两个人先后追赶, 互不相让, 劲头十足。经过不懈的努力, 他们终于达到了目标彩色打印4ppm、黑白打印16ppm, 而且首页输出时间的表现也相当不错!
C++/C 第9篇
发散性思维 (亦称扩散性思维或求异思维) , 是一种充分发挥人的想象力, 突破原有知识圈, 从一点向外辐射思考, 并通过知识、观念的重新组合, 寻找更多的设想、答案或方法;是一种创造性思维, 具有流畅性、变通性和独创性等特点。[1]
在C/C++程序设计学习中, 很多同学对给出的问题的理解往往思路单一, 程序设计理念不深, 或对程序的基本结构、语法等基础知识掌握不够, 这会导致不能解决给定的实际问题。[2]本文通过发散性思维对一道经典题算法实现的讨论, 旨在激发、提高同学的编程兴趣。
问题:给出中间行行号 (例如n=4) , 打印如图1所示的钻石型图案。
1 利用基本流程控制实现
利用程序设计的基本流程控制 (顺序、选择、循环结构) , 对该例做如下分析, 如图2所示。
设该图案的中间行行号是n, 将图案在控制台模式下, 中间列在屏幕上第Mid列输出, 即图案关于第n行对称, 关于第Mid列对称输出。先将图案分成图中的 (1) 、 (2) 两部分。分析第 (1) 部分。设控制行变化的循环变量为i, i从第1行变化到中间行n (第n行) 。则对于第i行的输出的关键是计算图案两端的’*’在屏幕上的列序号, 计算结果是:第i行最左侧的’*’在屏幕上的列序号是Mid-i+1, 最右边的列序号是Mid+i-1, 该行共有2i-1个’*’。在该行左侧可输出空格, 以达到格式控制的目的。先输出从屏幕第1列到第Mid-i列共Mid-i个空格, 随后连续输出2i-1个’*’即可完成图案上半部分, 代码如下:
for (i=1;i<=mid N;i++) {//输出上半部分
for (j=1;j<=Mid-i;j++) cout<<'';//输出Mid-i个空格
for (j=1;j<=2*i-1;j++) cout<<'*';//输出2i-1个’*’
cout<
}
对于图案的下半部分, 从第n+1行开始, 到第2n-1行, 每行依次递减2个’*’, 每行亦关于Mid列对称。为简化处理, 重新处理行号, 此时设循环变i的初值是n-1 (第n+1行) , 终值是1 (第2n-1行) , 即i从n-1变化到1。从图中可以看出, 第i行左侧仍需连续输出共Mid-i个空格, 随后连续输出2i-1个’*’。由此可得下半部分 (2) 的实现代码:
for (i=mid N-1;i>=1;i) {//输出上半部分
for (j=1;j<=Mid-i;j++) cout<<'';
for (j=1;j<=2*i-1;j++) cout<<'*';
cout<
}
2 利用数组实现
数组是由具有相同数据类型的、且一定顺序关系的元素所构成的集合。本问题可以看成是由’*’和空格两类元素按一定规律所构成的一个二维数组。
2.1 空格填充后, 置换相应元素为’*’
设图案的中间行行号为N, 则该图案共有2N-1行, 2N-1列, 此时可以考虑用二维数组来处理。为处理方便, 可设数组为a[2*N][2*N], 其中第0行, 第0列的元素不用。首先, 用空格初始化数组a中的每个元素;其次, 将该数组a中相应元素的空格再置换成’*’;最后, 输出整个数组。此时图案在数组a中的中间行行号是N, 中间列的列号也是N (图3) 。
对于图3中 (1) 所示的上半部分, 当行循环变量i (第i行) 在第1行与第N行间变化时, 第i行最左边的’*’在数组a中的元素是a[i][N-i+1], 最右边的’*’在数组a中的元素是a[i][N+i-1]。此时只需把第i行从第N-i+1列到第N+i-1列中的元素换成’*’即可。对于下半部分 (2) , 设置行号i的变化范围是从1 (第N+1行) 到N-1 (第2N-1行) , 则第i行中需转换的元素是a[N+i][i+1]到a[N+1][2*N-i-1]。由以上分析可写出该该问题的实现代码, 如下:
int N=8;//行对称轴的行号
int i, j;
char a[2*N][2*N];
for (i=0;i<2*N;i++) //用空格填充整个矩形
for (j=0;j<2*N;j++) a[i][j]='';
for (i=1;i<=N;i++) //输出上半部分
for (j=N-i+1;j<=N+i-1;j++) a[i][j]='*';
for (i=1;i<=N-1;i++) {//输出下半部分
for (j=i+1;j<=2*N-i-1;j++) a[N+i][j]='*';
2.2 对2.1的改进
对于方案2.1, 在处理下半部分 (2) 时, 计算稍显麻烦, 此时可借助于图2的思路, 让行变量i从N-1 (第N+1行) 到1 (第2N-1行) , 如图4所示。此时该行中应置换的元素的范围是a[2*N-i][N-i+1]到a[2*N-i][N+i-1]。实现代码如下:
int N=8;//行对称轴的行号
int i, j;
char a[2*N][2*N];
for (i=0;i<2*N;i++) //用空格填充整个矩形
for (j=0;j<2*N;j++) a[i][j]='';
for (i=1;i<=N;i++) //输出图案的上半部分
for (j=N-i+1;j<=N+i-1;j++) a[i][j]='*';
for (i=1;i<=N-1;i++) {//输出下半部分, 从第2N-1行开始
for (j=N-i+1;j<=N+i-1;j++) a[2*N-i][j]='*';
2.3 先填充’*’后置换
对于图3和图4所示的两种算法, 亦可先用字符’*’填充整个矩阵, 然后用空格替换上下左右四个角的等腰直角三角形中除斜边以外的所有’*’, 即可得到中间的图案, 如图5所示。算法如下:
for (i=1;i<=N;i++) {//输出上半部分
for (j=1;j<=N-i;j++) a[i][j]='';//上左三角形
for (j=N+i;j<=2*N-1;j++) a[i][j]='';//上右三角形
}for (i=1;i<=N-1;i++) {
for (j=1;j<=i;j++) a[N+i][j]='';//下左三角形
for (j=2*N-i;j<=2*N-1;j++) a[mid N+i][j]='';//下右三角形
}
3 递归函数实现
一个函数在它的函数体内直接或间接调用自身的过程称为递归调用, 这种函数称为递归函数。递归是设计和描述算法的一种有力的工具, 它在很多复杂算法中被经常采用。[3]本题亦可采用递归思想来完成。[3]算法设计如下:
int count=1;//重复画空格或’*’的计数器
int i=1;//控制行的变化
int flag=0;//第i行中画空格或*的标识
int mid Col=20;//图案列对称轴位于屏幕上第20列
void Diamond Recursion (int n) {//n是图案行对称轴的行号
if (flag==0) {//flag=0时, 输出空格
if (count<=mid Col-i) {cout<<'';count++;}
else{flag=1;count=1;}//输出空格结束, 置flag=1
Diamond Recursion (n) ;//递归
}
else{//flag=1输出’*’
if (count<=2*i-1) {cout<<"*";count++;Diamond Recursion (n) ;}
else if (i
cout<<"n";flag=0;count=1;i++;Diamond Recursion (n) ;
}
else if (n>1) {//当行变量i已经超过第n行后
cout<<"n";flag=0;count=1;n;i;Diamond Recursion (n) ;
}}}
4 算法复杂度分析
对于图2的算法, 主要是完成输出功能, 问题的规模与中间列Mid及中间行号N有关, 其所需的时间复杂度为:Mid* (2n-1) +n*n。对于图3到图5的算法复杂度, 除了输出图形的复杂度 (Mid+N) * (2N-1) 处, 还有赋值: (2n-1) (2n-1) , 置换n*n+ (n-1) (n-1) , 因此就时间复杂度而言, 图2的算法较图3到图5的算法效率高, 也较递归法的调用高。但在数组中, 处理较为方便。
5 结束语
在对该问题的处理中, 除以上方法外, 还可以用诸如一元数组等其它思路来完成。总之, 在程序设计课程的教学中, 通过对同一问题的发散型分析, 不仅巩固了学生的基础知识, 还拓展了学生的编程思路, 提高了学生的编程兴趣, 收到了良好的教学效果。
参考文献
[1]张东, 龚晓娟.发散性思维与一题多解[J].新疆职业大学学报, 2004 (1) :79-80.
[2]马苗, 田红鹏.“面向对象程序设计与C++”教学中的问题与思考[J].计算机教育, 2008 (6) :81-82.
C++/C 第10篇
关键词:C语言,scanf函数,格式输入
C语言本身不提供输入语句, 输入操作是由函数实现的。scanf函数就是最常用的一个, 它是C语言函数库中的“格式输入”函数。
C语言中格式输入的规定比较繁琐, 用得不对就得不到预期的结果, 而输入又是最基本的操作, 几乎每一个程序都包含它。在这几年的教学实践中, 我发现不少学生由于掌握不好这个知识点而浪费了大量调试程序的时间, 也为进一步学习造成了障碍, 因此, 这个知识点就成了教学的重点和难点。不过根据这几年的教学经验, 我觉得只要遵循规律, 精讲多练, 学生完全能够掌握这个知识点。下面我将这方面的体会进行一些归纳和总结。
首先, 要准确讲解scanf函数的一般格式, 这是教学的前提和基础:
scanf函数一般格式是:scanf (格式控制, 地址表列) ;
“格式控制”是用双引号括起来的字符串, 它包括两种信息:
a.格式说明, 由“%”和“格式字符”组成, 它的作用是将输入的数据转换成指定的格式。
b.普通字符, 即需要原样输入的字符。
“地址表列”是由若干个地址组成的表列, 可以是变量的地址, 或字符串的首地址。
如输入dog坻
则‘d’送给a, ‘’送给b, ‘o’送给c, 因为%c只要求读入一个字符, 后面不需要用空格作为两个字符的间隔, 因此‘’作为下一个字符送给b。
d.在输入数据时, 遇到以下情况时该数据认为结束。
(1) 遇空格, 或按“回车”或“跳格” (Tab) 键。
(2) 按指定的宽度结束, 如“%3d”, 只取3列。
(3) 遇非法输入。
e.可以指定输入数据所占列数, 系统自动按它截取所需数据, 如:
系统自动将123赋给a, 将456赋给
b, 此方法也可用于字符型:
如果从键盘连续输入3个字符abc, 由于ch只能容纳一个字符, 系统就把第一个字符'a赋给ch。
f.输入数据时不能规定精度, 例如:
这是不合法的, 不能企图用这样的scanf函数输入以下数据而使a的值为12345.67.
最后, 还要补充的一点就是在使用C语言库函数时, 要用预编译命令“#include”将有关的“头文件”包括到用户源文件中, 在头文件中包含了与用到的函数有关的信息。但考虑到scanf函数使用频繁, 系统允许在使用此函数时可不加#include命令。
C++/C 第11篇
罗技网络摄像头C110/C170
两款产品在外形上几乎没什么区别,只是130万像素的C110采用磨砂外观,而500万像素的C170则是镜面设计。它们都支持即插即用特性,连接至计算机的USB接口之后,仅需在聊天工具中简单设置即可投入使用,得益于Logitech Fluid Crystal和Logitech Right Sound技术,两款网络摄像头均可提供清晰的画面和完美的音质——前者通过独特的算法,可实现自动调节捕捉帧率、颜色和清晰度,即便是照明环境不理想的情况下也可提供良好的视频效果;后者则可以识别使用者和被通话者的声波,在不同人发出声音讯号时过滤重复声波,从而令通话更加清晰,且双方可同时交谈,不会像其他低端产品那样在一方讲话时屏蔽另一方的声音。
C110/C170还支持XVGA视频录制,使用者可在录制简单的视频并在网络上与朋友分享;其通用固定夹可令两款产品同时适用于笔记本电脑或LCD显示器,也可放置于书架或桌面。
罗技无线键盘K270
这是我个人认为性价比最出色的无线键盘产品——在仅129元的价格基础上,K270提供了支持罗技优联技术的迷你接收器、可在10米内自由使用的2.4GHz无线技术、多个排水孔提供了良好的防泼溅功能、包括多媒体控制和启动应用程序在内的8个快捷键、耐用的紫外线镀膜按键,以及长达2年的电池寿命。
C++/C 第12篇
目前,许多高性能的系统程序或应用程序,都是采用C/C++语言实现的。但是,由于C/C++语言的支持库在本质上存在潜在的安全缺陷,使得基于C/C++的代码很容易引入若干因自身缺陷而导致的严重的安全隐患。尤其是用C/C++语言设计的网络程序,在信息化的互联网平台上,黑客往往利用这些安全问题产生的漏洞来绕过安全策略,以达到网络攻击的目的。因此,保障这些程序的安全是构造安全主机(或网络)环境的重要前提,本文设计并实现的“面向C/C++代码的漏洞检测系统”就是通过对源代码进行全面的检查,发现系统存在的潜在威胁,达到预防的作用。它具有自动化程度高、分析速度快等优点。
1 漏洞检测系统的基本原理
漏洞检测系统的基本原理是:通过对C/C++程序的源代码扫描,根据已知的C/C++中有潜在危险的API,或用户自定义危险级别的函数使用,利用词法分析的方法,对源代码进行词法分析,将对源代码词法分析的结果在不安全函数库指导下进行分析,找出潜在的漏洞,并给出相关的警告、提示信息或优化代码的解决方案。
为了实现系统的扩展性,系统内部设计了不安全函数库,使用者可以根据自身专业知识扩展不安全函数库,来满足不同层次程序员的需要。
2 漏洞检测系统的总体设计
本漏洞检测系统主要基于源代码检测程序漏洞,通过代码的扫描,发现具有潜在漏洞的函数的调用,给出合理化建议。主题思想就是逐字查找代码中使用的函数以及函数的调用方式,将其与不安全代码库中的函数进行匹配比较,相同特征的函数将被系统标记,最终结果形成文件反馈给系统的使用者。
2.1 系统模块组成
系统如图1所示。
读入代码文件模块 读入C/C++代码文件,保存相应的代码文件名和文件数量,以备扫描模块再次读入代码文件进行相应的扫描;
初始化模块 导入不安全代码函数库,为扫描模块准备函数规则,同时进行相关参数的初始化工作和内存分配;
扫描模块 根据读入代码文件模块保存的代码文件名和文件数量,打开相应的源程序文件或文件组,按逐行读取的方式,匹配查找源文件中的函数,并取得其使用参数,将查找得到的分析列表保存在内存中,根据已知的函数库,在不安全函数库的指导下,查询相关函数的不安全用法,记录可能存在安全漏洞的函数和相应函数所在的行号、不安全等级等信息,并给出相应的指导意见,传递给后处理模块处理;
后处理模块 根据扫描模块得到的结果,向用户提交扫描结果;
库函数编辑模块 主要用于编辑不安全函数库,同时提高检测系统的可扩展性。用户可以根据自身的专业知识或某些特殊要求,拓展不安全函数库或提升某些不安全函数的危险度,可以增加其他语言的方法调用,实现系统的可扩展性。
2.2 系统处理流程
系统首先读入C/C++代码文件,进行一系列的初始化工作,并导入不安全函数库文件,创建不安全函数库链表;其次扫描源程序,将源程序代码分块,可以分为预编译部分、空白行、普通代码块、注释块、常规字符串等五类,主要对普通代码段函数的用法进行词法分析,抓取代码中使用的函数及其参数,放入待分析函数变量列表中;再次遍历不安全函数列表,匹配待分析函数变量,对于需要进行语法分析的函数再做进一步语法分析,标记具有不安全特征的函数,并计算其所在行号等基本信息,重复此过程直到分析完所有源程序;最后形成结果文件并输出。程序的处理流程如图2所示。
3 系统详细设计
本系统主体部分采用标准的C/C++代码,可以运行在Linux/Unix、Windows等支持C/C++的操作平台上,为了操作的方便,代码通过MFC进行了主界面设计。
本系统主要实现了词法分析器、不安全函数库管理、程序界面、处理模块部分。其中,词法分析器主要用于正则匹配源代码中的函数;不安全函数库管理实现程序的可扩展性;程序界面提高系统可操作性;处理模块是系统的核心部分。
3.1 词法分析器
系统的重点部分是词法分析器的实现,本文根据 IEEE Std 1003.1, 2004 Edition,The Open Group Base Specifications Issue 6提出的标准,使用Lex实现词法的分析。
Lex(generate programs for lexical tasks)是词法分析器的生成器。Lex有POSIX标准,完整Lex的有三部分:
1) Lex定义文件解析。
2) 正则表达式到NFA, NFA -> DFA, DFA是最小化算法的实现。
3) 输出的词法分析器C代码模板。
算法的实现核心是怎样表示NFA,这其实是数据结构的设计问题,本文采用了一种最直接的方法,虽然不是效率最高的方法,但是这种方法容易理解,思路清晰。
本文设计的NFA类主要的数据成员有:
状态集合 states
开始状态 initialState
终止状态集合 finalStates
转换集合 transitions ,集合的元素是 (from,to,lable)的三元组
alphabet 默认其为ASCII字符
这个类可以实现上面的模型。move函数可以通过查询transitions来实现;当然这样每次查询都遍历这个列表是效率很低的,本文建立了三个map来将from,to,label映射为对应的transitions编号集合,DFA是一个NFA的特例,可以使用继承关系来实现。本文在类里面加了一个_isDeterminized 来表示是否确定化了。
从正则表达式到NFA,本文采用的是 Thompson’s construction方法,其基本思想就是有简单的正则表达式的NFA,通过固定的规则来构造复杂的正则表达式的NFA:
1) 构造简单的正则表达式的NFA。可以称其为原子NFA;比如识别一个字符的NFA,识别类似[a-e0-9]这样的正则表达式的NFA,这两种NFA都只有两个状态。
2) 实现各种NFA的组合操作:
closure A*
option A?
union A|B
iteration A{min,max}
concatenation AB
3) lex支持的正则表达式可以从其POSIX规范中找到,本系统只使用了它的一个子集。
3.2 界面设计
为了使用者操作的方便,系统框架采用标准的Windows架构,界面由标题栏、菜单栏、工具栏、项目工作区窗口、文档窗口、输出窗口以及状态栏等组成,如图3所示。
3.3 不安全函数库设计
函数库有以下几个部分组成:风险级别常量定义、风险种类常量定义、提示信息字符串常量定义、改进措施字符串常量定义、函数体。函数库文件形式如下:
风险级别常量、风险种类常量、提示信息常量、改进措施常量可以直接用于函数体中。
函数体的各项参数的含义:
1) desc 警告信息,可使用提示信息常量;
2) solution 建议或改进信息,可使用改进措施常量;
3) risk 风险级别,可使用风险级别常量;
4) handler 风险种类,可使用风险种类常量。
根据不安全函数库的格式,用户可以通过以下两种方式编辑不安全函数库:
(1) 通过纯文本编辑器,直接编辑不安全函数库文件,对于大量修改工作或高级用户而言非常的简单方便;
(2) 通过本系统提供的菜单:“编辑”->“编辑不安全函数库”,修改库文件。
通过这两种方式,可以把新出现的问题函数按已定义的规则添加到不安全函数库,从而提升系统的检查能力。
3.4 关键模块的实现
系统关键处理模块由如下五个模块组成,下面详细叙述各模块的实现。
1) C/C++文件读入模块
它是系统的基础。C/C++文件读入模块可以分成两个部分:读入文件和读入工程文件。现在分别详细介绍:
(1) 读入文件 系统读入代码文件时不记录代码文件的内容,而是记录代码文件名和文件数量,文件内容只是保存在一个局部变量FileContext,以便在输出窗口进行显示。在读入过程中分别将文件名称和文件数量存入字符串数组m_pFileName[]和变量m_pFileCount中,同时将打开的文件中的内容保存到局部FileContext中,系统输出窗口根据FileContext中的内容进行显示。
(2) 读入工程文件 系统是基于VC6.0进行开发,所以以读入.dsp为后缀的工程文件为主。当用户要打开相应的工程文件时,工程文件记录了当前工程包括的文件数量,根据工程文件的内容,分别向CtreeCtrl标准控件类中增加源文件(.cpp)和头文件(.h),并在CtreeCtrl控件类中分别向源文件(.cpp)和头文件(.h)增添不同的图标,同时将源文件(.cpp)的文件名记录入m_pFileName[]字符串数组中,m_pFileCount根据增加的文件名自动加1,以此完成工程文件读入,同时为了方便用户查看工程文件中的源代码内容,双击显示的文件名可以得到相应的文件源代码内容。
记录的文件名和文件数量是为检测模块准备的,检测模块根据记录的文件名和文件数量打开文件,进行相应的扫描。
2) 初始化模块
它主要完成如下几个方面的任务:为导入不安全函数库和检测结果存放分配内存空间,导入不安全函数库,并对不安全函数库进行词法分析,将相关函数名称及安全警告、风险种类、风险级别推荐的改进办法等信息以字典的形式存储在计算机内存中。内部采用哈希表形式,以加快分析代码时的查找速度,提高系统的检测速度。
3) 扫描模块
漏洞检测的过程如下:将当前分析的代码文件名传递词法分析器Lex,词法分析器Lex根据提供的代码文件名打开需要分析的代码文件,对代码文件词法分析,词法分析的结果表示为一个list,记录了代码中词所在的行号及所代表的类别,将词的类别分成了OPERATOR、STRING、CHAR1、IDENTIFIER、INTEGER、REAL、PREPROC_START、PREPROC_END、PREPROC_NUM、COMMENT等九个类型,在计算机中以枚举变量表示。完成当前文件的词法分析后,根据词法分析的结果进行静态安全检查,将词法分析的结果和对不安全函数库词法分析结果进行对比,根据可能产生的安全类型进行检测,判断当前代码是否存在不安全的漏洞。系统中分成了H_DEFAULT、H_STRCPY、H_SPRINTF、H_SNPRINTF、H_SCANF、H_SSCANF、H_TOCTOUA、H_TOCTOUB、H_TOCTOUC、H_FPRINTF、H_PRINTF、H_SYSLOG等12种,其中第一种可以忽略,它的存在主要是为了switch语句的完整使用。当对当前文件的词法分析和不安全函数库的词法分析结果进行比较时,根据不安全函数库的desc安全类型分别调用各自的判别函数进行安全检测,得到不同的检测结果。
4) 后处理模块
扫描模块完成后,检测结果存于Result结构数组中,Result为结构体,记录安全漏洞存在的文件名,所在文件的行号,安全叙述,以及安全等级等信息,在输出结果前有必要进行有关处理,扫描Result结构数组中的值,对结构数组中的值扫描处理,过滤不必要的报警或提高某些函数的安全等级等。扫描完成后,系统在结果输出区自动给出检测结果,其中包括代码所在的文件名、安全类型、安全等级、推荐的解决方案,并且产生outputfile.out文本文件,将结果输出到该文本文件中。
下面给出了本系统可以检查的缓冲区溢出问题和竞争条件问题的输出结果:
(1) 竞争条件检查结果如图4所示。
(2) 缓冲区溢出检查结果如图5所示。
5) 库函数编辑模块
当前系统包含了131个不安全因素。在不安全函数库中保存了如下几点信息:(1)对安全问题的简略描述;(2)代码围绕问题的高水平描述;(3)安全等级的描述,在此将安全等级分成了NO_RISK、LOW_RISK、MODERATE_RISK、RISKY、VERY_RISKY、MOST_RISKY等六个级别;(4)安全类型的分类,对每个不安全函数给予安全类型分类。
在系统涉及的五个模块中,扫描模块是核心,其他四个模块为之服务。读入文件模块,记录文件名和文件数量,为系统再次打开需要检测的文件提供文件地址;初始化模块为扫描源代码文件分配内存空间,为扫描提供内存基础;导入不安全函数库为扫描提供规则基础;后处理模块用来处理扫描模块得到的结果,通过优化处理向用户提供最有价值的扫描信息;库函数编辑模块为扫描模块辅助作用,用户根据自身的知识集和系统使用的要求,编辑不安全函数库,扩展系统的适应性。
各模块关系如图6所示。
4 结 论
系统开发完成后,利用易于发现安全漏洞的代码进行了实验。扫描源程序后,系统标记出源代码中使用的具有潜在危险的函数,其中包括:缓冲区溢出、竞争条件、标准库危险使用等。根据函数的使用及参数的调用,提示检测者检查该函数的使用是否符合规范。系统检测速度快,操作简单,可信赖程度高,基本达到了预期的目的。
参考文献
[1]陈丹伟,李军,刘继兴.C/C++源代码风格检测工具的实现[J].计算机应用,2003(10).
[2]陈丹伟,孙国梓.代码风格检测分析器工作流模型[J].计算机工程与设计,2004(12).
[3]阮新新,程序静态结构的分析及其应用[J].武汉理工大学学报:信息与管理工程版,2006(4).
[4]Michael Howard,David LeBlanc.编写安全的代码[M].程永敬,吴嵘,庄锦山,等译.北京:机械工业出版社,2002.
[5]Keith J Jones,Mike Shema,Bradley C Johonson.狙击黑客[M].宋震,易晓东,肖国尊,等译.电子工业出版社.
C++/C范文
声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。如若本站内容侵犯了原著者的合法权益,可联系本站删除。