往通俗点地说C/C++的不同之处(面对新手向)(持续更新ing)

又要记上课笔记了……
在这里插入图片描述今天是总结C语言和C++的不同之处,请各位同学找好位置坐下来好好听课呢!


1.面向思想不同

   首先C语言的特性是POP 面向过程编程 (Procedure Oriented Programming)
   其次是C++的OOP面向对象编程 (Object Oriented Programming)

种地例子

咱们这里就通俗的讲解何为面向过程和面向对象:
在这里插入图片描述
   假设你是个农民,要种地,你就得想怎么种地,种什么,什么时候收获,这些问题你都得想出解决方案,面向过程就是说明解决这些方案的特性:你可以通过面向过程设计个解决怎么种地的问题,种什么东西的问题……注意,这里是说一对一的解决问题,也就是说只编一个函数对应一个问题,比如说你要种西瓜,改程序中的函数,你还要再种个芒果,再改……久而久之你可以想到程序有多少行代码才能适应所有情况
code_eg:

char * getSeed()
{
	...//省略过程
}
void seed(int,cahr *)//播种函数,解决播种过程
{
	...//省略过程
}
void farming()//种植函数,解决种植过程
{
	...//省略过程
}

   而面对对象就不一样了,它不关心你种什么东西,也不想管你到底咋种,它关心的是农具、农民这些对象物体,比如说农具是一个类,你定义了一个农具类的对象,也就是变量,但你可以不经过函数解决问题,而是通过变量本身的具有方法解决问题
code_eg:

class farmer
{
	private:
		int age;//年龄
		char name[];//名字
		char seed[];//种子
	public:
		farmer();//类的构造函数,构造对象的函数;
		~farmer();//析构函数,像delete和free这些来释放对象(变量)所占的内存
		char * getSeed();
		void seed(int,cahr *);
		void farming();
		...
}
farmer::farmer()
{
	...
}
farmer::~farmer()
{
	...
}
char * farmer::getSeed()
{
	...
}
...
void farm()//这里在farm函数里面创建一个叫Tom的对象
{
	farmer Tom();//定义一个对象(变量)
	char * seed=Tom.getSeed();//利用.获取对象所包含的方法
	Tom.seed();
	Tom.farming();
}

在这里插入图片描述
   你可以将一个农民和一把农具当成对象,农民伯伯是个对象,他有丰富的种地经验,所以我们不用管他怎么种,这是他的事,他爱咋种就咋种,我们只需要告诉他种什么,他就能给我们立刻种出来我们要的东西……
   通过这上面两个例子我们可以看出来:面向过程需要考虑很多元素,而面向对象只需要考虑选什么对象
   这两个情况可以看成面向过程是通过函数实现一个功能,而面向对象是通过对象实现一个功能,并且这个功能的性能还是有很大的差距。

2.基本函数和函数对象的差别

关于标准I/O的差别

   首先我们得知道printf和scanf与cin和cout的差别
   1.printf和scanf是C语言的I/O
   2.cin和cout是C++的I/O
printf和scanf是函数,cin和cout是对象(嘿嘿嘿,结合上面的农民伯伯例子就可以知道对象是什么啦)
printf有很多格式要求,比如说

scanf("%d",&a);
printf("%d",a);

   但cin和cout就不一样啦

cin>>a;
cout<<a;

   是不是简便多了?其实啊,这都是对象的原因,要不是cin/cout可以智能分辨出a的数据类型,并且按正确的格式输入和输出!(cin和cout很聪明,学过C语言的都知道格式控制符有多么累,有时候出错点可能还是格式控制符和数据类型不一致)

3.基本运算符的差别

   目前运算符如果按照参数数目可以分为单目运算符、双目运算符与三目运算符。但那样分类的话会漏很多运算符,还会缺少C++的灵魂——::作用域运算符(目前用不上了解这个,以后会学到的,现在只需要知道这个运算符是C++对于C语言特有的)
所以可以这样分类

  1. 算术运算符
  2. 关系运算符
  3. 逻辑运算符
  4. 位运算符
  5. 赋值运算符
  6. 杂项运算符

如果是C++玩家,最好了解所有的运算符,因为C++要用到很多运算符,比C语言相比C++的运算符还是很多的,而且C++11的标准C也加了不少可选的运算符
以下只需了解就行,平时可能用不了

  • C++有::作用域运算符,而C语言平时没有
  • C++中,抛出异常采用的是try catch throw,而C语言使用的则是setjmp与longjmp

4.动态分配内存的差别

在这里插入图片描述
   其实在C++11中就允许直接申请一个变量然后直接用在数组的下标上了,但在那之前是都不允许的,所以学习动态分配内存是有必要学的,因为如果直接通过声明一个变量然后当作数组下标是很危险的行为,我们不知道这个数组内存的所在位置(如果听不懂这段的意思,可以想象下内存划分三个部分:栈,堆,自由分配内存,其中操作系统是要占据一部分内存的,如果你调用了其中的内存单元并且刚好是操作系统中的重要单元,轻则重启,重则崩溃……

int a,b[a];//就像这样的命名,在VC6++这个老编译器是行不通的,Dev c++是可以运行的

   malloc()和free()是C语言常用的动态分配内存,通常他们是函数,调用格式为

int *p=(int*)malloc(int);//这只是举例子,不一定要用int,申请int大小的内存
free p;//释放内存

   而C++就不一样了,他们是new()和delete()

int *p=new int;
delete p;

   注意,这两个有本质上的区别,malloc()和free()是函数,new和delete是运算符

5.代码上的风格的差别

命名空间域&命名空间

using namespace std;这个语句在C++中很常见,其实这里是命名空间语句,如果要搞清楚他的话,得明白作用域:
C 语言中作用域只有两个:局部,全局,往细里面再划分的话就是块作用域和文件作用域还有函数作用域与函数原型作用域。C++中则是有:局部作用域,类作用域,名字空间作用域三种。

#include<iostream>
using namespace std;
const int a;//我是全局变量,我在全局域,我不和下面的变量发生冲突。
/*在各种函数的外头,也就是不在任何的函数里面,是全局域,在全局定义的变量叫全局变量*/
int main
{
	/*在任何函数的里头,是局部域,比如说我定义了下面的a变量,是在main函数里面,也就是说他是局部变量*/
	int a;//我是局部变量,我在局部域,我不和上面的全局变量冲突哦。
	cout<<"helloworld"<<endl;
}

目前咱们只需要知道局部和全局是怎么回事就行了,命名空间域和类作用域解释起来过于生涩,在这里只说明,C++比C语言多了类作用域和名字空间作用域。
   那么什么是命名空间,你可以想象下你划分出好几个空间,那么在不同空间之内,有不同的程序员,他们各敲出相同的变量,那么那些变量会冲突吗?不会,因为是在不同的空间内,肯定不会发生变量重合什么的事情……

强制类型转换

   关于这个强制类型转换可能就有朋友听不懂了,这里是说在一个数据类型情况下转换另一个数据类型,但为啥有强制这个两个字:
原因是因为在强制类型转换之前还有个按数据类型优先性隐性转换,这个是看不见的,它要按照数据类型的排名来决定是否转换,那么要是遇到不能转换的,你却需要转换的情况下怎么办,你可以选择强行转换

double a;
(int) a;//C语言的强制转换
int (a);//C++的强制转换

注意C++是能兼用C语言的强制类型转换格式,也就是说我能用C++代码写C语言的强制类型转化格式。

指针类型转换

   这里稍微说下C和C++的区别:
   C 语言中 void *类型可以转化指向其他类型的指针类型,但 C++就做不到
这里是说C语言可以自动转化,但C++是不能直接赋值给其他指针,必须强制类型转化,但其他类型指针是可以自动给void *赋值

6.头文件的差别

注:自己写的头文件需要用“”引用,而标准的头文件要用<>引用

   C语言的标准头文件是#include<XXXX.h>
   现在C++的标准头文件基本都是不带.h,但其实C++是允许带.h的,但那已经是老式的写法了,而且没有经过标准化,里面存放的函数原型很多都是和现在的标准头文件差了太多了.
   比如说#include<iostream.h>和#include<iostream>这两大区别就是有无经过标准化,而且如果C++使用带.h的头文件,则不需要添加使用命名空间的声明。
   如果是自己写的头文件,那必须加上.h,比如#include"XXXX.h".C语言同理

7.编译器的差别

   C语言主要用GCC编译器(现在也能编译C++)
   C++主要用G++编译器

8.C++在C语言上扩展的部分

关键字上的不同

  1. C语言全局const会被存储到只读数据段。C++中全局const当声明extern或者对变量取地址时,编译器会分配存储地址,变量存储在只读数据段。两个都受到了只读数据段的保护,不可修改。
  2. C语言中局部const存储在堆栈区,只是不能通过变量直接修改const只读变量的值,但是可以跳过编译器的检查,通过指针间接修改const值。
  3. C语言的const不能用于命名数组的下标,C++可以用于命名数组的下标。

多态,继承,类,数据抽象和封装等等带有POP的特征

   目前只需要知道这些是面向对象的基本特征就行,往后会深入了解这些三大基本特征:多态 继承 类

9.常用函数的用法不同(扩展)

1.I/O函数和函数对象

cin&&scanf

   经过上一个博客之后大家都能了解到关于printf、scanf和cin、cout的较明显不同之处,那么在这篇,我再给大家继续加深对标准I/O函数和函数对象的了解。
   首先请大家看下面的代码,然后动手试试输入一个回车键看看有什么现象:

char a;
scanf("%c",&a);
printf("%c",a);//a输出一个回车键,当然你看不到,它这里是以字符显示,先转换字符,然后转换成转义字符,再执行转义字符,即a="\n"=下一行(不显示,光标直接跳到下一行,然后程序结束
printf("%d",(int)a);//a输出10,\n回车键的ASCII码是10,所以按照int类型输出的是10
char a;
cin>>a;//如果一开始输入回车就不显示,只有当你输入具体的字符才会显示……
cout<<a;//a不显示,一直回车也没反应

   这里大家可能就有点疑惑了,如果a输入的是回车键的ASCII码,也就是a='\n'的时候,scanf能读取,cin却无法读取,这是正常的现象,因为cin本身就忽略空格键,tab键和回车键等等,这是cin的本身缺陷之一,但走运的是,你可以用cin.get()获取空格,回车键。
   那么这里可能就有人问既然没法读取回车,但是cout会读取空白符从而结束cin啊,莫急,让我慢慢道来~
首先,我这里说的忽略可不跟按下回车结束的一个东西呢,这里是说cin会跳过空白符,也就是说,只要你一开始输入回车或者空格,cin都会当作无视,跳过他们,直至读取到实质的数字或者字符之类的……

cout&&printf

   讲解完cin之后,我们再讲讲cout这个函数对象。关于cout,许多人在用过C语言之后刚刚用cout会发现一片天堂(不是printf它不好,是cout太香了.jpg)
在这里插入图片描述
但用到后面你会发现cout实在是太智能了,有时候它错了,咱们可能还要纠正过来。比如说控制输出几位小数,或者说用浮点数输出,结果发现小数点之后的零被省略掉了……

double a=6.0;
cout<<a<<endl;//a输出的是6,而不是6.000000,因为cout会忽略有效数之后的0…
printf("%f",a);//输出的是6.000000因为%f默认输出6位小数

如果你要保留小数点,你可能要用到#include<iomanip>以及各种类的成员函数(比起printf,这个输入巨繁多是真的蛋疼)

double b=6.0000;//这里b是4个小数,并且都带0
cout<<setiosflags(ios_base::fixed)<<setprecision(3)<<b<<endl;//b输出的是6.000,保留三位小数
printf("%.3f",b);//c语言只需要在格式控制符前面加.3就可以控制保留3位小数

在这里插入图片描述
相比之下还是C语言的标准函数printf更温柔了点呢

2.I/O的快速读取和读出(总结)

   C语言的快速读取函数大全:

getchar();
getche();
getch();
getc();
gets();
fgets();
fgetc();

   读出:

putchar();
putch();
putc();
puts();
fputs();
fputchar();
fgetc();

   C++在C语言的基础上扩展的读取函数:

getline();
get();
put();

10.格式的不同(扩展)

1.结构体

   大家都知道,声明一个结构体时候,都是这样的:

struct birthday
{
    int year;
    int month;
    int day;
};//不要忘记加引号

那么在定义一个结构体性变量上C语言和C++有什么区别:

struct birthday zhanggao;//C语言写法
birthday zhanggao;//C++写法,可兼用C语言写法

   是的,C++可以省略struct来定义一个结构体类型变量,也可以兼用C语言的写法,但谁不想少写呢?
注:C++中的结构体不是C中的结构体,而是被当成了来处理,保留struct关键字是为了兼容C语言。换句话说,你用class和struct都是差不多的,都是可以声明一个成员函数,C语言结构体无法声明一个函数。

2.类

   如果是学过C语言的伙伴们,应该知道结构体是什么,其实类跟结构体是差不多的,我给你举个例子:

class zhanggao//这是名叫zhanggao的类的声明
{
public://这是表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用
	int a;//这跟结构体非常相似,是定义一个int类型变量
	double b;
};

   大家可能对public这个非常陌生,莫慌,我给大家举个例子:
请想象你有一堆家人,那么家人之间肯定要有一定的信息交流和物质交换对吧,那么儿子只想分享给自己的大哥(喵喵喵,可能是?),不想给爸爸分享怎么办呢,只需要向家人声明下这个不给爸爸看,只给哥哥看,那么这里的public是什么意思呢,是所有人都可以用啦(是在这个类中的函数和子类的函数还有对象等等都可以用这里面的东东)
在这里插入图片描述
   经过这上面的例子我们不难知道public大概是什么了吧,public的实质就是作用域,只是他有权限控制别人的访问,不让别人借用什么的,像public的还有private/protected,他们的保密程度比public还高,基本上都不让别人借用什么的。
总结出来:

  1. public表明可被该类中的函数、子类的函数、朋友函数访问,也可以由该类的对象访问。
  2. private表示私有,私有的意思就是除了类自己之外,任何人都不可以直接使用,私有财产神圣不可侵犯嘛,即便是子女,朋友,都不可以使用。
  3. protected对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部类,protected就变成private。

11.作用域的不同(难点)

1.命名空间作用域(名字空间域)

   其实命名空间的正式叫法应该是名字空间,而命名空间域的叫法是因为比较习惯,那么它到底是啥样子的?不妨用代码来看看它的本质:

namespace space;//这是叫space的命名空间
	char a[13]="hello,world!";//这就是命名空间作用域
cout<<space::a<<endl;//出来的是hello,world!

那么我们可以知道using namespace std;到底是啥玩意了,其实就是使用标准命名空间的声明,因为cin和cout都在std里面,如果要使用cin和cout都必须先声明using namespace std;才行哦

2.类作用域

   前面有讲到过C++中类和结构体很相似,其实这个说法也不是毫无力度,类确实可以看成一个特殊的结构体,只是多了成员函数、访问控制权限、继承、包含多态等面向对象(OOP)特性。可以说学会了类就是学会了C++的一小半,为什么说一小半,因为后面还有继承,多态和抽象数据等等,但学完类是真的占据了C++的一小半。

class 类名//这个看起来很像结构体,但比结构体多了很多的"功能"
{
public: //前面有介绍
	int a;//这些都是类作用域
	double b;//这也是类作用域部分
};

12.指针的区别

1.在可指向的范围的不同

   首先指针可以指向的范围有点不同,C++可以指向对象和引用,支持成员指针,包括成员变量指针和成员函数指针,而C语言没有,只能指向结构体、变量或函数,即C++中类成员指针等类型是C语言中所没有的。
关于引用这段实在太复杂,放到下次再讲(下次一定)
在这里插入图片描述

2.对空指针的定义不同

   在C语言中:#define NULL (void*)0
   在C++语言:#define NULL 0
   在这上面有类型上的区别就是定义上的不同,空指针就是指向0X0000这一零地址,不会伤害到重要内存部位,你可以想象下指针是一把枪,当你枪口瞄对靶子(内存单元的地址),目标就在靶子上,你可以按下枪的开关(*解引用符),然后得到了对应的目标(对应内存单元上的值),那么打完之后,你枪里面还有子弹,这时候应该由另一个选手上来了,而你应该还指向着已经更换过的靶子,这时候你不应该瞄准靶子,而是放下枪口,哪个靶子都不对准(指向了空),这就是空指针,保证了安全。
在这里插入图片描述

3.智能指针

   嘛,C语言和C++的指针其实还有更深的区别,就我个人而言是智能指针了(不强求看,量力而行)
   这个知识点不要太僵硬了,我们可以想象下这个智能指针是一个保姆,她要管理好多的资源宝宝,不让宝宝发生“内存泄漏”和“内存多次释放”的危险,那么智能指针肯定要有个工具(牛奶 )来管理资源宝宝吧?这个工具就是啦。她会通过这个“类”工具来实现宝宝放空自我(自动回收内存)。
如果你要通过new申请一些内存来存放数据,那么你肯定不得不在后面加个delete释放内存。但如果要使用智能指针的话,是可以大胆地放心申请内存,后面会帮你自动delete。
这是普通申请和释放内存的代码

类名 *p=new 类名(参数)//这里要申请一个类对象的堆内存
p->类中的一部分函数;//调用类里面的函数;
delete p;//释放内存

   不用纠结什么类不类的,只需要记住咱们申请了一部分内存就行
   然后再看用智能指针的代码

类指针 t = new 类名(20);
t->类中的一部分函数;

   这里不用写delete也是能实现释放内存效果,具体是怎么实现的是要看指针类的内容,不过这里也不好做解释。如果朋友们有兴趣,是可以CSDN里面搜索智能指针,非常多的,而且这里只讲解不同的地方,对后面的了解顺序也打个基础,而且C语言也不是没有智能指针,但实现方式是完全不一样的,是依靠编译器本身的。


后记:

如果有人对于本博客有疑问,可以通过我的QQ号2320404851来联系我
  • 213
    点赞
  • 643
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 40
    评论
C++中,`memcpy`和`memmove`都是用于将一段内存中的数据复制到另一段内存中的函数,但它们在实现和使用上有些许区别。 `memcpy`函数是将源内存区域的数据复制到目标内存区域,复制的字节数由第三个参数指定。这个函数的特点是只关心数据的拷贝,不考虑数据中是否存在重叠区域。如果内存区域有重叠,那么`memcpy`函数的行为是未定义的,可能会导致数据的错误复制。因此,`memcpy`适用于内存区域不重叠的情况。 `memmove`函数也是将源内存区域的数据复制到目标内存区域,复制的字节数由第三个参数指定。与`memcpy`不同的是,`memmove`函数会判断源内存区域与目标内存区域是否有重叠,如果有重叠,`memmove`函数会保证数据的正确性,即先将源内存区域的数据复制到一个临时缓冲区中,然后再将数据复制到目标内存区域中。因此,`memmove`函数适用于内存区域有重叠的情况。 下面举个例子来明它们的区别:假设有一个数组`arr`,它的长度为10,现在我们要将前5个元素复制到后5个元素的位置。如果我们使用`memcpy`函数,代码如下: ```c++ int arr[10] = {1, 2, 3, 4, 5, 0, 0, 0, 0, 0}; memcpy(arr + 5, arr, 5 * sizeof(int)); ``` 这段代码的意思是将`arr`数组的前5个元素复制到后5个元素的位置。但是由于`memcpy`函数不考虑数据重叠的情况,所以这段代码的行为是未定义的,可能会导致数据的错误复制。 如果我们使用`memmove`函数,代码如下: ```c++ int arr[10] = {1, 2, 3, 4, 5, 0, 0, 0, 0, 0}; memmove(arr + 5, arr, 5 * sizeof(int)); ``` 这段代码的意思是将`arr`数组的前5个元素复制到后5个元素的位置。由于`memmove`函数会判断数据重叠的情况,并保证数据的正确性,所以这段代码的行为是正确的,可以正确地将数据复制到目标内存区域中。 总的来,如果你确定内存区域不会重叠,那么可以使用`memcpy`函数。如果内存区域有可能重叠,那么应该使用`memmove`函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 40
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长高

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值