有哪个网站做ic,诸暨公司网站建设,企业宣传片怎么拍,个人网站怎么接广告大家好~我们接着【C】C入门知识详解#xff08;上#xff09;-CSDN博客来介绍另一些C入门基础知识。
1.缺省值和缺省参数 缺省参数就是声明或定义函数时为函数的参数指定一个缺省参数。在调用该函数时#xff0c;如果没有指定实参#xff0c;则采用该形参的缺省值#xf…
大家好~我们接着【C】C入门知识详解上-CSDN博客来介绍另一些C入门基础知识。
1.缺省值和缺省参数 缺省参数就是声明或定义函数时为函数的参数指定一个缺省参数。在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的参数。 有些地方把缺省参数也叫做默认参数。其实并不复杂看下面的函数例子就能理解了。
void Func(int a 0)
{cout a endl;
}
我们定义这个函数时会给形参一个默认值这个默认值其实就是缺省值当我们调用这个函数时传参和不传参情况如下。
Func();
Func(1); 我们可以看到不传实参时函数就用原本的a0这个默认值作为形参当给函数传参时传的什么形参就是什么。这里的0就是缺省值a就是缺省参数。 缺省参数分为全缺省和半缺省。全缺省就是全部形参给缺省值半缺省就是部分形参给缺省值。C规定半缺省参数必须从右往左依次连续缺省不能间隔、不能跳跃给缺省值。 //全缺省
void Func1(int a 1, int b 2, int c 3)
{cout a a ;cout b b c c endl;
}//半缺省
void Func2(int a, int b 2, int c 3)
{cout a a ;cout b b c c endl;
} 而且带缺省参数的函数调用C规定必须从左往右依次给实参不能跳跃、不能间隔给实参。 我们先看全缺省的函数Func1。有如下四种调用
Func1(); //什么都不传
Func1(4); //传一个实参
Func1(4, 5); //传两个实参
Func1(4, 5, 6);//传三个实参 半缺省的函数Func2因为第一个参数没有给缺省值所以必须传一个实参。有如下3种调用
Func2(4); //传一个实参
Func2(4, 5); //传两个实参
Func2(4, 5, 6);//传三个实参 缺省参数在实践中是非常有意义和价值的在学习中我们可以体会到。 函数声明和定义分离时缺省参数不能在函数声明和定义中同时出现规定必须函数声明给缺省值。 比如在头文件Func.h中我们声明上面的函数Func1和Func2
void Func1(int a 1, int b 2, int c 3);
void Func2(int a, int b 2, int c 3);
在源文件test.cpp中定义Func1和Func2
void Func1(int a, int b, int c)
{cout a a ;cout b b c c endl;
}
void Func2(int a, int b, int c)
{cout a a ;cout b b c c endl;
}
如果在test.cpp文件中函数定义的参数还是出现了缺省值就会报类似下面这样的错误显示重定义默认参数。 2.函数重载 C支持在同一作用域出现同名函数但是要求这些同名函数的形参不同可以是参数个数不同或者类型不同。返回值不能作为函数重载的条件。 这样C函数调用就表现出了多态行为使用更灵活。C语言是不支持同一作用域出现同名函数的。
2.1 参数类型不同
同样是实现两个数的相加一个是整数的相加一个是小数的相加在C语言中我们可能就会分成两个不同的函数来实现比如add1实现整数的相加add2实现小数的相加有了函数重载我们就可以用add一个函数名来实现这两个函数。
int add(int a, int b)
{cout int add(int a, int b) endl;return a b;
}double add(double a, double b)
{cout double add(double a, double b) endl;return a b;
}
同名函数那怎么调用呢 其实在函数调用时会根据我们传给函数的实参的类型自动调用相应的函数。
add(1, 2); //传int型实参
add(1.1, 2.2); //传double型实参 单看函数名我们就觉得自己在用同一个函数其实并不是一个函数。这样函数用起来就很有可读性。 2.2 参数顺序不同
函数重载的参数也可以是类型相同顺序不同看下面两个函数
void f(char c, int i) //先char后int
{cout void f(char c, int i) endl;
}
void f(int i, char c) //先int后char
{cout void f(int i, char c) endl;
}
调用时也是一样根据传给函数的实参的类型自动调用相应的函数 2.3 参数个数不同
名字相同参数个数不同也是函数重载。普通的例子在这里就不列举了我们说一下特殊的。
看下面两个函数一个函数无参一个函数带参这两个函数构成函数重载吗
void f1()
{cout void f1() endl;
}
void f1(int a 10)
{cout void f1(int a) endl;
} 构成函数重载。但是调用时会存在歧义。
当我们调用时给函数传参就会自动调用有参数的那个函数这个没问题 那当我们不给函数传参时应该调用哪个呢含缺省参数的函数无参也可以调用啊。所以此时函数调用就会有歧义程序会报错 所以大家在学习中要注意这些问题。 3.引用
3.1 引用的概念、定义及特征 引用不是新定义一个变量而是给已存在的变量起个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。符号为。类型 引用别名 引用对象 引用的出现其实针对的是C语言中的指针。引用其实很好理解比如在水浒传中林冲外号“豹子头”李逵江湖人称“黑旋风”等这些就是他们的别名而不管是豹子头还是林冲都代表的是同一个人引用也是一个道理。我们来看具体程序。
int a 0;
int b a; //给a取别名为b
int c a; //给a再取一个别名为c
我们可以不止取一个别名一个变量可以有多个别名。我们还可以给别名取别名如下。
int d b; //给别名b取别名为d 此时d相当于还是a的别名我们看看a,b,c,d的地址会发现它们的地址一摸一样。 这就验证了这句话编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间 。如果我对d加加呢 会发现a,b,c,d全都加了这也证明a,b,c,d是同一个都是a。 引用的特征 1.引用在定义时必须初始化 2.一个变量可以有多个引用 3.引用一旦引用一个实体就不能再引用其他实体引用不能改变指向 前两个特征很好理解我们重点说一下第三个特征。先看看下面这段代码。
int a 10;
int b a;int c 20;
b c;
思考一下这里 b c 是什么意思是把b变成c的别名还是c赋值给b 这里其实是赋值把c的值赋给bb是a的别名也就是把c的值赋给a。我们把a,b,c都打印出来看看 。 这也证明引用不能改变指向。我们还可以通过观察地址来看引用是否改变了指向。 可以看到a,b地址相同b还是a的别名和a共用同一块空间而c只是赋值给b。 3.2 引用的应用
引用在实践中主要用于传参和做返回值准确说就是用在函数。 1.函数传参其实是把实参拷贝给形参形参是实参的临时拷贝有了引用就可以减少拷贝。 2.引用从语法上来说形参是实参的别名形参也不会额外开辟空间效率就得到了提高。 3.在函数中我们知道形参的改变不影响实参想要通过函数改变实参就要传地址对吧学了引用后我们大部分情况就可以不用指针传地址实现了。 举一个最简单例子函数实现交换两个数在C语言中应该像下面这样传地址才能实现实参的改变。
void Swap(int* px, int* py)
{int temp *px;*px *py;*py temp;
}
当我们学了引用就不需要指针了我们直接将形参看作实参的别名
void Swap(int rx, int ry)
{int temp rx;rx ry;ry temp;
} 这里rx就是x的别名rx的改变就是x的改变ry就是y的别名ry的改变就是y的改变rx与ry的交换就实现了x与y的交换 。
在能使用指针的地方比如说栈队列等都可以尝试用引用会方便很多。引用做返回值我们后续再讨论。 3.3 coust引用
const引用被const修饰的量
一个被const修饰的变量a怎么给它取别名
const int a 10;
直接int ra a绝对是不可以的。这是一个经典的权限放大。被const修饰的变量其实就是让这个变量变得只能读不能写而int类型是可读可写的别名ra也可读可写a被const修饰只能读而现在来一个可读可写的别名这就让a权限放大了。
引用权限可以缩小不能放大。那应该怎么给const修饰的变量取别名呢像下面这样。
const int ra a; const引用正常变量
没有被const修饰的变量b可以直接用int rb b;来取别名
int b 20;
那可以像下面这样吗?
const int rb b;
可以这里就是引用权限缩小一个可读可写的b别名rb只能读。
但是这并不意味着b的权限缩小了用b这个名字的时候还是可读可写但是用rb这个名字的时候就只能读但rb还是b只是权限不同就像你在家可以想脱鞋就脱鞋在教室就不能随意脱鞋但你还是你只是权限不一样。 const引用常量
const引用还可以给常量取别名。比如说我要给10这个常数取别名。如果不加const就不行。
const int rc 10; const引用临时对象 const引用还可以给临时对象取别名。比如给ab这个表达式取别名如果不加const就不行。
//这里a和b有没有const无所谓
const int a 10;
int b 20;
const int rd a b;因为表达式的结果会存在临时对象里面临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象这个临时对象具有常属性。如果是 int rd a b;意思就是把ab结果的临时对象拷贝给rd。
再看下面这个我们怎么给double类型的d取一个int类型的别名
double d 3.14;
int i d;
上面这个d并不是直接赋值给id和i类型不同这里叫隐式类型转换隐式类型转换中间会产生临时对象存储。所以这里我们也不能直接用int ri d;取别名还是要加const。
double d 3.14;
const int ri d; //给d取int类型的别名
临时对象的生命周期会和别名的生命周期一样 3.4 引用和指针的区别
C中指针和引用就像两个性格迥异的亲兄弟指针是哥哥引用是弟弟在实践中他们相辅相成功能有重叠性但是各有自己的特点互相不可替代。 • 语法概念上引用是一个变量的取别名不开空间指针是存储一个要量地址要开空间。 • 引用在定义时必须初始化指针建议初始化但是语法上不是必须的。 • 引用在初始化时引用一个对象后就不能再引用其他对象而指针可以在不断地改变指向对象。 • 引用可以直接访问指向对象指针需要解引用才是访问指向对象。 • sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数32位平台下占4个字节64位下是8byte • 指针很容易出现空指针和野指针的问题引用很少出现引用使用起来相对更安全一些。 4.inline内联
1C语言实现宏函数会在预处理时替换展开但是宏函数的实现很复杂很容易出错且不方便调试C设计了inline目的就是代替C语言的宏函数。
2用inline修饰的函数叫内联函数编译时C编译器会在调用的地方展开内联函数这样调用内联函数就不需要建立栈帧了就可以提高效率。
3vs编译器debug版本下默认是不展开inline的这样方便调试debug版本想展开需要设置下面两个地方 4inline对于编译器而言只是一个建议也就是说你加了inline编译器也可以选择在调用的地方不展开不同编译器关于inline什么情况展开各不相同因为C标准没有规定这个。inline适用于频繁调用的短小函数对于递归函数代码相对多一些的函数加上inline也会被编译器忽略。
5inline不建议声明和定义分离到两个文件分离会导致链接错误。因为inline被展开就没有函数地址链接时会出现报错。 5.nullptr空指针
NULL实际是一个宏在.c文件中定义一个指针赋值为NULL然后按住ctrl点击这个NULL就会跳转到如下代码处。
#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif C中NULL可能被定义为字面常量0或者C中被定义为无类型指针void*的常量。不论采取何种定义在使用空值的指针时都不可避免的会遇到一些麻烦本想通过f(NULL)调用指针版本的f(int*)函数但是由于NULL被定义成O调用了f(int x)因此与程序的初衷相悖。f((void*)NULL);调用会报错。
插入一个小知识
在C语言中void*的指针是可以转成任意类型的比如
void* p1 NULL;
int* p2 p1; 而在C中语法更加严格上面的p1想赋值给p2必须强制类型转换成int*。
void* p1 NULL;
int* p2 (int*)p1; 我们来看具体例子下面这个函数重载实参传入NULL时会调用哪个函数第二个
void f(int x)
{cout void f(int x) endl;
}
void f(int* p)
{cout void f(int* p) endl;
} 结果调用了第一个函数。证明NULL在C中其实是0。
C11中引入nullptr,nullptr是一个特殊的关键字nullptr是一种特殊类型的字面量它可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题因为nullptr只能被隐式地转换为指针类型而不能被转换为整数类型。
简而言之就是在C中的空指针变成了nullptr不是NULL。 本篇到这里就结束了拜拜~