广州营销型网站优化,wordpress 外贸插件,盾思途旅游网站建设,天津市做网站的公司关键要点#xff1a;iostream 是 Input Output Stream 的缩写#xff0c;是标准的输⼊、输出流库#xff0c;定义了标准的输入、输出对象。std::cin 是 istream 类的对象#xff0c;它主要面向窄字符#xff08;narrow characters (of type char)#xff09;的标…关键要点iostream 是 Input Output Stream 的缩写是标准的输⼊、输出流库定义了标准的输入、输出对象。std::cin 是 istream 类的对象它主要面向窄字符narrow characters (of type char)的标准输入流。std::cout 是 ostream 类的对象它主要面向窄字符的标准输出流。std::endl 是一个函数流插入输出时相当于插入一个换行字符加刷新缓冲区。是流插入运算符是流提取运算符。C语言还用这两个运算符做位运算左移/右移使用C输入输出更方便不需要像printf/scanf输入输出时那样需要手动指定格式C的输入输出可以自动识别变量类型(本质是通过函数重载实现的这个以后会讲到)其实最重要的是C的流能更好的支持自定义类型对象的输入输出。IO流涉及类和对象运算符重载、继承等很多面向对象的知识这些知识我们还没有讲解所以这里我们只能简单认识⼀下C IO流的用法。cout/cin/endl等都属于C标准库C标准库都放在一个叫std(standard)的命名空间中所以要通过命名空间的使用方式去用他们。⼀般日常练习中我们可以using namespace std实际项目开发中不建议using namespace std。这里我们没有包含stdio.h也可以使用printf和scanf在包含iostream间接包含了。vs系列编译器是这样的其他编译器可能会报错。举例说明代码语言javascriptAI代码解释#includeiostream #includealgorithm using namespace std; int main() { int a, b; cin a b;//输入 cout a b endl; cout a b \n; return 0; }--用\n的效率会更高除此以外cout和cin的效率其实也是不如printf和scanf的但是我们可以通过取消同步流的操作来解决这个问题代码如下代码语言javascriptAI代码解释#includeiostream using namespace std; int main() { //取消同步流 //在io需求比较高的地方比如需要大量输入的竞赛题中加上以下3行代码,可以提高效率 ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }二、缺省参数关键要点缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的实参缺省参数分为全缺省和半缺省参数。有些地方把缺省参数也叫默认参数全缺省就是全部形参给缺省值半缺省就是部分形参给缺省值。C规定半缺省参数必须从右往左依次连续缺省不能间隔跳跃给缺省值带缺省参数的函数调用C规定必须从左到右依次给实参不能跳跃给实参函数声明和定义分离时缺省参数不能在函数声明和定义中同时出现规定必须函数声明给缺省值举例说明--包含全缺省和半缺省需要注意的地方注释都讲的比较清楚代码语言javascriptAI代码解释#includeiostream using namespace std; void func(int a 0) { cout a \n \n; } //全缺省 void func1(int a 10, int b 20, int c 30) { cout a a \n; cout b b \n; cout c c \n \n; } //半缺省 //从右至左依次缺省才可以中间不能跳跃缺省不能出现 int a10,int b20,int c或int a10,int b,int c30等这种情况 //如果非要a缺省的话我们可以把a和c的位置换一下void func2(int c, int b, int a 30) void func2(int a, int b, int c 30) { cout a a \n; cout b b \n; cout c c \n \n; } int main() { func(1);//1传参时使用的是指定的实参 func();//没有传参会使用缺省值 //以下4种都可以 func1(1, 2, 3); func1(1, 2); func1(1); func1(); //这种不行不能跳跃传参 //func1(1, , 3); func2(1, 2, 3); func2(1, 2); return 0; }缺省参数在改进数据结构中也使用的比较多我们这里以顺序表这个结构的几个接口来看看中间的实现我会比较省略详细的结构实现可以去看一下博主之前的博客SeqList.cppSeqList.htest.c代码语言javascriptAI代码解释#includeiostream #includeSeqList.h using namespace std; int main() { int n; cin n; SL s; SLInit(s, n);//已经知道n的大小 //如果没有输入操作这里n就是不知道的就会使用缺省的n4 //为啥要定义这么一个n呢 //比如我要尾插1000个数据那么不用这个的话就要不停的扩容会有消耗 for (int i 0; i n; i) { SLPushBack(s, i); } //Find //原来的find无法实现找顺序表中所有需要找的元素(重复的只会返回第一个) //改了之后通过下述操作可以找到所有的 // 5 4 6 3 4 7 4 // // 查找出所有的4 int x SLFind(s, 4); while (x ! -1) { x SLFind(s, 4, x 1); } return 0; }三.函数重载我们知道在C语言中不支持同一作用域中出现同名函数。但是C是支持的不过要求这些同名函数的形参不同可以是参数个数不同或者类型不同。这样C函数调用就表现出了多态的行为使用更加灵活三种构成重载的一般情况1.参数类型不同可以自动识别参数类型代码语言javascriptAI代码解释//1.函数参数类型不同 #includeiostream using namespace std; int Add(int left, int right) { cout int Add(int left, int right) endl; return left right; } double Add(double left, double right) { cout double Add(double left, double right) endl; return left right; } int main() { cout Add(1, 2) \n; cout Add(1.1, 2.2) \n; //自动识别函数传的参数类型调用不同的函数 return 0; }2.参数个数不同代码语言javascriptAI代码解释//2.函数参数个数不同 #includeiostream using namespace std; void f1() { cout f1() \n; } void f1(int a) { cout f1(int a) \n; } int main() { f1(); f1(1); //根据传参的个数自动识别调用对应函数 return 0; }3.参数类型顺序不同可以自动识别代码语言javascriptAI代码解释//3.函数类型顺序不同--本质上还是对应的函数参数类型不同 #includeiostream using namespace std; void f1(double a,int b) { cout f1(double a,int b) \n; } void f1(int a,double b) { cout f1(int a,double b) \n; } int main() { f1(2.3,1); f1(1,2.3); //根据传参的顺序来调用对应函数 return 0; }两种特殊情况无法构成重载(函数的仅仅只是返回类型不同)代码语言javascriptAI代码解释//1.无法构成重载 #includeiostream using namespace std; void fork() { } int fork() { return 1; } int main() { //调用时无法确定调用那个 fork(); int x fork(); return 0; }E0311无法重载仅按返回类型区分的函数可以构成重载但是调用时会出现歧义(缺省的情况)代码语言javascriptAI代码解释//2.可以构成重载 //但是一些情况下调用存在歧义 #includeiostream using namespace std; void f1() { cout f() endl; } void f1(int a 10) { cout f(int a) endl; } int main() { f1(1);//这个可以 f1();//这样调用就不行了不确定调用那一个 return 0; }四.引用引用的概念和定义引用并不是定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它的引用变量共用同一块内存空间。写法类型 引用别名 引用对象在C中为了避免引入太多的运算符会复用C语言的一些符号比如前面的和以及这里的可以是取地址引用还可以是按位与运算符特别容易混淆大家需要注意区分的角度。引用的特性引用在定义时必须初始化一个变量可以有多个引用引用一旦引用⼀个实体再不能引用其他实体举例说明代码语言javascriptAI代码解释#includeiostream using namespace std; int main() { int i 1; int j i;//j是i的别名 //一个变量可以有多个别名引用。别名也可以有它的别名 int k j; k;//k的变化会影响i和j cout i \n; cout j \n; cout k \n; //打印发现地址一样 //引用时必须先初始化 //int x;//看报错 //x i; // 引用一旦引用一个实体再不能引用其他实体,引用也不能改变指向 //所以这里的操作是赋值而不是引用 int m 20; k m; return 0; }引用的使用引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被引用对象。引用传参跟指针传参功能是类似的引用传参相对更方便⼀些。引用返回值的场景相对比较复杂我们在这里后续会简单讲一下场景还有一些内容后续在类和对象的学习中会继续深入了解。引用和指针在实践中相辅相成功能有重叠性但是各有特点互相不可替代。C的引用跟其他语言的引用(如Java)是有很大的区别的除了用法最大的点C引用定义后不能改变指向Java的引用可以改变指向。一些主要用C代码实现版本数据结构教材中使用C引用替代指针传参目的是简化程序避开复杂的指针但是我们可能当时还没学过引用导致一头雾水。举例说明例子1(交换值在顺序表中的应用)代码语言javascriptAI代码解释//指针引用 //大部分情况下引用都可以替代指针除了一些特殊情况比如链表的树的节点的定义只能使用指针 #includeiostream using namespace std; void swap(int* x, int* y) { int tmp *x; *x *y; *y tmp; } void swap(int x, int y) { int tmp x; x y; y tmp; } int main() { //swap函数交换值 int a 1; int b 7; cout a b \n; swap(a, b);//用指针 cout a b \n; swap(a, b);//用引用 cout a b \n; return 0; }例子2(交换指针在链表中的使用)代码语言javascriptAI代码解释//替代二级指针使用 #includeiostream using namespace std; void swap(int**x, int** y) { int*tmp *x; *x *y; *y tmp; } void swap(int* x, int* y) { int*tmp x; x y; y tmp; } int main() { //swap函数交换指针 int a 1; int b 7; int* pa a; int* pb b; cout *pa *pb \n; swap(pa, pb);//用指针 cout *pa *pb \n; swap(pa, pb);//用引用 cout *pa *pb \n; return 0; }--我们再来看看在链表中的使用还是省略化的展示代码语言javascriptAI代码解释//在链表中的使用 #includeiostream using namespace std; typedef struct SListNode { struct SListNode* next; int val; }SLTNode;//, //*PSLTNode; //typedef struct SListNode SLTNode; //typedef struct SListNode* PSLTNode; //以前使用二级指针的实现方法 //void SLTPushBack(SLTNode** pphead, int x) //{ // SLTNode* newnode; // malloc // // if (*pphead NULL) // { // *pphead newnode; // } // else // { // // 找到尾结点newnode链接到尾结点 // } //} //void SLTPushBack(PSLTNode phead, int x)//这个在书上有时候会用 void SLTPushBack(SLTNode* phead, int x) { SLTNode* newnode NULL; // malloc这里省掉过程 if (phead NULL) { phead newnode; } else { // 找到尾结点newnode链接到尾结点 } } int main() { //用二级指针 SLTNode* plist NULL; //SLTPushBack(plist, 1); //SLTPushBack(plist, 2); //SLTPushBack(plist, 3); //SLTPushBack(plist, 4); //用引用 //PSLTNode plist NULL; SLTNode* plist NULL; SLTPushBack(plist, 1); SLTPushBack(plist, 2); SLTPushBack(plist, 3); SLTPushBack(plist, 4); return 0; }我们来看一个可能有的教材上会出现但是很容易把我们看懵的形式(注意看图片解释)-在大部分的场景下引用都可以代替指针使用但是在链表树的节点定义位置只能使用指针引用无法替代。这是因为C的引用无法改变指向节点一定存在改变指向的场景传值返回传引用返回传值返回(主要看错误的地方)代码语言javascriptAI代码解释#includeiostream using namespace std; int fun() { int ret 0; return ret; } int main() { int x fun();//x接受的其实是ret的拷贝值在fun函数销毁时ret就没了通过临时变量带出 //fun() 1;//所以这里就无法直接修改 return 0; }这里返回接收的是ret吗--不是是ret的临时拷贝传引用返回(主要看错误的地方代码语言javascriptAI代码解释//传引用返回 #includeiostream using namespace std; int fun() { int ret 0; return ret; } int main() { intx fun(); cout x \n;//这里可能会是随机值 return 0; }这里的x可能是0也可能是随机值为什么 相当于野指针的访问比较危险--我们再来看看一个更奇怪的例子为啥x后面没有变但却是y的值依旧是看错误的地方代码语言javascriptAI代码解释#includeiostream using namespace std; int func1() { int ret 0; // ... return ret; } int func2() { int y 456; // ... return y; } int main() { int x func1(); cout x endl; func2();//明明没有修改x但x的值还是会为y这是因为fun1函数栈帧销毁后还给了操作系统但是func2又使用了这块空间 cout x endl; return 0; }为什么会出现这样的现象呢结合上一个例子可以思考一下1.销毁了之后为什么还可以访问--因为销毁了并不代表空间没了内存空间是可以反复使用的销毁的本质是还给操作系统跟租房一样返回的别名就相当于偷偷留了把钥匙但是这样是不可取的。2.这里相当于野指针的访问为什么没报错--越界不一定报错越界写可能会报错在数组中存在越界抽查这个现象。所以我们有时候编译器不会在这里报错的传引用返回的正确使用(顺序表为例)--我们这里的实现还是简略的写一下中间使用了引用的写法大家刚好可以看看注意看注释SeqList.h代码语言javascriptAI代码解释#pragma once #includestdio.h #includestdlib.h typedef struct SeqList { int* arr; int size; int capacity; }SL; void SLInit(SL pls, int n 4); void SLPushBack(SL pls, int x); int SLFind(SL pls, int x, int i 0); int SLat(SL pls, int i);//这里使用引用就又可以读取i位置的值又可以修改了 void SLModify(SL pls, int i, int x);//这个并不好用SeqList.cpp代码语言javascriptAI代码解释#define _CRT_SECURE_NO_WARNINGS 1 #includeSeqList.h void SLInit(SL pls,int n) { pls.arr (int*)malloc(n * sizeof(int)); pls.size 0; pls.capacity n; } void SLPushBack(SL pls, int x) { //……………… pls.arr[pls.size] x; } int SLFind(SL pls, int x,int i) { while (i pls.size) { //……………… } return -1; } int SLat(SL pls, int i)//这里使用引用就又可以读取i位置的值又可以修改了 { //…… return pls.arr[i];//这里可以使用引用返回是因为它是结构体中的一个在堆的数据 } void SLModify(SL pls, int i, int x)//这个并不好用 { //…… pls.arr[i] x; }test.c代码语言javascriptAI代码解释//那我们什么时候可以使用到这个传引用返回呢 //我们拿顺序表这个数据结构为例子当然栈也可以 #includeSeqList.h #includeiostream using namespace std; int main() { SL s; SLInit(s, 10); for (size_t i 0; i 10; i) { SLPushBack(s, i); } for (size_t i 0; i 10; i) { cout SLat(s, i) ; } cout endl; // 把顺序表第i个位置的值修改为x int i 0; int x 0; cin i; cin x; //SLModify(s, i, x);//不好用 SLat(s, i) x;//用了返回引用这里就可以直接修改赋值了 for (size_t i 0; i 10; i) { cout SLat(s, i) ; } cout endl; return 0; }--这里成功使用了传引用返回所以是又可以读又可以写的