打开C嘎嘎的大门:你好,C嘎嘎!(2)

news2024/9/22 12:33:30

前言:

  小编在今天已经学完了C嘎嘎的入门知识了,在自己敲了一遍代码以后,开始今天这篇代码的书写了,以加强我的记忆,下面废话不多说开始进入今天的讲解环节:

目录:

1.缺省参数

1.1.缺省参数的概念

1.2.全缺省

1.3.半缺省

2.函数重载

2.1.函数重载的概念

2.2.函数重载的三种表现形式

3.引用

3.1.引用的概念和定义

3.2.引用的特性

3.3.引用的使用

3.4.const引用

3.5.指针和引用的关系

4.inline

5.nullptr 

正文:

1.缺省参数  

 1.1.缺省参数的概念

  缺省参数(有的地方也叫默认参数,感觉这个名字理解起来它的用法会更好)是声明或者定义函数时候为函数的参数指定一个缺省值,这是C++比C多出来的功能,简单来说,平常我们在使用函数的时候一般如果我们在形参的时候设定了参数,类似下面这样:

int add(int a)
{

}

  那么这个时候我们在调用函数的时候一定是要传递一个实参的,不然就会说函数调用的参数过少, 祖师爷(C嘎嘎创始人)可能觉着这样有点不太合理,于是就特地的设置出缺省参数这个概念,下面小编来讲一下缺省参数如何定义。

 1.2.缺省参数的定义

  对于缺省参数的定义,在我们知晓缺省参数的概念以后,其实已经变得很简单了,以小编的话来说,就是给形参附上一个值,这个值就是缺省值,如果我们在调用函数的时候没有给上实参是谁,那么就会默认使用缺省值,小编就以下图所示:

using namespace std;  //这个说法按理说是不太规范的,但是对于讲解小编直接这么写了

void add(int a = 10)
{
	cout << a << endl;
}

int main()
{
	add();
	add(1);
}

   上面就是对于缺省参数的定义,说白了讲,就是在形参的时候就赋值,从而可以减少我们在调用函数的时候会出现参数过少这个错误,这个功能的增加小编觉着还是蛮不错的。对于缺省参数,也同样分为了两种,分别是全缺省(给每个形参都赋值),半缺省(给部分形参赋值),下面小拜年都分开详细说说这全缺省和半缺省的具体定义和使用。

1.3.全缺省

  全缺省,顾名思义,就是每个形参都有缺省值,下面为了让读者朋友有着更好的了解,小编直接给出一段代码带大家认识一下全缺省:

using namespace std;

void ceshi(int a = 10, int b = 20, int c = 30)  //这就是全缺省的样子,每个形参都有缺省值
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
	cout << "———————" << endl;   
}

int main()
{
	ceshi();   //这个默认全部使用缺省值
	ceshi(1);   //这个除了a都用缺省值
	ceshi(1,2);  //这个除了a,b都使用缺省值
	ceshi(1,2,3);   //这个全部用的给定的值
	return 0;
}

   上面就是全缺省,对于缺省参数的函数调用,我们必须从左往右给缺省值,不能出现一下集中情况:

add(,1,2)         add(,,3)

  可能有很多读者朋友会问问什么,咋说呢,这个是祖师爷定的规矩,我们就得照着规矩来,这是规定好的,小编也不知道该如何去解释,小编突然想起看过的一句话:如果觉着这种代码的书写方式是不好的,那么各位可以从现在开始努力,争取将来也能发明出一种语言,来使用你想要的写代码方式,当然这是玩笑话,有质疑精神是好的,我们不能死脑筋,对于前人研究的就认为是对的,我们要带着批判性的思维去看待世界,行了说的废话多了点,下面我们进入下一环节,对于半缺省的介绍!

 1.4.半缺省

  对于半缺省参数,小编也在前面提到了,半缺省就是对部分的形参赋予缺省值,当然我们不能想给哪个形参缺省值就给哪个形参,这也是有规定的,对于半缺省,我们应该从右到左连续给缺省值,而不能随机给,下面小编就通过一个代码的例子来带读者朋友了解并且学会使用半缺省:

using namespace std;

void ceshi(int a , int b = 20, int c = 30)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
	cout << "———————" << endl;
}

int main()
{
	//对了,对于半缺省,我们不能在出现ceshi()这种情况了,里面至少有一个参数来进行传递
	ceshi(1);
	ceshi(1,2);
	ceshi(1,2,3);
	return 0;
}

  上面就是关于半缺省的使用,其实它的使用方法就是比全缺省少了一个参数,并且至少传一个参数而已,其他就和全缺省完全一样,不过我们一定要记得对于缺省值的书写,一定是从右往左依次给,也就是至少最右边有一个参数,如果最右边都没有那就说明这个函数没有缺省参数,以上就是对于缺省参数的介绍,各位一定要好好的掌握,对了,对于缺省参数,在我们函数声明和定义分开时(参考前面的顺序表),缺省参数不能在函数的声明和定义中同时出现,参数值只能在声明中体现,下面进入C++比C多的另一个东西:函数重载。上车喽!

2.函数重载 

 2.1.函数重载的概念

  正如小编上面所说,函数重载是C++比C多出来的一项功能,C++允许在同一个作用域中出现许多个一样的函数名,但是要求这些同名函数的形参不同,个数不同,参数顺序不同,这可是比C要好太多了,在小编当时学C的时候可不能这么做,下面小编就依次来讲述一下可以函数重载的三种方式:

 2.2.1形参的类型不同

  正如标题所言,如果函数里面的形参不同,我们是可以进行函数名字重复利用的,对于其具体描述,小编就放到下面的代码页了:

using namespace std;
//此时下面的add函数的形参是两个整形
int add(int a, int b)
{
	return a + b;
}
//此时下面的add函数的形参是两个浮点型,类型不同,所以函数可以重载
double add(double a, double b)
{
	return a + b;
}
 2.2.2.形参的个数不同

  如果形参个数不相同的话,函数也是可以重载的,小编用下面的代码来给各位读者朋友举例子:

using namespace std;

//下面的形参数量是两个
int add(int a, int b)
{
	return a + b;
}

//下面虽然与上面函数名是相同的,但是形参数量是三个,所以函数可以重载
int add(int a, int b, int c)
{
	return a + b + c;
}
 2.2.3.形参的类型顺序不同

   其实主要是顺序不同,小编之所以加上类型,其实也是一个意思,总体来说就如下面代码所说这样:

using namespace std;

//下面的add函数是里面先放着一个整形a,然后放着浮点型b
void add(int a, double b)
{
	cout << "add(int a, double b)"<< endl;
}

//下面的add函数是里面先放着一个浮点型b,然后放着整型a,因为类型顺序不同,所以可以函数重载
void add(double b, int a)
{
	cout << "add(double b, int a)" << endl;
}

  对于函数重载,这三种情况符合一种就可实现,有些读者朋友可能会问了,如果返回值不同可以函数重载吗?答案自然是不可以的,咱们就以下面的代码举例进行讲解

using namespace std;

int add(int a, int b)
{
	cout << "add(int a, int b)1" << endl;
}

double add(int a, int b)
{
	cout << "add(int a,int b)2" << endl;
}

    这样自然是不可以的,在我们调用函数的时候,这俩情况均可,此时编译器是分不清的,会直接出错,并且告诉了我们出错原因,读者朋友们不要去犯这样的错误:

  本来我觉着函数重载写到这里就可以了,小编突然想起一个比较坑的题目,下面小编给大家分享一下,有点小坑但不多。

 2.2.一个比较坑的代码 

void f1()
{
	cout << "f()" << endl;
}

void f1(int a = 10)
{
	cout << "f(int a)" << endl;
}

  仔细一看,第二个函数涉及到了小编之前讲的一个知识点:缺省参数,不知道各位读者朋友觉着这么写对不对?按理来说,这么写应该是正确的,确实,在语法层面上,这个写法是没问题的,可以理解为参数个数是不同的,但是实际上,如果把这段代码交给编译器去编译的话,是会报错的,因为编译器这个时候也迷糊,假如我们调用函数的时候,写的是:fi(),按理说,f1里面有缺省参数,这里不调用会自动调用缺省数,但是这样的话第一个和第二个都没啥问题,所以编译器分不清了,是会报错的,所以我们在学习语法的时候,有时候语法对了但是实际应用上确实不同的,所以理论是理论,实际是实际,我们如果想知道一个代码的好坏,不仅要做到语法对,还要让编译器所认可,当时小编就被这个题坑了,以为这是对的,结果这个不对,大家引以为戒,下面进入C嘎嘎的重点之一——引用! 

3.引用 

 3.1.引用的概念

  引用不是新定义一个变量,而是在已有变量的基础上给变量取一个别名(可以类比为我们日常生活中的小名,外号),就比如在水浒传中,我们知道一个好汉叫做李逵,宋江叫“铁牛”,江湖人却称他为“黑旋风”,引用变脸就是类似这样的,值得一体的是,编辑器不会尾为引用变量开辟新的内存空间,它和它引用的变量共用一块内存空间(等会小编在说引用的定义的时候用图文进行解释),概念已经讲完,下面小编开始进入对于引用定义的讲解。

 3.2.引用的定义

  对于引用的定义,正如上图所示,可能有许多读者朋友在这个时候会对" & "这个操作符的使用会有一点不解,因为在C语言中,这个操作符的含义是取地址操作符,但是在C++中,这个操作符在这里是引用操作符,这里小编顺便吐槽一下C++的一个小缺点,C++有时候对于一种新的概念的定义,用的操作符可能以前在C语言的的时候学习到的操作符,所以每个操作符可能会有双重含义,小编认为可能祖师爷认为符号太多会让人记不住,所以才用的一个操作符会有多重含义,在这里小编希望以后的C++的语法会多引进一些符号来取代一些多重含义的符号,行了,感觉我说的越来越偏离主题了,下面我们进入对于引用的代码呈现。

  下面我们就以下面的代码来举例子:

int a = 10;

  我们如果此时想使用引用变量,那么我们就可以参考上面小编定义的方式来写,可以看小编下面举出的例子:

int main()
{
	int a = 3;
	int& b = a;
	int& c = a;
	int& d = a;
	int& e = a;
}

  此时b,c,d,e都是a的引用(小名),此时它们其实指向的都是同一个值,如下图所示: 

  此时我们可以知道这几个变脸都是存的一个值,并且小编还说过,我们在使用引用操作的时候,是不会开辟空间的,这几个引用变量都是指向的同一块地址,可能有许多读者朋友是不相信的,不要着急,小编通过打印它们的地址来验证小编的说法:

   可以看出,这些变量的地址都是相同的,所以验证了小编说的,它们是“不开辟”(记住这句话)空间的,它们是指向同一个空间的,为了让各位读者朋友可以更形象的去了解这个性质,小编花了个图来帮助各位理解,如下图所示:

  当然,我么在引用定义完以后,如果我们对引用变量做出改变,它原本指向的变量也会改变,这和它们共用一块空间相关(毕竟小名指向的都变了,本体指向的肯定也会改变),这个点大家一定要记住,待会要用! 在讲完引用的概念和定义以后,下面准备进入引用的特性环节!

 3.3.引用的特性  

  对于引用的特性,小编准备从一下三部分入手去讲,下面先开始第一部分的讲解,上车啦:

1.引用在定义时必须初始化

  这个还是蛮好理解的,小编在前面说了,我们在使用引用操作的时候,其实就是给变量取小名,如果我们连变量都没有,怎么再给变量取小名?所以我们在使用引用的时候一定记着初始化,这里小编就要提到一个跟引用类似,但是不是必须要初始化的一个变量了,那就是在C语言让别人闻风丧胆的指针,指针在定义的时候不一定非要初始化,其实仔细一想,指针和引用其实是很相似的,当它们所指向的内容改变以后,本体也会改变,但是他俩也是会有区别的,小编这里先设置一个悬念,等会小编就会做出解释。

 2.一个变量可以有多个引用

  其实对于这个性质,小编已经在定义的时候使用过了,我们用引用的时候可以对一个变量进行重复的使用,毕竟一个人的外号可能会有很多种,这里小编就不做出过多的解释了,下面直接进入最后一个性质,这个性质很重要,关乎着后期一个比较重要的知识点,各位已经要理解

 3.引用一旦引用了一个实体,就不能在引用其他实体了

  首先我们先看这一句话,小编在当初想到是,引用是真专一,对于这句话的理解,小编给出自己的理解,对于引用,我们在引用一个实体的时候,也就是给一个实体起了外号以后,此时这个引用变量就是这个实体的外号了,自己的外号可以几个人共用吗,想必是不可以的,所以引用在引用了一个实体以后,就不能在引用其他的实体了,这里小编通过下面的代码来给各位举例子:

using namespace std;

猜一猜这一句代码之后,b的地址是啥
int main()
{
	int a = 2;
	int x = 3;
	int& b = a;
	b = x;
	cout << &a << endl;
	cout << &b << endl;
	cout << &x << endl;
}

  根据小编说过的特性,猜测此时b的地址其实还是a的地址,到底对不对呢?且看小编最后的运行图:

   可以发现此时的运行图正好符合了小编的解释,同时也很好的解释了引用过了一个实体以后,就不能在引用其他的实体了!此时其实也说明了一句话,引用是无法改变变量的指向的,但是指针是可以随时改变指向的,这里小编提早说一下,C++中引用是无法替代指针的,就是因为这个特性,后面小编也会说明这个的,因为小编会在后面越讲越能体现出引用和指针之间的相似性。

  此时引用的特性小编已经讲完,下面我们进入下个环节:引用的使用

3.4.引用的使用和注意事项

   可能很多读者朋友会说小编前面不是已经说明了引用的使用方法了,难道小编是想水字数再写一篇吗?不要把小编说的那么坏,上面是浅层次的引用的使用,下面小编准备讲一些比较深层次的引用的使用,那么小编也不卖关子了,开始进入引用使用的讲解

 1.引用在实践中主要是于引用传参,从而减少拷贝提高效率

  对于引用,其实它最大功能的体现就是我们在传参的时候,可以通过传引用参数来代替指针,就比如下面的代码所示:

using namespace std;

void swp(int& a, int& b)
{
	int t = a;
	a = b;
	b = t;
}

int main()
{
	int a = 12, b = 13;
	cout << a << " " << b << endl;
	swp(a, b);
	cout << a << " " << b << endl;
	return 0;
}

  对于上面的代码,可能各位读者朋友是看不懂的,下面小编就给出大家解释,大家在C语言阶段学习的时候,我们在实现两个数交换的时候,是需要传址调用,所以应该用指针来接受,这样才能实现两个数的交换,那么为什么我们直接传引用值就可以交换呢?其实这和小编在上面讲的引用的特性有关,引用值和本体是占用同一块内存空间的,所以引用值改变,本体就会改变,此时我们把引用参数传递过去,并且让引用参数实现了交换,其实就是对本体进行了交换,所以其实对于传地址的操作,我们通过引用操作同样可以去实现,并且我们在使用引用去接受的时候,其实都不会额外在分配一些空间,当我们传值的时候会额外的去开辟空间,所以我们可以减小空间的开辟从而提高效率,此时也体现出了指针和引用的相似之处。

 2.函数的返回值类型也可以是引用,可以在改变引用对象的同时同时改变被影响对象

  引用可以当做函数的返回值吗?当然可以,那么我们在什么情况下会使用这个呢?其实什么情况都可以,下面小编给出一个例子来让各位读者朋友知道引用的好处:

using namespace std;

int ceshi(int* arr)
{
	return arr[9];
}

int& ceshi1(int* arr)
{
	return arr[9];
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	ceshi(arr)++;
	ceshi1(arr)++;
	return 0;
}

  上面小编分别写出了俩函数,它们除了返回值不同,其他都是相同的,我们让两个函数同时++,我们可以发现第一个函数会出现错误,它的报错原因是:表达式必须是可修改的左值,为什么第一种函数是不可以通过的呢?下面小编通过两幅图来带你了解两个函数的不同:

  首先我们可以看第一个函数,它的返回值类型是int,那么它是如何去把书给return回来的呢?其实这个函数在传回来值的时候,会先创建一个临时对象来接受此函数的值,临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象,在C++规定中,临时对象具有常性,然后通过临时对象传递回主函数,所以我们不能对他进行运算,它是属于等号右边的!如下图所示:

  而对于返回值是引用的话,那么我们此时返回的就是返回值的小名,这里就没有了所谓的临时对象掺和,直接把小名传递过来了,所以我们可以对其进行运算,本质上还是变量,如下图所示:

  从这里我们可以看出用引用做返回值的方便,但是我们是不是可以任何情况下都可以吧引用作为返回值?那肯定是不可以的,对于某些情况我们是不可以用引用作为返回值的,下面小编通过一个例子来给各位读者朋友做出解释:

int& add()
{
	int ret = 1;
	ret++;
	return ret;
}

  对于这个函数此时我们用引用做返回值就意味着,我们会返回ret的小名,但是问题就出在这里,ret属于在add这个函数局部域内部的,我们在调用完这个函数以后,此时函数已经销毁,ret自然也跟着销毁了,但是我们却传递回了它的小名,说明此时传递的就是一个类似野指针的东西,小编称它为野引用,各位以后一定要小心这种情况。

 3.引用传参跟指针传参的功能是类似的,引用传参相对方便些(注意上面提到的特殊情况) 

  这个小编上面讲了就不在多赘述了,记住就好。

 4.引用和指针是相辅相成的,在C++中谁也替代不了谁

  这个小编在上面已经解释清楚原因了,这里再说一下,有的读者朋友可能觉得引用和指针那么相似,并且引用比指针要更好用,我们为什么不让引用替代指针呢?因为小编讲的指针的第三个特性,我们就知道引用无法改变变量指向,但指针可以,所以例如链表这种数据结构,还得是指针来实现。以上就是对于引用的使用,下面我们进入下一小节!看到这里,各位是否觉得:

3.5.const引用 

  const,我们并不陌生,在我们学习C语言的时候就见过它了,它是用来限制变量的,小编在之前的文章写过,忘记了的读者朋友可以去回顾一下,小编先把链接放到下面:深入理解并打败C语言难关之一————指针(1)-CSDN博客

  小编在之前说过,引用和指针是类似的,所以引用也是可以被const修饰的,我们是可以引用一个const对象的,不过在这个时候我们一定要注意,此时我们也需要被const引用,如果出现了下面这种情况,是会报错的:

	const int a = 2;
	int& x = a;

  此时相当于我们把a的权限给放大了,本来a的数值已经固定了,如果我们用下面的方式来书写的话,这样会导致我们可以通过修改x来改变a,这样就是我们在引用过程中把权限给放大了,在C++中,我们只能权限缩小或者权限相等,下面给出正确的两个例子来说明我们如何去使用const引用:

int main()
{
	const int a = 2;
	const int& x = a;  //此时代表着权限相等

	int b = 2;
	const int& y = b;  //此时经过引用之后我们把权限给缩小了,也是没问题的
	return 0;
}

  上面才是我们使用const引用的正确用法,所以我们一定不要犯权限放大的错误,权限放小或者相等是都可以的,这里有个需要注意的代码问题,看看以下代码,读者朋友们判断一下小编说的是不是正确的:

int a = 2;
int& b = a * 2;

   这个肯定是不对的,可能有些读者朋友会觉的奇怪,感觉这么写是没毛病的,这其实也算是一个一个坑,我们看引用的对象是谁,不是a,也不是2,是a * 2,此时这个表达式其实是被临时对象保存着,小编在上面说过,临时对象具有常性,所以我们如果这么用,相当于权限放大了,此时我们需要在int前面在加上const,用来进行缩小数据,此时这个代码就是正确的,各位读者朋友一定要记得这个坑,这就是const引用的大致内容,如果小编没写全的话后续会补充,下面我们进入引用的最后一节:引用和指针的关系!

3.6.指针和引用之间的关系

  在C++中,指针和引用就像是两个性格迥异的亲兄弟,指针是老大哥,而引用是弟弟,它们功能有着重叠性,但也各自有所不同,它们各自有自己的特点,都不可以被对面替换掉,下面我们来说说二者之间有什么各自鲜明的特点

 1.在语法的概念上,引用不需要开辟空间,而指针需要开辟空间。

  本来小编在这里不太想多解释的,但实际上,在底层中,引用也是需要开辟空间的,此时我们需要借助汇编代码进行查看,下面小编放上两张图各位就知道小编为什么说引用其实也开辟空间了:

  首先我们先不用管汇编代码是什么,我们可以惊奇的发现,无论我们是用指针或者是用引用,这俩汇编代码是一样的,所以从这里可以看出,其实引用在底层上是创造了空间,不过在语法上这是不创造空间的,这个小知识点各位可以记住!

 2.引用在使用时必须初始化,但是指针可初始化,也可以不初始化。

 3. 引用在初始化一个对象以后,就不能再引用其它对象,指针在初始化完一个对象后,可以接着初始化别的对象。

 4.引用在访问对象时可以直接访问,指针需要用到接引用操作符才可以访问。

 5.指针比较容易出现野指针或者空指针问题,引用相对而言出现的少一些(前面举过野引用的问题)

 6.引用的字节数取决于引用对象类型,指针看操作系统类型

  好了,这些便就是引用的相关知识点,引用是一个很重要的概念在C++中,大家一定要好好掌握!

4.inline

  inline也是C++新增加的一项功能之一,它是来修饰函数的,被它修饰过的函数叫做内联函数,编译时C++会在被调用的地方直接展开函数,这样就不需要在建立函数栈帧了从而可以提高效率。它的存在其实是想来替代我们C语言时学过的一个知识点:宏函数,就是用宏写过的函数,它也不是需要建立函数栈帧的,不过用宏写函数是比较繁琐的,就比如我们要知道在一些地方需要加上括号,所以此时我们祖师爷加了一个inline从而可以替代宏函数,下面小编说一说用inline时需要注意的几个点:

 1.inline对于编译器只是一个建议,有的地方就算加了inline编译器也会不展开,不同的编译器对于inline展开的情况都不太相同,因为C++规定中没有明确规定,inline适用于一些被频繁调用的短小函数,对于一些代码量大的,递归函数是不会展开的

  其实编译器也是有自己思想的,事项一下,如果一个代码量比较大的函数展开了,是不是就增加了编译器的负担?所以inline对于编译器仅仅只是一个建议,至于展不展开还是得看编译器的,如果代码量小的函数会采用这个建议,代码量大的编译器直接忽略这个inline。

 2.对于VS这个编译器,在默认的debug情况下是不展开inline的,这样方便调试,如果想要展开函数,那么需要设置下面两个地方,小编通过图文告诉大家如何设置

   经过这几部操作后,VS就可以支持我们使用inline函数了,下面小编就来展示一下inline函数和正常调用函数时候的不同,请看下面小编用的展示代码:

inline int add(int a, int b)
{
	return a + b;
}

int add1(int a, int b)
{
	return a = b;
}

int main()
{
	cout << add(1, 2) << endl;
	cout << add1(1, 2) << endl;
	return 0;
}

  此时我们想要看看这俩的区别,又要去借用汇编代码去来调试来看一下inline后的函数是否还展开了,下面是两个函数的汇编代码对比图:

   此时我们可以明显看出上图和下图之间的不同,下图小编特意的标了一个红,注意此时的call,这个代表着函数的调用,也就是意味着函数栈帧的创建,这是我们写一般函数都要有的功能,下面我们再看上图,上面被inline修饰过的函数没有经过这一步,说明了上面的函数已经展开了,并没有函数栈帧的建立,所以我们以后想要增加程序的效率的话,我们可以对一些比较短的函数进行inline操作!下面小编再来讲一下最后一个注意事项inline的。

 3.inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时就会出现报错。

5.nullptr

  下面我们进入C嘎嘎入门最后一部分的讲解,nullptr也是C++新增的一项功能,它是来替代我们C语言时常常用到的NULL,NULL其实是一个宏,它的使用区分成了C和C++两种,下面的图正式NULL的宏的组成部分:

    从上图我们可以清晰的看出,在C语言阶段时的NULL代表着(void*)0,而在C++的NULL,其实就是0,无论采取了什么定义,在使用空的指针时,都会遇到一些麻烦,下面小编通过代码进行解释:

void add(int a)
{
	cout << "add(int a)" << endl;
}

void  add(int* ptr)
{
	cout << "add(int* ptr)" << endl;
}

int main()
{
	add(0);
	//本想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。
	add(NULL);

	//如果我们想要直接把NULL强制类型转换,那么就会出现编译报错,这么写在C++是不被允许的

	add((void*)NULL);
}

  此时正如上图所示,此时我们如果把除了第三个调用之外运行程序以后,会统一的出现第一种打印方式,因为此时的NULL就是0,可能一些读者是想把它强制类型转换成void*,但是很遗憾,这种操作在C++是不被允许的,各位读者朋友一定要记得这个点,所以为了让C++有NULL在C中的特性,于是引入了nullptr这个关键字,它是一种特殊类型的字面量,他可以转化成任意类型的指针,使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能隐式的转换成指针类型,而不是整形类型,如下卖弄的代码和图所示:

void add(int a)
{
	cout << "add(int a)" << endl;
}

void  add(int* ptr)
{
	cout << "add(int* ptr)" << endl;
}

int main()
{
	add(0);
	//本想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。
	add(NULL);
	add(nullptr);
	return 0;
}

  通过上图就可以清楚nullptr的作用,各位读者朋友要培养以后用nullptr的习惯,来替代NULL

总结:

  现在小编已经讲完了关于C嘎嘎的入门篇了,这篇文章小编自我感觉写的是有点啰嗦了,我想把我知道的知识都写到文章里,可能会有一些重复的话,读者朋友见谅,学完了C嘎嘎入门篇以后,小编觉着C嘎嘎蛮有趣的(以后可能不会),已经期待后续知识的学习了,如果文章有错误,恳请在评论区指出,小编会及时听取各位的意见,那么我们下篇文章见啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2155065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【416】【举报垃圾信息】

这题倒挺简单的 注意一下映射关系&#xff0c;再使用字典即可。 class Solution:def reportSpam(self, message: List[str], bannedWords: List[str]) -> bool:nlen(message)if n1:return Falsedictdefaultdict(int)num0for a in message:dict[a]1for b in bannedWords:if…

NXP实战笔记(十六):NXP 32K3xx系列单片机有关OTA升级的思考

目录 1、概述 2、参考资料 3、思考点1&#xff1a;需不需要传统BootLoader&#xff1f; 3.1、无需传统BootLoader 3.2、有传统BootLoader 4、OTA升级之后是否立即实施切换 5、兼容编程会话 6、APP内部集成34、36、37服务 7、Flash放置问题 1、概述 NXP的S32K3系列单片机…

江协科技STM32学习- P16 实验-TIM输出比较(PWD驱动LED呼吸灯,舵机,直流电机)

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

Redisson 总结

1. 基础使用 1.1 引入依赖 <dependencies><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId></dependency> </dependencies>包含的依赖如下 1.2 配置文件 其实默认主机就…

掌控历史:如何通过Git版本管理工具提升你的开发效率

先一览全局: git目录 一.打开git二.git bash的基础命令三.配置git四.仓库搭建五.文件操作和状态六.忽略文件七.gitee的使用1.添加公钥2.创建仓库 八.vs中使用git九.git分支常用命令十.文件差异比较十一.文件回溯和推进十二.合并冲突和消除十三.合并/压缩提交十四.远程仓库推拉十…

SkyWalking 环境搭建部署

架构简介 skywalking agent : 和业务系统绑定在一起,负责收集各种监控数据skywalking oapservice : 是负责处理监控数据的,比如接受skywalking agent的监控数据,并存储在数据库中;接受skywalking webapp的前端请求,从数据库查询数据,并返回数据给前端。Skywalking oapserv…

第一个Web项目(java+servlet+jsp)

通过百度网盘分享的文件&#xff1a;第一个Web项目 链接&#xff1a;https://pan.baidu.com/s/11vnAPeAf6Dtax7H6aYKZgA 提取码&#xff1a;1234 目录 声明&#xff1a; 简介&#xff1a; 注意&#xff1a; 操作步骤&#xff1a; 1.在idea中新建java项目&#xff0c;项目…

华为HarmonyOS地图服务 7- 在地图上绘制标记

场景介绍 本章节将向您介绍如何在地图的指定位置添加标记以标识位置、商家、建筑等。 点标记用来在地图上标记任何位置&#xff0c;例如用户位置、车辆位置、店铺位置等一切带有位置属性的事物。Map Kit提供的点标记功能&#xff08;又称 Marker&#xff09;封装了大量的触发…

TMS320F28335的定时器中断实验

TTMS320F28335 的 CPU 定时器有 3 个且均为 32 位,分别是 Timer0、Timer1、Timer2, 其中 Timer2 是为操作系统 DSP/BIOS 保留的,当未移植操作系统时,可用来做普 通的定时器。这三个定时器的中断信号分别为 TINT0,TINT1,TINT2,分别对应中断向量于 INT1,INT13,INT14。 1 …

C++速通LeetCode中等第15题-搜索二维矩阵II(两种方法)

方法一&#xff1a;二分法按行遍历查找&#xff1a; class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {for (const auto& row: matrix) {auto it lower_bound(row.begin(), row.end(), target);if (it ! row.end()…

苹果macOS 15.0 Sequoia正式版发布:iPhone应用镜像玩、手机消息电脑知

9月17日苹果向 Mac 电脑用户推送了 macOS 15 更新&#xff08;内部版本号&#xff1a;24A335&#xff09;&#xff0c;除了引入数个 iOS 18 的新功能外&#xff0c;macOS 15 Sequoia 还带来了全新的 Continuity 功能 ——iPhone 镜像。 iPhone 镜像功能可以让用户直接在 Mac 上…

高密原型验证系统解决方案(上篇)

0 引言 随着当今 SoC 设计规模的快速膨胀&#xff0c;仅仅靠几 颗当代最先进的 FPGA 已经无法满足原型验证的需求。简单的增加系统的容量&#xff0c;会遇到系统时钟复位同 步&#xff0c;设计分割以及高速接口和先进 Memory控制器 IP 验证等多重困难。此时&#xff0c;一个商用…

vscode软件在 C发中常用插件

一. 简介 本文简单介绍一下&#xff0c;当做 C开发时 vscode软件常用的插件。 vscode软件是 微软公司目前提供的一款免费的开发软件&#xff0c;可以通过 vscode官网下载 vscode。 二. vscode软件在 C开发中常用插件 注意&#xff1a;vscode软件安装后&#xff0c;可以直接…

表盘针头位置检测系统源码分享

表盘针头位置检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

JAVA开源项目 房屋租赁系统 计算机毕业设计

本文项目编号 T 041 &#xff0c;文末自助获取源码 \color{red}{T041&#xff0c;文末自助获取源码} T041&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…

MySQL深入原理

MySQL深入原理 索引、事务、日志原理、InnoDB引擎、缓存、锁 有4个数据库是属于MySQL自带的系统数据库&#xff1a; ​ mysql MySQL 系统自带的核心数据库&#xff0c;它存储了MySQL的用户账户和权限信息&#xff0c;一些存储过程、事件的定义信息&#xff0c;一些运行过程中…

WebGL系列教程九(动画)

目录 1 前言2 绘制立方体并进行纹理映射3 动画思路4 开始绘制4.1 在顶点着色器中声明旋转矩阵4.2 获取旋转矩阵变量并进行赋值4.3 计算角度4.4 每一帧都去绘制4.5 效果4.6 完整代码 5 总结 1 前言 上一篇我们讲了WebGL中的基础语法&#xff0c;现在我们已经讲过了三维物体的绘制…

14.面试算法-字符串常见算法题(三)

1. 字符串回文问题 1.1 LeetCode.125. 验证回文串 回文问题在链表中是重点&#xff0c;在字符串中同样是个重点。当初我去美团面试第一轮技术面的第一个算法题就是让写判断字符串回文的问题。 这个本身还是比较简单的&#xff0c;只要先转换成字符数组&#xff0c;然后使用双…

PS相关操作记录

1. 磨皮步骤 1.1. 图层操作 先对照片进行去瑕疵、液化等操作&#xff0c;操作完的图层&#xff0c;重命名为液化&#xff0c;方便识别。复制两个图层&#xff0c;分别改为“低频”、“高频”&#xff0c;低频在下&#xff0c;高频在上。选中“低频”图层&#xff0c;滤镜 -&g…

NodeJs文档

文件操作 // 1. 导入fs模块 const fs require(fs)文件写入 //异步写入 // fs.writeFile(文件名&#xff0c; 待写入的数据&#xff0c; 选项设置&#xff08;可选&#xff09;&#xff0c; 回调函数) fs.writeFile(./座右铭.txt, 三人行&#xff0c;必有我师傅, err > {/…