概述
c和c++都是在实践中发展起来的语言。实用性极强。c是与UNIX/Linux的发展相辅相成的。而C++是B.S为了摆脱c与硬件以及底层过于紧密的苦恼,而开发的语言。C++可以说就是c语言的超集。任何c语言的程序理论上都应当是合法的C++程序。
C语言的特点:过程性编程和结构化编程。函数作为编程的主要载体和任务模块。一方面用for,while,if-else为代表的分支来规划程序结构,另一方面采用自顶向下编程思想。大任务分为小任务,小任务再往下分,表现为,函数套函数。结构清晰明了。
C++语言特点:1.过程性编程 ----- 函数或者类中的方法设计与开发仍然占据重要地位。
2.面向对象编程(OOP)------以封装,继承,多态等思想为代表。主要的工作在于类的设计和实例(对象)的应用。
3.通用编程(generic programming)。------------以模板template为代表。。。实际上到现在自己也没怎么学会这个。要加油了。
一开始,C++的规范并不太一致,C则以K&R C为通用标准。后来ANSI/ISO制定了ANSI/ISO C++标准,同时制订了ANSI/ISO C标准,定义了两种语言都使用的标准C库。。从此,两种语言有了官方的标准并且不断由官方发布更新版本。
在官方标准中,C语言引进了函数原型和const类型限定符,并且支持了//注释格式。以及long long等新的整型。
今年寒假,考完研,就找了一本C++和一本C的教材,在毕(shi)业(ye)之前好好温习一下两种语言。两种语言有很多相似之处。下面总结(zhao chao)一下两种语言的相似之处.
第一条:include 的头文件。
C语言的传统是使用扩展名.h,例如math.h。C++的定位之一是C语言的拓展,所以起初C++也是以.h作为头文件后缀,而且有些C的头文件直接当作C++的头文件。
但是后来C++的标准发生改变:头文件不再使用拓展名,来自C语言的头文件,在头文件名前加上前缀c。例如:math.h --》cmath和iostream.h --》iostream 。同时,这种改变也意味着一些新的特性,比如namespace,等被加入。所以一些老旧的编译器会支持#include<iostream.h>但是不支持#include<iostream>。
头文件类型 | 约定 | 范例 | 说明 |
C++旧式风格 | 以.h结尾 | iostream.h | C++程序可以使用 |
C旧式风格 | 以.h结尾 | math.h | C,C++程序可以使用 |
C++新式风格 | 没有拓展名 | iostream | C++程序可以使用,使用namespace std |
转换后的C | 加上前缀c,没有拓展名 | cmath | C++程序可以使用,可以使用不是c的特性 |
第二条:main函数
经典C函数头: main() //original C style, omitting int
在C语言中,省略返回类型就是默认函数类型为int。。。但是,C++逐步淘汰这种用法,所以不建议省略 int 返回类型。而且不建议使用 void main()。因为部分系统可能不支持。
此外int main(void)在C和C++中都表示函数不接受任何参数。而int main()在C++中与使用void相同作用,在C中则意味着对是否接受参数保持沉默。(好吧,什么叫保持沉默。。。。)
第三条:注释风格
/*-----*/是C的注释风格,//是C++的注释风格。C99之后,C支持了//。两者在两种语言中均可混用。但是应尽量保持风格统一。
同时,应尽量使用C++注释风格,因为它不涉及到结尾符号与起始符号的匹配。有些人写了/* 就容易忘记写 */
第四条:const与 #define 定义符号常量。
编译过程中,先将源程序先交给预处理器(the preprocessor)作相应的处理。然后再编译。不是边编译,边处理。比如#include 将整个头文件替换到#include 这个语句处。#define X Y是先在源程序中查找独立的标记X,将其替换为Y。。。替换完之后再编译。
c中常用#define MAX 100来定义常量。C++支持这种C风格,但是它提供了更适合的const int MAX = 100;
在C中,const定义常量,初始值可以为常量数字或者常量数字参与的表达式,但是不能是const常量或者const常量参与的表达式。数组大小亦如此。
C++中,const常量的初始值和数组大小的初始值均可以为const常量参与的表达式。
#define LIMIT 20
const int LIM = 20;
static int data1[LIMIT];
//C++ Valid, C Valid
static int data2[LIM];
//C++ Valid, C Invalid
const int LIM2
= 2 * LIMIT; //C++ Valid, C Valid
const int LIM3
= 2 * LIM;
//C++ Valid, C Invalid
在头文件中必须使用#define MAX。因为const int MAX = 100.是一种定义。一个变量只能被定义一次。
此外,const常量具有作用域规则,如for循环内部的const常量,在for外失效。但是#define的作用域是从define开始到文件结尾。
使用const要注意以下规则:
- 创建常量的通用格式: 注意,声明常量时必须要初始化。而且初始化之后,常量不能进行改变。
const type name = value;
- 在头文件中尽量不要使用const,而应该使用#define。
- const可以根据C++的作用域规则将定义限定在特定的函数或者文件中。而#define则在其所在文件范围内均可见
- 如果数据类型本身不是指针,则可以将const数据或非const数据的地址赋给指向const的指针,但只能将非const数据的地址赋给非const指针。
- 由第四条可知,我们要尽可能使用const,尤其是指针作为函数的参数,而函数又不会通过指针修改指针指向的数据时,这点在类的设计方面更加凸显。
原因有两条:其一:这样可以避免由于无意间修改数据而导致的编程错误;其二:使用const使得函数能够处理const和非const实参,否则将只能接受非const。所以如果条件允许,则应将指针形参声明为指向const的指针。
第五条:bool类型
此处暂留,等看完if-else之后综合比较。主要观点就是经典C中没有bool类型,C++中有bool。但是自从C99之后,C可以支持bool型了。
C语言的布尔类型
第六条:强制转换
强制转换的通用格式:
(typename)value //C-style, both supporting
typename(value) //C++ -style, only C++ supporting
static_cast<typename> (value) //another operator for casting in C++..
第二种C++风格的想法是强制转换类似与函数调用,所以借鉴函数调用格式。value类似与传入参数,typename类似与函数名。但是,一和二一般采用C风格。。第三个是C++独有的强制类型转换操作符。
第七条:指针
1,在C中,将指针视为无符号整数,注意并不是int类型。。具体格式看具体编译器和操作系统的实现。而C++将指针(地址)视为一种独立的类型。所以:
//字符串常量在C和C++中编译时,表示的是字符串所在的内存地址。
char fish[] = "Bubbles"; //字符串常量初始化数组。实际上将const char * 赋值给char* 类型的fish。,alright,no pa。
char fish = “Bubbles”;
//这句话在C中能够编译通过,但是会发出警告。将指针赋值给整型变量。默认启用。
//但是在C++中。这句话是错误的。无法编译通错,会报错:不能将const char * 赋值给char类型变量。因为他们是不同的类型。
int * a = 100; //在C语言中将整数赋值给指针,可以编译通过,但是会发出警告。 int * a = 100; //在C++中,则不可以,因为int 和int *是两种类型。 int * a = (int *)100;//也可以通过强制类型转换。但是,这样做十分危险。。
ps,在C/C++中将NULL初始化指针。要养成这样的好习惯。。不然指针未被赋值时,其内部存储的数据是任意的,无法预测的。十分危险。
2,C,C++对待void类型指针是不同的。在两种语言中,都可以将一个指向任意类型的指针赋值给void *。但是 ,在将一个void *赋值给一个指向具体数据类型的指针时,C++需要一次强制类型转换。C中,这种强制转换是可选的。但是考虑到强制类型转换更能表达指针意义,建议使用强制类型转换。
第八条:string类和C-风格字符串
string类是ISO/ANSI C++ 添加的。使用时要#include。要使用处理C-风格字符串的函数,如strlen,strcmp,要#include 或者#include。。。其余的。。属于OOP和C语言基本功的问题了。。。
另外,C++很早就有istream类,而string类是后期出现的。所以cin>>和cin.getline()都没有考虑针对string类的重载。。所以通过重载>>和getline()为string类和istream类的友元函数实现cin>>str, getline(cin, str)。。不能用cin.getline(string, int)。。
第九条结构体:struct
记住所有C支持的struct的特性,C++都支持。但是C++具有很多独有的特性。而且这种特性是类(class)相似的。
- 在C中,声明结构体变量时struct关键字不能省略。但是在C++中可以。C++这种变化是强调结构声明定义了一种新类型。类比类(class),声明类变量时不必加上class关键字。。
- C和C++都支持同结构类型的结构变量使用 = 赋值。。。成员值会完全相同。。
- 结构体可以声明构造函数,可以声明自己的方法,可以重载操作符,可以有public和private等权限。类的默认权限是private,struct的默认权限是public。稍后总结。。
第十条:逻辑操作符的其它表示方法
C++为逻辑操作符提供标识符表示方法:and,or,not。这些都是C++的保留字,可以直接使用。但是它们在C中不是保留字。只有在程序中#include才能使用它们。
操作符 | 替换代表 |
---|---|
&& | and |
|| | or |
! | not |
第十一条:函数
C语言和C++在函数上大体是相同的。C++支持绝大部分C语言的函数语法。而C++增添了很多特有的函数功能,如内联函数,引用变量,默认参数,函数重载和函数模板。函数由函数头和函数体组成。
- 函数原型:函数原型就是函数头后加上;。C++中在在第一次使用函数前,必须先用函数原型声明函数,或者直接将函数定义放在前面。否则,编译器会报错,函数未声明.
在C中对此没有显性要求。可以不声明函数原型。声明函数原型时括号可以为空,这只是表示函数的参数先不告诉编译器,但是参数是确定的。而C++中为空则表示参数为void。同时与fun(...)也不一样。‘...’表示参数不确定。三者要分清楚。当然,从编程风格来讲,C语言中最好也要声明函数原型。 - 内联函数与宏定义:内联函数就是编译器在编译时,会直接把函数的指令插入到内联函数调用处。而不是采取堆栈式管理方式,跳转到函数入口处。每一处函数调用都会插入函数指令,但是省去了跳转时间。“空间换时间”。如果函数的执行时间很长,那么跳转时间占比例很小,“不值得”。如果函数的执行时间较短,跳转时间占比例大。但是绝对时间又不大。所以,内联函数适合短小但是经常被调用的函数。因此多应用在类设计中
同时,编译器也会对内联函数作出判断,如果编译器自己认为函数不宜被设计为内联函数,如函数过大,或者函数含有递归。那么编译器也不会将其编译为内联的。
内联函数格式:inline 函数原型/函数定义
C语言中含有参数的宏定义#define FUN(X) XXX 有类似功能。但是两者有本质差别。预处理器#define实现的是编译前的文本替换。将源代码中的文本替换之后,进行编译。而内联函数,是编译时将函数指令替换函数调用。具体差别可以查看C语言关于#define宏定义。
C语言中自从C99之后,也支持inline函数了。 - 引用:引用与const指针相似,必须在声明引用时进行初始化,const变量也是。而const指针和引用都是一旦与某个变量关联,就保持这种联系,不再改变。
const int * ptr是指向const的指针,int * const ptr是const指针。
因为引用参数的不确定性,所以不建议对简单的基本类型变量使用引用参数。当参数的数据比较大,如数据结构和类时,使用引用参数将节省空间和时间。但是除非明确要使用引用参数达到修改变量的目的,否则在函数定义时,要使用const Type & argument..
传递引用的限制更严格,如果实参与引用参数不匹配,如,实参为非左值(字面变量,包含多项的表达式)或者类型不匹配时,当形参引用参数为const时,将生成临时变量,非const时,绝大部分编译器会报错。少数会生成临时变量。所以应尽可能将引用形参声明为const。 - 默认参数:一:默认参数可以只在函数原型中声明,二,默认参数必须从右到左添加默认值,默认参数的右边也必须是默认参数。否则,fun(int i = 0, int j),那么调用fun(3)。3是覆盖i还是赋值给j,无法判断。
- 函数重载:函数重载的关键在于函数的参数列表,即特征标(function signature)不同。
现讨论六种比较有疑惑的函数;按值传递:int fun(int a), int fun(const int a),引用形参: int fun(int & a), int fun(const int & a),指针形参 int fun(int * a), int fun(const int *a)。指针形参和其它两组显然可以重载。- 对于按值传递int fun(int a)和int fun(const int a)无法重载。因为显然编译器在调用fun(a)时,无法区别选择哪一个。
- 按值传递和引用形参无法进行重载。
int fun(int & a)和int fun(int a)以及int fun(const int a)是同样的。调用fun(a)时,编译器无法区别调用的是哪个函数。因此会报错,重复定义函数。 - 引用形参的const和非const形式,指针形参的const和非const形式均可以视为重载。因为const实参只能赋值给const形参,所以编译器对const实参调用const形参,对非const实参调用非const形参。
- 重载是对参数列表进行重载,而不是返回类型。所以参数列表相同,返回类型不同不算重载,编译报错。参数列表不同,则返回类型也可以不同。
当实参与形参的类型不匹配时,会按照一定的规则进行自动类型转化。面对重载的函数,如果按照自动类型转换规则,只有一个函数适合,那么编译器就调用那个函数。如果有多个函数适合,编译器报错,有二异性。
void fun(double x); * void fun(double x);
void fun(int x); * void fun(float x);
{ * {
float x = 9.0; * int x = 9;
fun(x); * fun(x);
} * }
float转换为double优于转换为int,所以编译器会调用fun(double) int转换为float和double均可以,所以编译器报错,调用fun会产生二异性。
- 函数重载:函数重载的关键在于函数的参数列表,即特征标(function signature)不同。
- 函数模板:适用:需要多个将同一种算法用于不同类型的函数。函数模板并不是减少了生成函数的个数,最终编译出来的函数个数和指令还是不变的。它只是为函数的定义在编写上提供遍历。
函数模板包括三方面:函数模板的隐式实例化(implicit instantiation),后期的C++也支持显式实例化(explicit instantiation),显式具体化(explicit specialization)。格式如下template <class T> void fun(T a); //模板原型,typename和class两个关键字可以互换模板原型和模板定义与函数原型和函数定义一样。在使用模板前必须声明模板原型。模板也可以重载,同名模板的参数列表不同,这与函数重载一样。由此,对于一个函数名,可以有非模板函数,模板函数,和显式具体化以及它们的重载提供的各种版本。调用函数时它们的优先级大致为非模板函数>显式具体化>显式实例化>模板函数。对于非模板函数重载的版本,根据形参和实参最佳匹配原则确定调用版本。对于模板重载函数,根据部分排序规则确定调用版本,就是找出最具体的模板。如果能找出一个最佳函数,则编译成功。否则,编译会报出二异性错误,函数无法解析。函数的选择是个非常细致和复杂的过程,可以找具体资料查询。
template void<int> fun(int a);//显式实例化的原型
template<> void fun (int a); //
template <> void fun(int a);//上面和这个两者都是显式具体化的原型,是等价的声明
转载于:https://www.cnblogs.com/xyqhello/p/3517833.html
最后
以上就是慈祥铅笔为你收集整理的C与C++的编程风格区别的全部内容,希望文章能够帮你解决C与C++的编程风格区别所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复