第一篇:黑马程序员C语言教程: CC++培训专家-预处理命令使用详解
传智播客C/C++培训专家:预处
理命令详解
作为一枚C/C++程序猿,在我们编写和查看C/C++源代码的过程中会遇到各种编译指令,这些指令称为预处理命令。预处理命令虽然不是C/C+的一部分,但却扩展了C程序的设计环境,下面传智播客C/C+培训专家将向大家介绍如何应用预处理程序和注释简化程序开发过程,并提高程序的可读性。
ANSI标准定义的C语言所有预处理命令均以符号#开头,比如我写程序时常用的:
#define,#undef,#include,#if,#else,#elif,#endif,#ifdef,#ifndef, #error 1.#define和 #undef 宏定义命令的一般形式为:
#define[MacroName][MacroValue],示例如下: #defineITHEIMA 传智播客黑马程序员
在源程序中每次遇到ITHEIMA时,均以定义的值传智播客黑马程序员代换它。
在使用该宏时,有以下几点注意事项:
该语句没有分号。在标识符和串之间可以有任意个空格。 定义宏的时候,可以使用之前已经定义好的宏。
如果串长于一行,可以在该行末尾用一反斜杠' '续行。
#defineLONG_STRING“good good study,day day up!” 在定义宏标识符时,字母一般需要大写。 预处理运算符的使用:
#--该符号是“字符串化”的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串
#define ERROR_LOG(module)
fprintf(stderr, “error: ”#module“n”)ERROR_LOG(“add”);转换为 fprintf(stderr,“error: ”add“n”);ERROR_LOG(devied =0);转换为 fprintf(stderr,“error: devied=0n”); ##--是连接符号,将多个串连接到一起。char *szStr = “传播播客_黑马程序员”;#define ITCAST(exp)cout < 2.#include 命令#i nclude使编译程序将另一源文件嵌入带有#include的源文件,被读入的源文件必须用双引号或尖括号括起来。例如: #include“stdio.h”或者#include 这两行代码均使用C编译程序读入并编译用于处理磁盘文件库的子程序。将文件嵌入#i nclude命令中的文件内是可行的,这种方 式称为嵌套的嵌入文件,嵌套层次依赖于具体实现。 如果显式路径名为文件标识符的一部分,则仅在那些子目录中搜索被嵌入文件。 例如: #include “../include/head.h” 如果文件名用双引号括起来,则首先检索当前工作目录。如果未发现文件,则在命令行中说明的所有目录中搜索。如果仍未发现文件,则搜索实现时定义的标准目录。例如: include “head.h” 如果文件名被尖括号括起来,则首先在编译命令行中的目录内检索。如果文件没找到,则检索标准目录,不检索当前工作目录。 例如: include
它与#endif之间的代码,否则跳过这些代码。命令#endif标识一个#if块的结束。
跟在#if后面的表达式在编译时求值,因此它必须仅含常量及已定义过的标识符,不可使用变量。表达式不许含有操作符sizeof(sizeof也是编译时求值)。
#else命令的功能有点象C语言中的else;#else建立另一选择(在#if失败的情况下)。注意,#else属于#if块。
#elif命令意义与ELSE IF 相同,它形成一个if else-if阶梯状语句,可进行多种编译选择。#elif 后跟一个常量表达式。如果表达式为true,则编译其后的代码块,不对其它#elif表达式进行测试。否则,顺序测试下一块。
4.#error 命令#error强迫编译程序停止编译,主要用于程序调试。该指令使预处理器发出一条错误消息,该消息包含指令中的文本.这条指令的目的就是在程序崩溃之前能够给出一定的信息。
5.#ifdef 和 #ifndef 条件编译的另一种方法是用#ifdef与#ifndef命令,它们分别表示“如果有定义”及“如果无定义”。# ifdef的一般形式是:
# ifdef macroname statement sequence
#endif #ifdef与#ifndef可以用于#if、#else,#elif语句中,但必须与一个#endif。
define MAX 91 #include
int main(){ #ifdef MAX cout<<“hello,MAX!”< #else cout<<“where is MAX?”< #endif #ifndef LEO cout<<“LEO is not defined”< #endif return 0;} 今天关于预处理命令的知识点传智播客C/C+培训专家就为大家接受到这里, 欢迎大家留言交流.
第二篇:黑马程序员C语言教程: CC++培训专家:漫谈软件编码风格
传智播客C/C++培训专家:漫谈软件编码风格
通过建立代码编写规范,形成开发小组编码约定,提高程序的可靠性、可读性、可修改性、可维护性、一致性,保证程序代码的质量,继承软件开发成果,充分利用资源。提高程序的可继承性,使开发人员之间的工作成果可以共享。代码的风格主要有以下几点:
1、缩进
缩进以4个空格为单位。预处理语句、全局数据、函数原型、标题、附加说明、函数说明、标号等均顶格书写。语句块的“{”“}”配对对齐,并与其前一行对齐,语句块类的语句缩进建议每个“{”“}”单独占一行。
2、空格
变量、类、常量数据和函数在其类型,修饰(如 __fastcall 等)名称之间适当空格并据情况对齐。关键字原则上空一格,如: if(...)等,运算符的空格规定如下:“::”、“->”、“[”、“]”、“++”、“--”、“~”、“!”、“+”、“-”(指正负号),“&”(取址或引用)、“*”(指使用指针时)等几个运算符两边不加空格(其中单目运算符系指与操作数相连的一边),其它运算符(包括大多数二目运算符和三目运算符“?:”两边均加一空格,“(”、“)”运算符在其内侧空一格,在作函
数定义时还可据情况多空或不空格来对齐,但在函数实现时可以不用。“,”运算符只在其后空一格,需对齐时也可不空或多空格,“sizeof”运算符建议也在其后空一格,不论是否有括号,对语句行后加的注释应用适当空格与语句隔开并尽可能对齐。
3、对齐
原则上关系密切的行应对齐,对齐包括类型、修饰、名称、参数等各部分对齐。另每一行的长度不应超过屏幕太多,必要时适当换行,换行时尽可能在“,”处或运算符处,换行后最好以运算符打头,并且以下各行均以该语句首行缩进,但该语句仍以首行的缩进为准,即如其下一行为“{”应与首行对齐。
变量定义最好通过添加空格形成对齐,同一类型的变量最好放在一起。
4、空行
程序文件结构各部分之间空两行,若不必要也可只空一行,各函数实现之间一般空两行,由于BCB会自动产生一行“//------”做分隔,另因每个函数还要有函数说明注释,故通常只需空一行或不空,但对于没有函数说明的情况至少应再空一行。对自己写的函数,建议也加上“//------”做分隔。函数内部数据与代码之间应空至少一行,代码中适当处应以空行空开,建议在代码中出现变量声明时,在其前空一行。类中四个“p”之间至少空一行,在其中的数据与函数之间也应空行。
5、注释
对注释有以下三点要求: A.必须是有意义。B.必须正确的描述了程序。C.必须是最新的。
注释必不可少,但也不应过多,以下是四种必要的注释: A.标题、附加说明。
B.函数说明。对几乎每个函数都应有适当的说明,通常加在函数实现之前,在没有函数实现部分的情况下则加在函数原型前,其内容主要是函数的功能、目的、算法等说明,参数说明、返回值说明等,必要时还要有一些如特别的软硬件要求等说明。C.在代码不明晰或不可移植处必须有一定的说明。D.及少量的其它注释。
注释有块注释和行注释两种,分别是指:“/**/”和“//”建议对A用块注释,D用行注释,B、C则视情况而定,但应统一,至少在一个单元中B类注释形式应统一。
6、代码长度:
对于每一个函数建议尽可能控制其代码长度为53行左右,超过53行的代码要重新考虑将其拆分为两个或两个以上的函数。函数拆分规则应该一不破坏原有算法为基础,同时拆分出来的部分应该是可以重复利用的。对于在多个模块或者窗体中都要用到的重复性代码,完全可以将起独立成为一个具备公用性质的函数,放置于一个公用模块中(Common.cpp/Common.h)。
第三篇:黑马程序员C语言教程: CC++培训专家-编写高效C语言的四大绝招
传智播客C/C++培训专家:编写高效C语言的四大绝招
C语言是很多程序猿的入门语言,而且C语言也是一门用不过时的语言。编写高效简洁的C语言代码,是许多软件工程师追求的目标。今天传智播客C/C++培训专家针对编程工作中的一些体会和经验给大家做相关的阐述。
第一招:以空间换时间
计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第1招--以空间换时间。比如说字符串的赋值: 方法A:通常的办法 #define LEN 32 char string1 [LEN];memset(string1, 0, LEN);strcpy(string1, “This is a example!”);方法B:
const char string2[LEN] =“This is a example!”;char * cp;cp = string2;
从上面的例子可以看出,A和B的效率是不能比的。在同样的存储空间下,B直接使用指针就可以操作了,而A需要调用两个字符函数才能完成。B的缺点在于灵活性没有A好。在需要频繁更改一个字符串内容的时候,A具有更好的灵活性;如果采用方法B,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。
第二招:数学方法解决问题
现在我们演绎高效C语言编写的第二招--采用数学方法来解决问题。数学是计算机之母,没有数学的依据和基础,就没有计算机的发展,所以在编写程序的时候,采用一些数学方法会对程序的执行效率有数量级的提高。举例如下,求 1~100的和。方法C: int I , j;for(I = 1;I<=100;I ++){ j += I;} 方法D int I;I =(100 *(1+100))/ 2;
这个例子是我印象最深的一个数学用例,是我的计算机启蒙老师考我的。当时我只有小学三年级,可惜我当时不知道用公式 N×(N+1)
/ 2 来解决这个问题。方法E循环了100次才解决问题,也就是说最少用了100个赋值,100个判断,200个加法(I和j);而方法F仅仅用了1个加法,1 次乘法,1次除法。效果自然不言而喻。所以,现在我在编程序的时候,更多的是动脑筋找规律,最大限度地发挥数学的威力来提高程序运行的效率。
第三招:使用位操作
实现高效的C语言编写的第三招——使用位操作。减少除法和取模的运算。在计算机程序中数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作。一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。举例如下: 方法E int I,J;I = 257 /8;J = 456 % 32;方法F int I,J;I = 257 >>3;J = 456-(456 >> 4 << 4);在字面上好像F比E麻烦了好多,但是,仔细查看产生的汇编代码就会明白,方法E调用了基本的取模函数和除法函数,既有函数调用,还有很多汇编代码和寄存器参与运算;而方法F则仅仅是几句相关的汇编,代码更简洁,效率更高。当然,由于编译器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 来看,效率的差距还是不小。相关汇编代码就不在这里列举了。
运用这招需要注意的是,因为CPU的不同而产生的问题。比如说,在PC上用这招编写的程序,并在PC上调试通过,在移植到一个16位机平台上的时候,可能会产生代码隐患。所以只有在一定技术进阶的基础下才可以使用这招。用移位实现乘除法运算
a=a*4;
b=b/4;
可以改为:
a=a<<2;
b=b>>2;
说明:
除2 = 右移1位 乘2 = 左移1位
除4 = 右移2位 乘4 = 左移2位
除8 = 右移3位 乘8 = 左移3位
......通常如果需要乘以或除以2的n次方,都可以用移位的方法代替。大部分的C编译器,用移位的方法得到代码比调用乘除法子程序生成的代码效率高。
第四招:汇编嵌入
高效C语言编程的必杀技,第四招——嵌入汇编。“在熟悉汇编语言的人眼里,C语言编写的程序都是垃圾”。这种说法虽然偏激了一些,但是却有它的道理。汇编语言是效率最高的计算机语言,但是,不可能靠着它来写一个操作系统吧?所以,为了获得程序的高效率,我们只好采用变通的方法--嵌入汇编,混合编程。举例如下,将数组一赋值给数组二,要求每一字节都相符。char string1[1024],string2[1024];方法G int i;for(i =0;i<1024;i++)*(string2 + i)= *(string1 + i)方法H #ifdef _PC_ int I;for(I =0;I<1024;I++)*(string2 + I)= *(string1 + I);#else #ifdef _ARM_ __asm
{ MOV R0,string1 MOV R1,string2 MOV R2,#0 loop: LDMIA R0!, [R3-R11] STMIA R1!, [R3-R11] ADD R2,R2,#8 CMP R2, #400 BNE loop } #endif 方法G是最常见的方法,使用了1024次循环;方法H则根据平台不同做了区分,在ARM平台下,用嵌入汇编仅用128次循环就完成了同样的操作。这里有朋友会说,为什么不用标准的内存拷贝函数呢?这是因为在源数据里可能含有数据为0的字节,这样的话,标准库函数会提前结束而不会完成我们要求的操作。这个例程典型应用于LCD数据的拷贝过程。根据不同的CPU,熟练使用相应的嵌入汇编,可以大大提高程序执行的效率。
虽然是必杀技,但是如果轻易使用会付出惨重的代价。这是因为,使用了嵌入汇编,便限制了程序的可移植性,使程序在不同平台移植的过程中,卧虎藏龙,险象环生!同时该招数也与现代软件工程的思想相违背,只有在迫不得已的情况下才可以采用。
今天分享到这里,大家有遇到什么问题可以向“传智播客C/C++培训专家”留言哦!
第四篇:黑马程序员C语言教程: cc++专家_交你如何使用c++中的类型转换
传智播客C/C++专家– 交你如何使用c++中的类型转换
关于C风格的强制类型转换是程序猿用的最多的一种转换方式, 不管什么类型的转换统统都是TYTE b =(TYPE)a。但是众所周知C++程序中对类型的检测非常严格,如果类型转换过程中使用C风格的类型转换就容易出现一些问题。所以今天传智播客C/C++专家在这里给大家介绍一下C++风格的类型转换的几种转换操作符以及使用场合。
C++风格的类型转换提供了4种类型转换操作符: const_cast,static_cast,dynamic_cast,reinterpret_cast 4种类型转换的格式,如:TYPE B = static_cast(TYPE)(a)。 const_cast 去掉类型的const或volatile属性。
struct SA { int i;};const SA ra;//ra.i = 10;
//直接修改const类型,编译错误
SA &rb = const_cast
rb.i =10; static_cast 类似于C风格的强制转换。主要应用场景:
基类和子类之间转换:其中子类指针转换成父类指针是安全的;但父类指针转换成子类指针是不安全的。(基类和子类之间的动态类型转换建议用dynamic_cast) 基本数据类型转换。
enum, struct, int, char, float等。static_cast不能进行无关类型(如非基类和子类)指针之间的转换。 把空指针转换成目标类型的空指针。 把任何类型的表达式转换成void类型。
static_cast不能去掉类型的const、volitale属性(用const_cast)。
int n =6;// 基本类型转换
double d = static_cast
dynamic_cast
有条件转换、动态类型转换,运行时会进行类型安全检查(转换失败返回NULL):
安全的基类和子类之间转换。 必须要有虚函数。
相同基类不同子类之间的交叉转换。但结果是NULL。class ItcastClass { public: int m_iNum;//基类必须有虚函数。保持多台特性才能使用dynamic_cast virtualvoid foo(){};};
class HeiMaClass: public ItcastClass { public: char*m_szName[100];void bar(){};};
ItcastClass* pb =new HeiMaClass();
//子类->父类,静态类型转换,正确但不推荐 HeiMaClass *pd1 = static_cast
HeiMaClass *pd2 = dynamic_cast
HeiMaClass *pd21 = static_cast
转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。
在比特位级别上进行转换。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。 最普通的用途就是在函数指针类型之间进行转换。 很难保证移植性。
int doSomething(){return0;};
//FuncPtr 是一个指向函数的指针,该函数没有参数,返回 值类型为 void typedef void(*FuncPtr)();//10个FuncPtrs指针的数组 让我们假设你希望(因为某些莫 名其妙的原因)把一个指向下面函数的指针存 入funcPtrArray数组: FuncPtr funcPtrArray[10];
// 编译错误!类型不匹配,reinterpret_cast可以让编译器以你 的方法去看待它们:funcPtrArray funcPtrArray[0] =&doSomething;//不同函数指针类型之间进行转换
funcPtrArray[0]=reinterpret_cast
多态类之间的类型转换用daynamic_cast。 不同类型的指针类型转换用reinterpret_cast。今天的内容就到这里, 祝大家学习愉快!
第五篇:黑马程序员C语言教程:QSignalMapper的使用
QSignalMapper信号转发器的使用 适用范围
简单的理解,可以把SignalMapper这个类看成是信号的翻译和转发器,它可以把一个无参数的信号翻译成带int参数、QString参数、QObject*参数或者QWidget*参数的信号,并将之转发。这么一说大家有没有联想到该类的适用范围呢?比如说:我有一堆的button,可以把clicked事件放在一个函数里处理,只要给button编个号或者给button起个名就行了,这样就不用给每个button写一个slot了,岂不是很方便?
使用方法
首先把原始的信号连接到QSignalMapper类的map()槽函数,这样QSignalMapper能在第一时间接收到原始信号;
其次调用setMapping方法建立映射关系,告诉QSignalMapper对象怎样去处理原始信号。(映射关系通过对应一个整数、字符串或者QWidget*实现)
最后通过接收QSignalMapper类转化后的带参信号mapped()与槽函数连接,在槽函数中获得需要的数据,并作出对应的处理。
映射的关系可以通过removeMappings()被移除。
示例:
// 头文件
classWidget:publicQWidget { Q_OBJECT
public: explicitWidget(QWidget*parent=0);~Widget();
publicslots: //自定义槽函数
voidslotClicked(QStringtext);
private: Ui::Widget*ui;QSignalMapper*signalMapper;};// 源文件
Widget::Widget(QWidget*parent): QWidget(parent), ui(newUi::Widget){ ui->setupUi(this);
QStringListtextList;signalMapper=newQSignalMapper(this);// 布局管理器
QVBoxLayout*vLayout=newQVBoxLayout(this);textList<<“北京”<<“上海”<<“广州”<<“南京”<<“天津”;for(inti=0;i<5;++i){ // 动态创建按钮
QPushButton*button=newQPushButton(textList[i]);button->setFixedSize(50,30);//按钮的信号和QSignalMapper类的map()槽函数关联
// 原始信号传递给signalMapper connect(button,SIGNAL(clicked(bool)),signalMapper,SLOT(map()));//设置signalmapper的转发规则, 转发为参数为QString类型的
// 信号,并把textList[i]的内容作为实参传递。
signalMapper->setMapping(button,textList[i]);vLayout->addWidget(button);} //将转发的信号连接到最终的槽函数
connect(signalMapper,SIGNAL(mapped(QString)),this,SLOT(slotClicked(QString)));}
// 自定义槽函数
voidWidget::slotClicked(QStringtext){ QMessageBox::information(this,“ButtonClicked”,text);}
当用户点击不同的按钮,会弹出不同的对话框,对话框中显示的内容为按钮的标题。