为什么博客需要wordpress,湖南seo技术培训,长沙教育网站建设,网站背景全屏目录
一、第一个C程序
二、C命名空间 1#xff09;什么是命名空间#xff1f; 2#xff09;命名空间的使用 3#xff09; std库与namespace展开 4#xff09;命名空间的嵌套使用
三、输入输出方式
四、缺省参数 1#xff09;什么是缺省参数#xff1f; 2#xff0…
目录
一、第一个C程序
二、C命名空间 1什么是命名空间 2命名空间的使用 3 std库与namespace展开 4命名空间的嵌套使用
三、输入输出方式
四、缺省参数 1什么是缺省参数 2多个缺省值的缺省参数
五、函数重载 1什么是函数重载 2为什么C可以用函数重载
六、C引用与指针 1什么是引用 2引用的规则特点 1、引用必须初始化才能用 2、引用不能传空指针
编辑 3、一个变量或对象可以有多个别名
编辑 4、没有所谓的二级引用
编辑 5、引用不能改变指向 3引用与指针的区别 4引用的使用
七、内联函数 1什么是内联函数 2内联函数的特性 前言 说到C大家总会想到C语言毕竟C也就是C plus plus 么没有错C在语法上是兼容C语言的。我们C的祖师爷本贾尼·斯特劳斯特卢普在写C程序的时候对于C的一些语法规则感到不合适于是祖师爷在C的基础上开发了这样一门语言。今天我们就要开启C世界的大门了。
一、第一个C程序
说到学习新的语言那就不得不写下人生中第一个C程序了————你好世界。
#includeiostreamusing namespace std;int main()
{cout Hello World!\n;return 0;
} 是不是有当年学C语言那味了首先我们来分析一下是如何打印出来Hello World!。 我们在C语言中是用printf函数打印字符串的在C中是用cout(console out控制台输出)来向控制台输出内容的在C语言中printf函数对不同类型的数据有对应的输出格式访问控制符像%d,%s...才会打印出对应类型的数据而在C中的cout会自动识别变量类型相比之下写起来更加方便。 而在cout 后面跟着 叫做流插入限定符表示在‘’右边的数据流向左边cout hello world\n; 就是将字符串信息流 流入到控制台当中打印。 那么知道了cout和流插入限定符就可以打印了吗很遗憾告诉你还是不行这就要涉及到using namespace std;这条语句了那这是什么意思呢那个头文件也和C语言也不一样啊又是什么意思呢让我来一一为你介绍。 二、C命名空间 1什么是命名空间 话说在祖师爷那个年代C语言是主流的高级语言当然祖师爷也不例外写项目也是用的C语言其中祖师也在写大型项目的时候总会遇到这样一个问题不同的程序员负责实现不同的模块但是在最后整合的时候总是会有两个程序员用的变量或函数的名字相同。例如
#includeiostream
#includestdio.h//C语言中printf函数所需要的头文件int printf 1;int main()
{printf(%d, printf);return 0;
} 这种命名冲突要改是很麻烦的祖师爷经常被这个东西搞得头疼所以祖师爷在开发C的时候直接规定了一种关键字来避免这种情况————namespace(命名空间) 命名空间就是使用 namespace 空间名 在namespace内部会自动生成“一堵墙” 这堵墙将namespace内部内容的命名与整个程序以及库里的程序隔开互不影响就像这个世界上不止一个人叫张三但是他们并不是同一个人。 2命名空间的使用 那么我们知道了命名空间但是该如何使用呢我们把可能会冲突的变量或者函数放进命名空间内在外部想要调用命名空间内的内容就需要 ‘::’ 叫做域作用限定符是访问namespace的专用符号使用方法是空间名::内部变量/函数等例如下面代码
#includeiostreamnamespace byte{int printf 1;}int main()
{std::cout byte::printf;return 0; } 这样就可以输出printf这个变量了就不会造成命名冲突的问题。 3 std库与namespace展开 有些时候我们在命名空间内的函数或者变量在外部要多次调用的情况每次调用之前都要加上空间名和域作用限定符也是一件挺麻烦的事情例如
#includeiostream
#includestdlib.h
#includestring.h
#includeassert.hnamespace byte{typedef int STDataType;typedef struct Stack{STDataType* _a;int _top; // 栈顶int _capacity; // 容量 }Stack;// 初始化栈 Stack* StackInit(STDataType n){Stack *p (Stack *)malloc(sizeof(Stack));//...p - _a (STDataType *)malloc(sizeof(STDataType) * n);//...p - _top -1;p - _capacity n;return p;}// 入栈 void StackPush(Stack* ps, STDataType data){assert(ps);//...ps - _a[ps - _top] data;return;}// 出栈 void StackPop(Stack* ps){assert(ps);if(ps - _top -1){ps - _top - 1;}return;}}void Test()
{byte::Stack *ps byte::StackInit(5);byte::StackPush(ps, 1);byte::StackPush(ps, 1);byte::StackPush(ps, 1);byte::StackPush(ps, 1);byte::StackPop(ps);//...
}int main()
{Test();return 0; } 我们来模拟栈的实现没有全部写出来把栈的操作放在namespace里面在Test()中想要访问栈每次都需要在造作前面加上这么一些东西写起来也很麻烦所以祖师爷就规定了一种配套的关键字——using使用方法是using namespace 空间名这样就可以展开命名空间也就是打开那堵墙在使用时就不需要加上前面那一大坨了但这个时候就不能保证命名冲突了。 我们只需在命名空间下面加上这样一条语句using namespace byte; 这样编译的效果和上面代码效果就是相同的了。 namespace还有一种局部展开的方式将命名空间内的常用的变量或函数名局部展开防止命名空间内的其他变量会与程序发生冲突使用方法是using 空间名::变量/函数名等 这样也是比较常用的展开方式。例如
#includeiostream
#includestdlib.h
#includestring.h
#includeassert.hnamespace byte{typedef int STDataType;typedef struct Stack{STDataType* _a;int _top; // 栈顶int _capacity; // 容量 }Stack;// 初始化栈 Stack* StackInit(STDataType n){Stack *p (Stack *)malloc(sizeof(Stack));//...p - _a (STDataType *)malloc(sizeof(STDataType) * n);//...p - _top -1;p - _capacity n;return p;}// 入栈 void StackPush(Stack* ps, STDataType data){assert(ps);//...ps - _a[ps - _top] data;return;}// 出栈 void StackPop(Stack* ps){assert(ps);if(ps - _top -1){ps - _top - 1;}return;}}using byte::StackPush;
using byte::StackPop;void Test()
{byte::Stack *ps byte::StackInit(5);StackPush(ps, 1);StackPush(ps, 1);StackPush(ps, 1);StackPush(ps, 1);StackPop(ps);//...
}int main()
{Test();return 0; } 这样不经常使用的和命名冲突的就可以不展开使用了常用的变量或函数就可以来展开使用避免了不必要的麻烦。 想必你也发现了我们在最开始打印hello world的时候发现有这样一条语句using namespace std;实际上std也是一种命名空间只不过std是C库的命名空间里面有很多用得到的函数模版等等东西非常多其中cout等也在std库内所以使用的时候要展开命名空间。 值得注意的是我们前面也说了如果展开命名空间就不能保证命名冲突的问题了而且std库内的的内容很多保不准就会发生命名冲突所以在写大型项目时最好不要展开std,但是在日常的练习中还是展开的。 4命名空间的嵌套使用 我们在使用命名空间内容比较多的时候也保不准命名空间内会出现命名冲突所以C就规定了可以允许命名空间嵌套命名空间例如
#includeiostreamnamespace ptr{namespace spa1{int ptr 1;}namespace spa2{int ptr 2; }namespace spa3{int ptr 3;}
}using std::cout;int main()
{cout spa1: ptr::spa1::ptr std::endl;cout spa2: ptr::spa2::ptr std::endl;cout spa3: ptr::spa3::ptr std::endl;return 0;
} 在C的一些库里这种方式也很常见但是嵌套太多层也是坑的很还是谨慎使用嵌套功能为好。 三、输入输出方式 C的输出方式在最开始也已经提到了cout:控制台输出:流插入运算符。那么我们输出有了我们输入呢 在C语言中我们输入的方式是调用stdio.h中的scanf函数来进行输入与printf一样也需要输入格式访问控制符才能对输入数据类型进行判断。 在C中我们的输入为cin(console in控制台输入)搭配(流提取运算符)使用使用方式为 std::cin 变量;//其中cin与cout一样会自动识别变量的类型 其中我们在使用输入输出的时候就需要包含头文件iostream也就是输入输出流文件保证cout和cin的正常输入输出的使用。 其中cin和cout都是在std库内的所以之前在使用的时候要展开std命名空间这里在介绍一种C中常用的换行符不是\n而是叫做endl(end line 结束行) 通常在cout结尾处使用例如
#includeiostreamint main()
{std::cout Hello World std::endl;return 0;
} 其中endl也是内置在std库里的这样写也是会有换行效果的。 到这里你可能还有一些疑问我们如果想要对浮点数进行精度控制C是不是也有新的语法规则来写呢很遗憾并没有但是C语法是兼容C的所以如果想要对浮点数进行精度控制的时候我们直接用printf函数进行精度控制就行。 四、缺省参数 1什么是缺省参数 祖师爷对C语言的函数部分也不是很满意例如在栈的数据结构中在栈的初始化期间需要传参capacity容量来给栈开辟空间大小在C语言中我们每次初始化时都需要给个值祖师爷觉得有些麻烦所以在C里面出现了一个叫做缺省参数的语法规则在函数传参时直接对参数进行赋值。
Stack *InitNewStack(int capacity 3)
{//...
} 这样在调用这个函数时不传参数就默认capacity初始化为3, 传参就以传的参数为准。 那么什么是缺省参数呢实际上缺省参数是声明和定义函数时为函数的参数指定一个缺省值在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的实参。
例如
#includeiostreamusing std::cout;
using std::cin;
using std::endl;void Func(int a 0)
{cout a endl;
}int main()
{Func();//没有参数时使用形参默认值Func(1);//有参数时使用指定实参return 0;
}这就是缺省参数的具体用法。 2多个缺省值的缺省参数 实际上缺省参数可以有多个缺省值而且在多个缺省值当中有着全缺省与半缺省之分。全缺省参数的函数在调用时没有传实参就会进行自动采用该参数的缺省值为参数如果传参就以实参为参数这点与上面的相同。 如果我们在全缺省函数传参传不完整参数会发生什么我们不妨看以下代码
#includeiostreamusing std::cout;
using std::cin;
using std::endl;void Func(int a 0, int b 1, int c 2)
{cout a a endl;cout b b endl;cout c c endl;
}int main()
{Func();//不传参数cout endl;Func(5);//传一个参数cout endl;Func(5, 6);//传两个参数cout endl;Func(5, 6, 7);//传三个参数cout endl;return 0;
}我们可以看到全缺省参数的函数是可以传空参的也可以传不完整的参数同时不知道聪明的你有没有发现这里传参有个规律当Func(5),Func(5, 6)的时候Func(5)是将第一个参数初始化后面两个参数为缺省值Func(5, 6)是将前两个参数初始化最后一个参数为缺省值。这其实就是祖师爷规定的默认传参顺序对于全缺省函数传不完整参数是从左到右进行传参的。 或许你又会有疑问能不能前面不传参后面传参或者只有中间值传参例如 Func( , 6, 7); 或 Func( , 6 , ); 实际上并不存在这种传参的方式C中这样是会报错的所以缺省参数只能顺序传参。
还有一种叫做半缺省参数的函数所谓半缺省就是函数参数一部分是缺省参数一部分是普通参数这类函数被称为半缺省参数函数而且半缺省参数函数的缺省值只能从右往左给例如
#includeiostreamusing std::cout;
using std::cin;
using std::endl;void Func(int a, int b 1, int c 2)
{cout a a endl;cout b b endl;cout c c endl;
}int main()
{Func(0);cout endl;Func(5);cout endl;Func(5, 6);cout endl;Func(5, 6, 7);cout endl;return 0;
}这个函数就是一个半缺省函数 a没有缺省值只有b,c有缺省值那么运行起来会发生什么事呢 我们发现在Func(5, 6)中 传的参数是依旧是从左往右进行传参的既然如此我们半缺省函数能不能缺省后边的值或者中间的值呢
#includeiostreamusing std::cout;
using std::cin;
using std::endl;void Func(int a 0, int b, int c 2)
{cout a a endl;cout b b endl;cout c c endl;
}void Func2(int a 0, int b 1, int c)
{cout a a endl;cout b b endl;cout c c endl;
}int main()
{return 0;
}这种会发生什么情况 我们发现这两种情况会报错但是为什么会报错报错信息显示形参缺少默认实参也就是说我们在传不完整参数的时候其实是不能确定你要传的是缺省参数还是普通形参所以干脆C把这种半缺省方式定义为错误的语法方式最终半缺省函数传参只能从右往左进行缺省。
注意缺省参数函数的生命和定义不能同时出现缺省值通常的做法是在声明时写缺省值定义时默认不写。 五、函数重载 1什么是函数重载 祖师爷不仅对函数的参数有意见对函数的命名也很有意见有些功能相似的函数或许只是参数不同但是却要好几个不同的命名像在函数名后面加1,2,3...用来区分不同的函数其实也就是为了解决一词多义的问题就例如网络段子“中国足球谁也赢不了中国乒乓球谁也赢不了”虽然都是赢不了但是意义却不一样祖师爷觉得这样很麻烦不如干脆用同一个函数名得了于是C中出现了函数重载这一语法规则。 实际上函数重载是函数的一种特殊情况C允许在同一个作用域中声明几个功能类似的同名函数这些同名函数的形参列表参数个数 或 类型顺序不同常用来处理实现功能类似数据类型不同的问题。 函数重载又分为1、参数类型不同。2、参数个数不同。3、参数顺序不同。代码如下
#includeiostreamusing namespace std;//1、参数类型不同
int f(int a, int b)
{cout f(int): a b endl; return a b;
}double f(int a, double b)
{cout f(double): a b endl;return a b;
}//2、个数不同
void f2(int index)
{cout f2(int index) endl;
}void f2()
{cout f2(NULL) endl;
}//3、顺序不同
void f3(int a, double b)
{cout f3(int, double) endl;
}void f3(double a, int b)
{cout f3(double, int) endl;
}int main()
{f(5, 5);f(3, 3.5);f2(0);f2();f3(4, 4.4);f3(4.4, 4);return 0;
} 以上都是函数重载的方式的具体用法。 值得注意的是这里并没有说返回值不同而造成函数的重载实际上仔细想想如果两个函数都是相同的只有返回值不一样那么究竟是调用哪个函数呢这就是造成重载的二义性的原因。 2为什么C可以用函数重载 在学习完函数重载的过程中有没有思考这样一个问题为什么这么好用的东西C语言不支持呢其实这里涉及到程序的编译与链接实际上程序在从写下来到打印到控制台上需要经历预处理、编译、汇编、链接的几个过程如果对于这几个过程没有一点概念的同学可以看看我的这篇文章C语言预编译详解可以稍微了解一些。 实际上重载函数在编译生成汇编的过程中C语言对于函数名并没有什么特殊的变化但是C在编译生成汇编的过程中函数名会生成某种符号规则来确定这个函数是否为重载。 六、C引用与指针 我们在日常生活中身边的朋友不免有些外号比如我的好朋友玩的好的都叫他‘小李子’只听过小李在校园传奇故事的同学都叫他‘李哥’那么这个李哥和前面的小李指的就是同一个人而引用在语法层面上的理解就是引用就是取别名。 1什么是引用 其实上面那个例子就已经能够说明什么是引用了实际上引用就是给变量取一个别名编译器不会给它专门开一个空间它与引用的变量共用同一块内存空间。 引用的格式如下 类型 引用变量名(对象名) 引用实体//左值引用 这里符号左右可以带空格可以不带空格没什么实际影响 我们来看一下如何给一个引用:
#includeiostream
#includestdio.husing namespace std;int main()
{int a 0;int b a;//b是a的别名cout a: a endl b: b endl;printf(%p\n,a);printf(%p\n,b);return 0;
} 我们可以看到对于变量a引用b是变量a的引用变量b输出的内容是变量a的值变量a与引用b都是指向同一片地址的。 2引用的规则特点 1、引用必须初始化才能用
#includeiostream
#includestdio.husing namespace std;int main()
{int a 1;int b;return 0;
}
C规定引用使用必须初始化。 2、引用不能传空指针
#includeiostream
#includestdio.husing namespace std;int main()
{int a 1;int b nullptr;return 0;
} 3、一个变量或对象可以有多个别名
#includeiostream
#includestdio.husing namespace std;int main()
{int a 1;int b a;int c a;int d a;cout a endl;cout b endl;cout c endl;cout d endl;return 0;
} 4、没有所谓的二级引用
#includeiostream
#includestdio.husing namespace std;int main()
{int a 1;int b a;int pb b;return 0;
} 5、引用不能改变指向
#includeiostream
#includestdio.husing namespace std;int main()
{int a 1;int c 2;int b a;int b c;return 0;
} 以上便是引用的一些规则的特点。 3引用与指针的区别 在汇编层面上来说引用就是一个指针但是不同的是引用相当于常量指针改变不了它所引用对象的地址。 在语法层面上来说
1、引用是别名指针存的是地址。
2、指针解引用要加上*而引用是自动解引用的
3、引用不会分配空间但是指针会分配空间
4、指针有空指针但是引用没有空引用
5、指针运用自增运算符是指向下一位而引用使用自增运算符是对内容1
6、指针有多级指针但是引用只有一级引用
7、对指针用sizeof是指针变量的大小对引用sizeof是引用变量的大小
8、引用比指针更加安全 4引用的使用 既然说到引用对象的地址不可改变值可以变对于指针来说值和地都可以随意改变他俩相比较下引用对象的地址不可变值可变指针指向对象的地址可变值也可变。当值不可变时又是什么情况呢
#includeiostream
#includestdio.husing namespace std;int main()
{const int a 1;int b a;return 0;
} 其实这个时候虽然a被加上了const变成了常量 但是依然可以通过b对a的值进行修改这种我们可以称之为权限放大就是b的权限要高于a了那么有权限放大就有权限缩小与平等权限没错实际上权限缩小就是对a不加任何修饰对引用b加上const修饰这样b不可修改a却可以平等权限就是两个变量前都加上const,这样就都不能改变了。
//权限缩小
int a 0;
const int b a;
//权限平等
const int c 0;
const int d c; 引用还有什么作用我们不妨以指针的视角看一下指针除了可以作为指针变量指针还可以传参还可以作为返回值。那么我们引用是否也可以传参作为返回值呢 答案是可以的而且引用做返回值在一定程度上会提高程序的运行效率。这是因为我们在传参数时实际上是拷贝一份实参传给形参的而加上了引用就不需要在进行拷贝了便可以直接访问内容。 而引用作为返回值也是比较奇怪的
#includeiostream
#includestdio.husing namespace std;int Add(int a, int b)
{int c a b;return c;
}int main()
{int a 1, b 2;int ind Add(a, b);cout ind endl;return 0;
} 首先以引用作为返回值表示返回的ind是c的别名这个就相当对在函数里返回一个局部指针出了作用域就会销毁但是现在的编译器都比较高级可能会保留下来这个值所以就能看到这个值是3我们再看下面这段代码
#includeiostream
#includestdio.husing namespace std;int Add(int a, int b)
{int c a b;return c;
}int main()
{int ind Add(5,5);cout ind endl;Add(3,4);cout ind endl;return 0;
} 这里我们两次调用了Add这个函数可是两次的值都是第一次调用的结果语法规定除了作用域为随机值这里是进行了优化第一次调用后将调用的c拷贝了一份给了ind这时他俩并不指向同一块空间所以第二次调用时并不会影响ind的值。 但是我们想要正常运行且能多次调用呢看看下面这段代码
#includeiostream
#includestdio.husing namespace std;int Add(int a, int b)
{int c a b;return c;
}int main()
{int ind Add(5,5);cout ind endl;Add(3,4);cout ind endl;return 0;
} 这里我们将ind为引用来接收引用的返回值他们指向了同一块空间多次调用时就不会是同一个结果了。 七、内联函数 1什么是内联函数 我们在C语言中其实有个很好用的东西————宏但是学过C的都知道宏很难用虽然它写的程序运行很快但是不能进行调试特别容易出错我们的祖师爷还是比较喜欢宏的于是祖师爷去劣留优也增加了一些新的规则创建了一种新的关键字——inline关键字。 内联函数也就是普通函数前面加上inline关键字就变成了内联函数内联函数本质上和宏一样都是对文本进行替换而且可以对函数进行调试这样可以节省很多函数调用销毁的开销但同时会让目标文件变大。 2内联函数的特性 这个时候你可能就会说了那以后每个函数都用inline关键字不香吗实际上内联函数的适用场景是短小、多次重复调用的函数因为内联本质上还是文本替换全都进行展开的话编译器会吃不消所以编译器默认最多你的函数在10行左右及以下inline才会有效否则就是个普通的函数。 内联函数的声明和定义不能分离在预处理过程会进行文本替换替换后函数的地址就找不到了那么就会在运行时报错。 创作不易还望各位佬能多多三连【可怜】【可怜】~~