wordpress地址跟站点,长沙竞价优化,龙岩网站推广软件,语言免费网站建设C第一讲#xff1a;开篇 1.C历史背景1.1C创世主--本贾尼1.2C版本更新1.3C的重要性1.4C书籍推荐 2.C的第一个程序3.命名空间3.1namespace是什么3.2namespace的使用3.3namespace使用注意事项3.4命名空间的使用 4.C输入和输出5.缺省参数6.函数重载7.引用7.1什么是引用7.2引用的定… C第一讲开篇 1.C历史背景1.1C创世主--本贾尼1.2C版本更新1.3C的重要性1.4C书籍推荐 2.C的第一个程序3.命名空间3.1namespace是什么3.2namespace的使用3.3namespace使用注意事项3.4命名空间的使用 4.C输入和输出5.缺省参数6.函数重载7.引用7.1什么是引用7.2引用的定义7.3引用的使用7.4引用注意事项7.5const引用7.5.1示例1--权力扩大问题7.5.2示例2--权力缩小7.5.3示例3 7.6指针与引用之间的关系 8.inline8.1define定义宏的劣势8.2inline函数的使用8.3inline函数使用注意事项 9.nullptr 这一讲是学习C的第一讲以后会一直学习C后序所写的算法题也将会使用C来写所以这一讲叫做开篇讲述的是C的基础知识 1.C历史背景 关于C的历史背景了解与否其实无关紧要但是这里还是稍微讲讲有意思的事情吧毕竟学习语言首先还是要先了解一下它的历史 1.1C创世主–本贾尼
C的起源可以追溯到1979年当时Bjarne Stroustrup(本贾尼·斯特劳斯特卢普这个翻译的名字不同的地⽅可能有差异)在⻉尔实验室从事计算机科学和软件⼯程的研究⼯作。他在实现项目的开发工作时感受到了C语言在一些方面的不足1983年于是他在C语言之上添加了面向对象的特性也是在这一年该语言被命名为C 仰望大佬
1.2C版本更新 1.3C的重要性
C的重要性想必也不用过多赘述了学习C的人应该都能够体会到C的重要性无论是在工作中、学习中
1.4C书籍推荐
下面的三本书是大佬推荐的书至于怎么样我相信到后边我会看的
2.C的第一个程序
//第一个C程序
#include iostream
using namespace std;int main()
{cout hello world! endl;return 0;
}这里我们是不是还看不懂这些代码没关系我们往下看 3.命名空间
3.1namespace是什么
在C语言中我们不能使用同一个名称定义多个变量
#include stdio.h
//在只引用stdio这一个头文件的情况下这串代码是正确的
int rand 10;int main()
{printf(%d , rand);return 0;
}那么下面我们改一下
#include stdio.h
#include stdlib.h
//在引用stdlib头文件之后就发生了报错rand重定义以前的定义是函数
//原因引用头文件其实就是在链接过程将头文件内容拿过来而stdlib这一头文件中
// 也包含rand而且它还是一个函数所以这里就报错了
int rand 10;int main()
{printf(%d , rand);return 0;
}这在以后多人项目中是非常拉跨的因为我们不能保证整个团队定义的变量都是不同的名称所以C中就引入了namespace来解决这个问题
3.2namespace的使用 总结 1.namespace是定义命名空间的关键字命名空间中可以包含变量、函数、结构体等 2.在C中两个冒号::被称为是作用域解析运算符它用来指定某个实体变量、函数、类属于哪个作用域或命名空间 3.namespace本质就是定义了一个命名空间这个域跟全局域相互独立不同的域可以定义同名变量所以这里的rand就不会报错了 4.C中有函数局部域、全局域、命名空间域、类域编译器在编译时会先在局部域中寻找变量然后再在全局域中寻找变量。局部域和全局域除了会影响编译查找逻辑还会影响变量的声明周期命名空间域和类域不会影响变量声明周期 1.namespace是定义命名空间的关键字命名空间中可以包含变量、函数、结构体等 #include stdio.h
#include stdlib.h
//namespace定义命名空间定义方法如下
//namespace命名空间名称--自己起--BCNR--爆炒脑仁
namespace BCNR
{//定义变量int rand 10;//定义函数int ADD(int x, int y){return x y;}//定义结构体struct Stack{int* arr;int capacity;int top;};
}int main()
{printf(%p\n, rand);//使用命名空间中对象的方法命名空间名称 :: 对象名称//提取变量printf(%d\n, BCNR::rand);//使用空间中的函数int c BCNR::ADD(10, 20);printf(%d\n, c);//使用结构体BCNR::Stack st1;return 0;
}2.在C中两个冒号::被称为是作用域解析运算符它用来指定某个实体变量、函数、类属于哪个作用域或命名空间 3.namespace本质就是定义了一个命名空间这个域跟全局域相互独立不同的域可以定义同名变量所以这里的rand就不会报错了 4.C中有函数局部域、全局域、命名空间域、类域编译器在编译时会先在局部域中寻找变量然后再在全局域中寻找变量。局部域和全局域除了会影响编译查找逻辑还会影响变量的声明周期命名空间域和类域不会影响变量声明周期 #include stdio.h
//定义在全局域
int a 10;namespace BCNR
{//定义在命名空间域int a 20;
}//函数局部域
int ADD(int x, int y)
{return x y;
}int main()
{//定义在函数局部域int a 30;printf(%d\n, a);//main函数局部域中30printf(%d\n, BCNR::a);//命名空间域中20printf(%d\n, ::a);//像这样只有两个冒号就是访问全局域中的变量10return 0;
}3.3namespace使用注意事项 总结 1.namespace可以嵌套使用 2.namespace只能定义在全局 3.项目工程中定义的多个同名namespace会认为是一个namespace不会冲突 4.C标准库都放在一个叫stdstandard的命名空间中 5.namespace创建的命名空间中的对象其实都是全局的因为这个变量能够在非命名空间域中被访问 1.namespace可以嵌套使用 #include stdio.h
//假设此时张三和李四要实现一个项目Game
namespace Game
{//张三namespace ZhangSan{int a 10;int ADD(int x, int y){return x y;}}//李四namespace LiSi{int a 20;int ADD(int x, int y){return x y * 10;}}
}int main()
{int ret;//张三printf(%d\n, Game::ZhangSan::a);//10ret Game::ZhangSan::ADD(10, 10);printf(%d\n, ret);//101020//李四printf(%d\n, Game::LiSi::a);//20ret Game::LiSi::ADD(10, 10);printf(%d\n, ret);//1010*10110return 0;
}2.namespace只能定义在全局 3.项目工程中定义的多个同名namespace会认为是一个namespace不会冲突 //同名namespace会合并在一起
//Stack.h
#includestdio.h
#includestdlib.h
#includestdbool.h
#includeassert.h
namespace bit
{typedef int STDataType;typedef struct Stack{STDataType* a;int top;int capacity;}ST;void STInit(ST* ps, int n);void STDestroy(ST* ps);void STPush(ST* ps, STDataType x);void STPop(ST* ps);STDataType STTop(ST* ps);int STSize(ST* ps);bool STEmpty(ST* ps);
}// Stack.cpp
#includeStack.h
namespace bit
{void STInit(ST* ps, int n){assert(ps);ps-a (STDataType*)malloc(n * sizeof(STDataType));ps-top 0;ps-capacity n;}// 栈顶void STPush(ST* ps, STDataType x){assert(ps);// 满了 扩容if (ps-top ps-capacity){printf(扩容\n);int newcapacity ps-capacity 0 ? 4 : ps-capacity* 2;STDataType* tmp (STDataType*)realloc(ps-a,newcapacity * sizeof(STDataType));if (tmp NULL){perror(realloc fail);return;}ps-a tmp;ps-capacity newcapacity;}ps-a[ps-top] x;ps-top;}
}4.C标准库都放在一个叫stdstandard的命名空间中 #include iostreamint main()
{//既然C标准库都放在了std这一命名空间中那么我们在引用了//头文件之后就可以对其中的对象进行使用了std::cout hello world! std::endl;return 0;
}5.namespace创建的命名空间中的对象其实都是全局的因为这个变量能够在非命名空间域中被访问 3.4命名空间的使用 上述我们已经了解到双冒号可以拿出指定命名空间中的变量来使用如果我们要一直拿出某个变量的话敲代码是一件很麻烦的事所以我们这样做 #include stdio.h
#include stdlib.h
namespace BCNR
{//定义变量int a 10;//定义函数int ADD(int x, int y){return x y;}
}//如果我们要像下面多次使用a的话是很麻烦的那么我们加上这个
//此时代表着展开BCNR这一命名空间中的a变量将a变量暴漏在全局
using BCNR::a;
//这样代表着将命名空间中所有的对象暴漏在全局中
using namespace BCNR;int main()
{printf(%d\n, a);printf(%d\n, BCNR::a);printf(%d\n, BCNR::a);printf(%d\n, BCNR::a);printf(%d\n, BCNR::a);printf(%d\n, BCNR::a);printf(%d\n, BCNR::a);printf(%d\n, BCNR::a);return 0;
}现在我们就可以理解C的第一个代码中的using namespace std是什么意思了
4.C输入和输出 1.iostream是标准输入流和标准输出流的一部分它提供了对标准输入和标准输出的访问是istream和ostream的组合istream表示输入流用于从输入流如键盘中读取数据ostream为输出流用于向输出流如屏幕中输出数据 2.std::cin是istream类的对象用于从标准输入流中读取数据 3.std::cout是ostream类的对象用于向标准输出流发送数据 4.std::enl它是一个函数而且是一个较为复杂的函数相当于插入一个换行符并刷新缓冲区 5.是流插入运算符是流提取运算符 6.与C语言不同的是C中的输入输出可以自动识别数据类型不需要使用%d、%c来格式化输入、输出 7.IO流中设计很多类和对象、重载中的知识后边会详细阐述 8.cout、cin、endl等都属于C标准库C标准库都放在一个叫std的命名空间中所以要通过命名空间的使用方法来使用它们 9.日常练习中我们可以用using namespace std在项目中万不可这样使用 10.仅仅包含iostream我们也可以使用printf、scanf可能是在iostream中间接包含了VS没有报错其他编译器可能会报错这也说明了IO流不仅仅是各司其职的在IO需求较高的情况下比如竞赛中可能会因为IO流之间的关系处理而浪费时间解决方式如下 #include iostream
int main()
{int a 0;double b 0.1;char c x;//会自动识别数据类型进行打印//如果想要保留小数的话建议使用printf来实现小数的保留std::cout a b c std::endl;//可以⾃动识别变量的类型std::cin a;std::cin b c;std::cout a std::endl;std::cout b c std::endl;return 0;
}#includeiostream
using namespace std;
int main()
{//在IO需求⽐较⾼的地⽅如部分⼤量输入的竞赛题中加上以下3⾏代码//可以提⾼CIO效率ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);return 0;
}5.缺省参数 缺省参数其实就是在函数声明时为函数参数定义一个缺省值如果传入的数据没有值的话就会使用缺省值 #include iostream
using namespace std;void Func(int a 0)
{cout a endl;
}int main()
{Func(); //没有传参时使⽤参数的默认值输出0Func(10); //传参时使⽤指定的实参输出10return 0;
}缺省分为全缺省和半缺省
#include iostream
using namespace std;// 全缺省
void Func1(int a 10, int b 20, int c 30)
{cout a a endl;cout b b endl;cout c c endl endl;
}// 半缺省
//半缺省中的参数必须从右向左进行赋值
//void Func2(int a 10, int b, int c 20)//err缺少实参2的默认实参
void Func2(int a, int b 10, int c 20)
{cout a a endl;cout b b endl;cout c c endl endl;
}int main()
{Func1();Func1(1);Func1(1, 2);//在传参时也不能跳跃着传参//Func1(, 1, );//err语法错误Func1(1, 2, 3);Func2(100);Func2(100, 200);Func2(100, 200, 300);return 0;
}需要注意的是缺省参数不能声明和函数同时给值只能够在声明中确定缺省值
//Stack.h
#include iostream
#include assert.h
using namespace std;
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;//只能在这里给缺省值
void STInit(ST* ps, int n 4);//Stack.cpp
#includeStack.h
//缺省参数不能声明和定义同时给这里就不能给缺省值了
void STInit(ST* ps, int n)
{assert(ps n 0);ps-a (STDataType*)malloc(n * sizeof(STDataType));ps-top 0;ps-capacity n;
}6.函数重载
C支持在同一作用域中出现同名函数但是这要求函数的形参不同可以是形参的个数不同也可以是形参的类型不同
#includeiostream
using namespace std;
//1、参数类型不同
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;
}
//2、参数个数不同
void f()
{cout f() endl;
}
void f(int a)
{cout f(int a) endl;
}
//3、参数类型顺序不同本质上也是形参类型不同
void f(int a, char b)
{cout f(int a,char b) endl;
}
void f(char b, int a)
{cout f(char b, int a) endl;
}int main()
{Add(10, 20);Add(10.1, 20.2);f();f(10);f(10, a);f(a, 10);return 0;
}返回值不同不能作为重载条件因为调用时也无法区分 //返回值不同不能作为重载条件因为调⽤时也⽆法区分
void fxx()
{}int fxx()
{return 0;
}注意点 //下⾯两个函数构成重载
//但是调⽤时会报错存在歧义编译器不知道调⽤谁
void f1()
{cout f() endl;
}void f1(int a 10)
{cout f(int a) endl;
}int main()
{//err对重载函数的调用不明确f1();return 0;
}7.引用
7.1什么是引用 引用其实就是给变量起别名引用并不是新定义了一个变量编译器不会给引用变量开辟空间 7.2引用的定义
//引用的定义
类型 引用别名 引用对象引用的使用如下
//类型 引用别名 引用对象
int main()
{//定义一个整形变量int a 10;//为整形变量起别名int ra a;//为别名起别名int rra ra;//为同一个整形变量起多个别名int rb a;//它们指向的地址相同说明并没有为它们单独开辟空间cout a endl;cout ra endl;cout rra endl;cout rb endl;return 0;
}而引用的原理为 对变量赋值和对变量引用的区别
7.3引用的使用 因为引用对象和原对象指向的空间相同所以说对引用对象进行修改原对象也会被修改引用的底层实现其实是指针所以我们可以这样写代码 //引用的使用
//对引用对象的修改会直接影响到原来的对象
//所以这里交换就会成功
void Swap(int x, int y)
{int tmp x;x y;y tmp;
}int main()
{int x 10;int y 20;//10 20cout x y endl;Swap(x, y);//20 10cout x y endl;return 0;
}Swap函数之前使用指针也能够完成那么有了指针引用是不是多余了呢肯定不是我们看下边的一个例子
//假设此时我们已经创建了一个栈
//void STInit(ST rs, int n 4);栈的初始化
//void STPush(ST rs, STDataType x);入栈
//int STTop(ST rs);//获取栈顶元素
int main()
{ST st1;STInit(st1);STPush(st1, 1);STPush(st1, 2);//此时我们要获取栈顶元素2cout STTop(st1) endl;return 0;
}获取栈顶元素比较简单但是这时我们想要对栈顶元素进行修改方法如下
// int STTop(ST rs)
//如果要对栈顶元素进行修改只需要将返回类型改为引用即可
//此时返回的是对原始对象的引用而不是对象的副本如果返回值的话返回时需要将值先存储到一个临时空间中所以是对象的副本
int STTop(ST rs)
{return rs.a[rs.top-1];
}//假设此时我们已经创建了一个栈
//void STInit(ST rs, int n 4);栈的初始化
//void STPush(ST rs, STDataType x);入栈
//int STTop(ST rs);//获取栈顶元素
int main()
{ST st1;STInit(st1);STPush(st1, 1);STPush(st1, 2);//此时我们要获取栈顶元素2cout STTop(st1) endl;//此时我们要读栈顶元素进行修改STTop(st1);cout STTop(st1) endl;//输出3return 0;
}这时如果要用指针来实现的话是非常麻烦的感兴趣的可以实现一下
7.4引用注意事项 1.由于引用作为返回值或传参不需要开辟临时变量所以可以提效而且还可以通过引用直接修改到原变量所以能够用引用的地方使用引用比指针更加方便 2.引用和指针在实践中是相辅相成的功能有重叠性各有特点互相不可替代但是与Java中的引用不同C中的引用不能够改变指向 3.引用可以简化程序比如使用引用代替指针传参可以避免复杂的指针更容易理解 4.引用必须初始化 5.引用一旦引用一个实体就不能引用其他实体 6.引用时如果引用临时变量就可能出现野引用的情况 int main()
{int a 10;int b 20;//5.引用一旦引用一个实体就不能引用其他实体int ra a;//ra b; err:无法从int转换成int*ra b;//这里是赋值此时a ra b 20//4.引用必须初始化//int rb; err:必须初始化引用return 0;
}//野引用
int f()
{int a 10;return a;
}7.5const引用 总的来说说的其实就是权力的问题权力可以缩小但是不能扩大 7.5.1示例1–权力扩大问题
//const引用示例1
int main()
{const int a 10;//此时a为const类型//int ra a;//err权限不能扩大不能从const int转换为intconst int ra a;//√const int和const int权限相同return 0;
}7.5.2示例2–权力缩小
//const引用示例2
int main()
{int a 10;const int ra a;//权力可以缩小int rb a;//正确int rra ra;//err无法从const转换成intreturn 0;
}7.5.3示例3
int main()
{int a 10;int rb 30;//err因为30是常数const int rrb 30;//这个才是正确的写法int ra a*3;//err因为a*3是常数const int rra a*3;//这样也是正确的表示a*3被rra引用可以通过rra找到a*3但是不能改变a*3的值rra 20;//err不能给常量赋值double c 1.2;const int rc c;//虽然是int类型但是这样也是对的涉及到整形提升//这里就是先将1.2传到一个int类型的临时变量中然后再让rc进行指向return 0;
}7.6指针与引用之间的关系 C中的指针和引用就像两个性格不同的亲兄弟指针是哥哥引用为弟弟它们在实践中相辅相成但各自有各自的特点互相不可替代 1.语法概念上引用是为变量起别名不需要开辟空间指针存储的是一个变量的地址需要开辟空间 2.sizeof引用的大小是引用对象的类型发大小而sizeof指针始终是4/8个字节 3.引用在定义时必须初始化指针只是建议初始化 4.引用在引用一个对象后就不能引用其它对象而指针可以不停地改变指向 5.引用可以直接访问引用对象而指针需要解引用 6.指针很容易出现野指针情况而引用比较难以出现野引用的情乱 8.inline
8.1define定义宏的劣势 之前我们就已经讲过define定义的宏具有很大的劣势比如我们要定义一个宏函数 //错误写法1
#define ADD(int x,, int y) return xy
//错误写法2
#define ADD(int x, int y) (xy)
//错误写法3
#define ADD(x, y) (xy)
//正确写法
#define ADD(x, y) ((x) (y))
//1.形参中不用追加类型
//2.必须加上合适的小括号
//3.最后边不能加因为是宏替换
//4.外面不加括号x12, y23形成错误
//5.里边不加括号x1|2y2^3形成错误位移运算符优先级低于为了解决这一个问题C中设计了一个inline函数
8.2inline函数的使用
//inline函数的使用
//直接在函数前加上关键词inline即可
inline int ADD(int x, int y)
{return x y;
}int main()
{cout ADD(10, 20) endl;//输出30return 0;
}8.3inline函数使用注意事项 总结 1.inline修饰的函数被称为内联函数在使用内联函数时不需要为改函数创建栈帧可以提高效率 2.inline对于编译器来说只是建议也就是说内敛函数的展开与否取决于编译器一般内联函数短小时就会展开内联函数较大时就不会展开 3.inline不建议声明和定义分离到两个文件分离会导致链接错误因为inline被展开是没有函数的地址的链接时就会报错 4.VS中debug版本下是默认不展开的想要展开可以搜索着改动一下编译器 9.nullptr
NULL其实是一个宏在传统的C头文件中可以看到
#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif也就是说C中NULL可能被定义为字面量0或者在C中被定义为(void*)的常量这样在使用重载的函数时就会遇到问题
//两个重载函数
void f(int x)
{cout f(int x) endl;
}void f(int* ptr)
{cout f(int* ptr) endl;
}int main()
{f(0);f(NULL); //这里调用的也是f(int x)函数因为NULL被解析成了0f((int*)NULL); //编译报错error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型f((void*)NULL);return 0;
}既然有着这样的问题那么就设计出一个新的东西nullptr nullptr是一个特殊的关键字是一种特殊类型的字面量它可以转换成其它任意类型的指针类型nullptr只能被转换成指针类型而不能被转换成整数类型所以以后用nullptr千万不要陌生