C语言是面向过程的,而C++是面向对象的
C和C++的区别:
C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)。
C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。 所以C与C++的最大区别在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”。
C与C++的最大区别:在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”,而就语言本身而言,在C中更多的是算法的概念。那么是不是C就不重要了,错!算法是程序设计的基础,好的设计如果没有好的算法,一样不行。而且,“C加上好的设计”也能写出非常好的东西。
很多小伙伴都老是会碰到疑问,其实还是基础没打扎实,这些题如果你不看答案你能知道多少呢?如果还有很多不知道就证明基础没打扎实,如果你还在入门纠结,如果你还在苦恼怎么入门!小编推荐一个学C语言/C++的学习裙【 六九九,四七零,五九六 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!
函数参数的按值传递机制:
给函数传递参数时,参数值不会直接传递给函数,而是先生成参数值的一份副本,存储在内存中的栈上,再把这个副本用于函数语句里,而不是使用初始值,我们来用一个程序更清楚的说明这一点:
#include
void swap(int a,int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void main()
{
int a = 1;
int b = 2;
printf("a=%d,b=%d ",a,b);
swap(a,b);
printf("a=%d,b=%d ",a,b);
}
程序运行结果:
a=1,b=2
a=1,b=2
可能有人会不解,为什么我们在调用了swap(a,b)函数后,a、b的值并没有发生交换,这就是我们说的按值传递机制,我们在把a、b作为参数传递给swap函数时,这里其实是生成了a、b的副本再供swap函数使用的,所以就算是a、b的副本发生了交换,a、b本身也还是原来的值。我们在这里要达到我们想要的功能,就要使用指针,修改如下:
#include
void swap(int *a,int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void main()
{
int a = 1;
int b = 2;
printf("a=%d,b=%d ",a,b);
swap(&a,&b);
printf("a=%d,b=%d ",a,b);
}
我们只是把函数swap里的所有a、b的部分分别换成了*a、*b,同时我们传递了&a和&b给swap函数,这样就能得到预期结果了。我们来看看我们到底做了何种修改,首先我们把a、b的地址(&a和&b)传递给swap函数,尽管这里还是会拷贝a、b地址的副本(我们假定是指针*pa和*pb)传递给swap,但是这两个副本pa和pb仍然是指向a、b的指针,它们和&a、&b是不同的指针,但是却指向了同一个地址,所以当我们使用“*指针”这种形式时就可以改变该地址内存放的值了。
可能有人要问C语言为什么要采用这种方式来传递参数呢,直接使用原来的实参不好吗?你想一个问题,假设C不使用按值传递,就拿上面的swap函数举例,那么当我们使用swap(1,2)这样调用函数时会发生什么?我们难道要把常量1和2进行直接交换吗,让1=2,2=1?这绝对不行。提一下在Pascal编程语言中,为了不给调用者的变量带来影响,在定义函数的时候,会特别地指定参数为变量参数,如果试图给参数为变量的函数传递常量,编译器就会报错。但是C语言讲究的是简单实用,所以它不可能背负常量修改这样的危险,也没有采用Pascal那样麻烦的方式来在语法上做文章,所以就使用了这种按值传递的方式来传递参数。
函数声明:
可能有的人会碰到这种情况,当他把上面的代码写成
void main(){...}
void swap{...}
这种顺序时程序就会报错,这时候就是编译器没有正确识别swap函数导致的,这就涉及到函数声明了。函数声明是一个定义函数基本特性的语句,它定义了函数的名字、返回值类型和每个参数的类型。事实上,可以将它编写为与函数头一模一样,只是要在尾部加一个分号表示语句结束。函数声明也叫作函数原型,因为它提供了函数的所有外部规范。函数原型能使编译器在使用这个函数的地方创建适当的指令,检查是否正确地使用它。在程序中包含头文件时,这个头文件就会在程序中为库函数添加函数原型。例如,头文件中就含有printf和scanf的函数原型,这个文件我们在C-Free 5mingwinclude目录下就可以找到。所以,如果你想把swap的定义放在main函数后面,可以像下面这样使用函数声明:
#include
void swap(int *,int *);
//也可以写成void swap(int *a,int *b);
void main()
{
int a = 1;
int b = 2;
printf("a=%d,b=%d ",a,b);
swap(&a,&b);
printf("a=%d,b=%d ",a,b);
}
void swap(int *a,int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
函数指针:
可能有人不理解我为什么要把函数和数组放在一章讲,主要是因为它们都和指针有莫大的关系,前面我们说了数组名就是一个特殊的指针,只是要注意sizeof(数组名)和&数组名两种特殊情况。其实函数名也是一种特殊的指针,我们的程序被编译器翻译成汇编语言时,函数会被翻译成为一段相对独立的汇编代码,函数名就是该段代码的首地址,每个函数的最后都会有一个ret或retn汇编指令,其实它就对应我们函数的return语句,不要以为有的函数没有return语句,其实只是因为该函数是返回空(void)写做return;所以就把return省略掉了。稍微修改下上面的程序:
小编推荐一个学C语言/C++的学习裙【 六九九,四七零,五九六 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!
,当我们使用void (*p)(int*,int*) = swap;这一句时其实就是定义了一个函数指针p,我们让指针p的值赋值为swap函数名(在我们这里是地址0x00401014),然后我们可以发现我们可以使用p(&a,&b)这样的方式来调用函数了,就和使用swap(a,b)完全一样,。当然我们还可以在最后再加上两句:
可以看到我们居然可以使用((void(*)(int*,int*))0x00401014)(&a,&b);这样的方式来通过地址0x00401014调用函数,(void(*)(int*,int*)其实就是定义了一个函数指针,(*)表示它是一个函数,void表示该函数返回值为空,(int*,int*)表示函数有两个int*类型的参数,然后我们使用它把地址0x00401014进行强制类型转换,这样我们的地址0x00401014就会被当成一个函数来使用了,要特别注意里面的括号,不要省略了,不然会报错的。
前面说了数组名作为指针有两种特殊的情况,那么函数名是不是也有sizeof(函数名)和&函数名这两种特殊情况呢,多说无益,当你不确定一件事时,就用代码把它打印出来:
这是我在C-Free5上面的打印结果,在dev-c++也差不多,但是在vc6上面就会报错,原因就是sizeof后面跟函数名会被vc6默认是违法操作,因为函数的代码数量是不确定的,所以函数的大小也不能确定,尽量不要出现sizeof(add)这样的操作,C-Free5和dev-c++则采用了相对比较温和的方式来处理;而"函数名=&函数名"这条确是成立的。
下面我们再来看看数组作为函数参数的情况下,我们使用sizeof会发生什么变化:
小编推荐一个学C语言/C++的学习裙【 六九九,四七零,五九六 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!
可以看到我们在main函数里调用sizeof(a)=40,而在作为参数传递给array函数后,再次使用sizeof(a)时大小就只剩下4了,这是为什么呢,因为数组名是一个特殊的指针,当它作为参数传递给函数时,这时候就会拷贝a的副本传递给函数,这个副本就是一个大小为4字节的指针,所以我们在函数调用里得到参数数组a的大小为4。如果我们要得到真实的数组大小,我们通常会采用额外的处理方式,一种就是我们前面在写压缩和解压函数时用到的,我们把数组名和数组大小都作为参数传递给函数;另一种是我们在数组的即为加上一个特殊的符号表示数组结束,比如字符串的结尾就是一个'',我们只要使用循环读取元素一直到结束符就可以得到数组大小,当然也可以用其它的符号表示结束,比如-1,这都是你根据实际情况自己定义的。
今天就到这里,欲知后事如何且听下回分解(手动滑稽)~
这些是C/C++能做的
服务器开发工程师、人工智能、云计算工程师、信息安全(黑客反黑客)、大数据 、数据平台、嵌入式工程师、流媒体服务器、数据控解、图像处理、音频视频开发工程师、游戏服务器、分布式系统、游戏辅助等