ps课程教学零基础网课,seo网站推广优化,福州网站建设招商,网站做哪些比较赚钱方法C基础入门统一初始化输入输出输入输出符输入字符串const与指针c和c中const的区别const与指针的关系常变量与指针同类型指针赋值的兼容规则引用引用的特点const引用作为形参替换指针其他引用形式引用和指针的区别inline函数缺省参数函数重载判断函数重载的规则名字粉碎C编译时函…
C基础入门统一初始化输入输出输入输出符输入字符串const与指针c和c中const的区别const与指针的关系常变量与指针同类型指针赋值的兼容规则引用引用的特点const引用作为形参替换指针其他引用形式引用和指针的区别inline函数缺省参数函数重载判断函数重载的规则名字粉碎C编译时函数名修饰约定规则函数模板名字空间nameplace命名空间的使用new/deleteC的动态内存管理C11的新特性类型推导auto的限制可以推导函数返回值decltype关键字基于范围的for循环遍历数组容器基于范围的for循环指针空值--nullptrtypedef与using的使用string的使用统一初始化
初始化列表的方法
int main() {int a 10;int b{ 10 };int c(10);int ar[] { 1,2,3,4,5,6,7,8 };int arr[]{ 1,2,3,4,6,5,7,8,9 };return 0;
}输入输出
输入输出符
输入流cin 键盘 输出流cout 控制台屏幕 使用cin和cout标准输入输出时必须包含 (iostream)头文件以及std标准命名空间。
int main() {int a;int b;cinab;//提取符coutaabbendl;//插入符 endl\n《换行return 0;
}输入字符串
int main() {const int n 128;char str[n];cin str ;cout strendl;//输入abc cde输出abccin.getline(str,n);cout str endl;//输入abc def输出abc defcin.getline(str,n,#);cout str endl;//输入def vyw# ugfua输出def vywreturn 0;
}getline函数 1getline(istream is,string str,char delim) 2getline(istream is,string str); delim终结符 遇到该字符停止读取操作不写默认回车
const与指针
c和c中const的区别
c中const修饰一个变量叫做常变量其本质上还是一个变量可以修改。而c中const是一个常量本质上不能修改。
int main() {const int a{ 10 };int ar[a]{ 1,2,3,4 };int* p (int *) a;*p 20;printf(a%d *p%d \n, a, *p);
}
int main() {const int n{ 10 };int ar[n]{ 1,2,3,4 };int* p (int*)n;*p 100;cout n n p *pendl;return 0;
}const与指针的关系
此处存在三种情况如下
int main() {int a10,b20;int* pa;//普通指针const int* p1a;//控制了解引用不能改变解引用为常性int const*p2a;//控制了解引用不能改变解引用为常性同上相同int * const p3a;//控制了指针本身指向指针变量自身为常性const int* const p4a;//解引用和指针本身指向都进行控制
}以上几种写法均正确只是编译方式不同
常变量与指针
int main() {const int a10;int* p1a;//error a本身为常量此处错误在于可以用*p解引用进行修改相矛盾const int* p2a;//OK const限制了解引用int * const p3a;//error仅控制了指向没有控制解引用const int* const p4a;//OKint *p5(int*)a;//OK 不安全return 0
}同类型指针赋值的兼容规则
能力强的指针赋值给能力收缩的指针
int main() {int a 10, b 20;int* p a;int* s1 p;const int* s2 p;int* const s3 p;const int* const s4 p;cout *p *p *s1 *s1 *s2 *s2 *s3 *s3 *s4 *s4 endl;return 0;
}引用
引用的定义 类型 引用变量名称变量名称 这就是引用变量的定义。和类型结合称之为引用符号不是取地址代表别名的意思。
int main() {int a10;int ba;int ca;//引用定义时必须进行初始化return 0
}引用的特点
- [ ] List itemint main() {int a10;int b;//error 引用定义时必须进行初始化int cnullptr;//error 不存在空引用int da;//OKinrea;//error 不存在双重引用
}const引用
int main() {int a10;const int b20;int xb;//errorconst int xb;//OKconst int ya;//OKconst int z10;//OK
}作为形参替换指针
使用指针替换两个变量值
void swap(int *x,int *y){int temp;temp*x;*x*y;*ytemp;
}
int main() {int a10,b20;swap(a,b);return 0;
}使用引用替换两个变量的值
void swap(int x,int y){int tempx;xy;ytemp;
}
int main () {int a10;int b20;swap(a,b);return 0;
}其他引用形式
int main() {int a10,b20;int ar[5]{1,2,3,4,5};int *pa;int *rpp;//引用指针int xar[0];int (br)[5]ar;//引用数组return 0;
}引用和指针的区别
从语法规则上讲指针变量存储某个实例变量或对象的地址引用是某个实例的别名。 程序为指针变量分配内存区域不为引用分配内存区域。 解引用是指针使用前要加上“ * ”引用可以直接使用不需要解引用 指针变量的值可以发生改变存储不同实例的地址引用在定义时就要被初始化之后无法改变不能是其他实例的引用。 指针变量的值可以为NULL引用不存在空引用。 指针变量作为形参时需要测试他的合法性判空引用不需要。 对指针变量使用sizeof得到的是指针变量的大小4字节对引用变量使用sizeof的到的是变量的大小。 理论上指针没有级数的限制二级指针等但是引用只有一级 引用与指针的效果不同对指针变量操作会使指针变量指向下一个实体的地址不改变实体本身的内容。但是对引用进行操作就会直接影响到变量的本身。 不可以对函数中的局部变量或对象以引用或者指针的方式返回。
int * fun() {int a10;return a;
}
int func_2() {int a10;return a;
}
//以上两种方法均错误此处我们在执行的过程中可以运行但是为什么不能作为返回值呢 当函数被调用时函数的形参都在栈桢中当使用指针或者引用作为返回值时我们返回的都是这个局部变量的地址但是函数返回时函数的栈桢会被回收我们返回的这个地址也就会失效。但是我们为什么可以运行呢因为这个地址还存在上一个栈桢的存留因此可以进行使用该地址找到我们局部变量的值如果这个栈桢被骚扰或者说这个栈桢被一个新的函数调用时作为了另一个函数的栈桢返回的地址上的值就会被改变就不是我们所期望的值。因为我们不能确定该栈桢有没有被骚扰所以不能用指针或者引用作为返回值。
inline函数
当程序执行函数调用时系统要进行建立栈空间进行保护现场传递参数控制程序执行和转移等当一个函数功能简单且执行多次为了提高效率直接将函数的代码嵌入到程序中这个办法会存在两个缺点一是书写相同代码二是程序可读性往往没使用函数的好。为了协调好效率和可读性之间的矛盾cr提供了另一种方法–内联函数方法是在函数定义时用修饰词inline。
inline bool IsNumber(char ch) {return ch 0 ch 9 ? 1 : 0;
}例如该函数在其面前加上inline关键字该函数就变成了内联函数编译期间编译器能够在调用点内联展开该函数。 inline是一种以空间换时间的做法省去调用函数的开销。但是函数体的代码过长或是递归函数即便加上inline关键字也不会在调用点内以内联展开该函数。 inline对编译器而言只是一个建议编译器会自动优化。 inline不建议声明和定义分离分离会导致链接错误。因为inline被展开就没有函数地址了链接就会找不到。
//A.h
#pragma once
inline bool IsNumber(char ch);
//A.cpp
#includeA.h
inline bool IsNumber(char ch) {return ch 0 ch 9 ? 1 : 0;
}
//main.cpp
#includeA.h
#includeiostream
using namespace std;int main() {char c 4;bool iIsNumber(c);cout i endl;
}
//无法解析的外部符号 bool __cdecl IsNumber(char) (?IsNumberYA_NDZ)函数 _main 中引用了该符号 测试 D:\tulun\测试\main.obj 1 那在什么情况下使用内联函数什么情况下使用普通函数呢 如果函数的执行开销小于开栈清栈开销函数体较小使用inline处理效率高如果函数执行开销大于开栈清栈开销使用普通函数方式处理。 内联函数和宏定义的区别
内联函数在编译时展开代餐的宏在预编译时展开内联函数直接嵌入到目标代码中带参的宏时简单的做文本替换。内联函数有类型检测语法判断功能宏只是替换
缺省参数
一般情况下函数调用时的参数个数和形参相同但是为了更方便的使用函数c也允许定义具有缺省参数的函数这种函数调用时参数个数可以与形参不相同。 缺省参数指在定义时函数时为形参指定缺省值。 这样的函数在调用时对于缺省函数可以给出实参也可以不给出参数值。如果给出实参将实参传递给形参进行调用如果不给出实参则按缺省值进行调用。 缺省参数的函数调用缺省参数并不一定时常量表达式可以是任意表达式甚至可以通过函数调用给出。如果缺省实参时任意表达式则函数每次被调用时则表达式被要求重新求值。但是表达式必须有意义。
#includeiostream
int add1(int a3,int b6){return ab;
}
int main() {coutadd1()endl;//9coutadd1(4)endl;//10coutadd1(1,2)endl;//3return 0;
}缺省参数可以有多个但所有缺省参数必须放在参数但是所有缺省参数必须放在参数表的右侧即先定义所有的非缺省参数在定义缺省参数。这是在函数调用时参数自左向有逐个匹配当实参个形参个数不一致时只有这样才不会产生二义性。 多文件结构
//A.h
void fun(int ,int b23,int c8000);
//void fun(int ,int 23.int 8000);
//A.cpp
void fun(int a,int b , int c) {cout a a b b c c endl;
}
//main.cpp
int main() {using namespace std;fun(12);fun(10, 20);fun(10, 20, 30);return 0;
}
运行结果 习惯上缺省参数在公共头文件包含的函数声明中指定不要再函数定义中指定。如果再函数的定义中指定缺省参数值再公共头文件包含的函数声明中不能再次指定缺省参数值。缺省实参不一定必须是常量表达式可以是任意表达式。
int my_rand() {srand(time(NULL));int ra rand() % 100;return ra;
}
void fun(int a, int b my_rand()) {cout a a b b endl;}
int main() {fun(12);return 0;
}当缺省实参是一个表达式时 在函数被调用时该表达式被求值。c语言不支持
函数重载
C语言实现int,double,char类型的比较大小函数。
int my_max_i(int a,int b) {return ab?a:b;}
double my_max_d(double a,double b) {return ab?a:b;}
char my_max_c(char a,char b) {return ab?a:b;}这些函数都执行了相同的一般性动作都返回两个形参中的最大值从用户角度来看只有一种操作就是判断最大值。这种词汇上的复杂性不是“判断参数中的最大值”问题本身固有的二十反应了程序设计环境的一种局限性在同一个域中出现的名字必须指向一个唯实体函数体。 这就要程序员记住每一个函数的名字函数重载解决了这种问题。 函数重载在C中可以为两个或两个以上的函数提供相同的函数名称只要参数类型不同或者参数类型相同而参数个数不同称为函数重载
int my_max(int a, int b) {return a b ? a : b;
}
double my_max(double a, double b) {return a b ? a : b;
}
char my_max(char a, char b) { return a b ? a : b;
}int main() {int ix my_max(1, 2);double dx my_max (12.23, 23.45);char cx my_max(a, b);return 0;}如上如果两个参数表中的参数个数或者顺序不同则认为两个函数重载。
判断函数重载的规则
1.如果两个参数的参数相同但是返回类型不同会被标记为编译错误函数的重复声明。
int my_max(int a,int b){return ab?a:b;}
unsigned int my_max(int a,int b){return ab?a:b;}
int main() {int ixmy_max(12,23);unsigned intmy_max(12.23);//error 返回值为intreturn 0;
}2.参数表的比较过程与形参名无关
int my_add(int a,int b);
int my_add(int x,int y);3.如果两个函数的参数表中只缺省参数不同则第二个声明被歧视为第一个的重复声明。
void Print(int*br,int n);
void Print(int* br,int len10);4.typedef名为先右数据提供了一个替换名他并没有创建一个新类型因此如果两个参数表的区别只在于一个使用了typedef而另一个使用了与typedef而另一个使用了与typedef相应的类型。则该参数表被视为相同的参数列表。
typedef unsigned int u_int;
int Print(u_int a);
int Print(unsigned int b);5.当一个参数类型又const或colatile修饰时如果形参是按值传递方式定义再识别函数声明是否相同时并不考虑修饰符。
void fun(int a) {}
void fun(const int a){}6.当一个形参类型又const或者volatile修饰时如果形参定义指针或引用时在识别函数声明是否是相同时就要考虑const修饰词。
void fun(int* p) {}
void fun(const int*p) {}
void fun(int a) {}
void fun(const int a) {}7.注意函数调用的二义性如果在两个函数的参数表中形参类型相同而形参个数不同形参默认值将会影响函数的重载。
void fun(int a) {}
void fun(int a,int b) {}
void fun(int a,int b10);8.函数重载解析的步骤如下 确定函数调用考虑的重载函数的结合确定函数调用中实参表的属性 从重载函数集合中选择函数该函数可以在给出实参个数和类型的情况下可以调用函数 选择与调用最匹配的函数。
名字粉碎
在c中可以实现函数重载就是因为名字粉碎技术使得重载函数在编译过程中进行了区别。例如 _fastcall调用约定在输出函数名前加上一个“”符号在函数名后面也是一个“”符号和其参数的字节数。格式为functionnamenumber
C编译时函数名修饰约定规则
_cdecl调用约定 1.以“”表示函数名的开始后面跟函数名 2.函数名后面以YA表示参数表的开始后跟参数表 3.参数表以代号表示 PA–表示指针后面的代号表明指针类型如果相同类型的指针连续出现以“0”代替一个“0”代表一次重复 4.参数表的第一项为该函数的返回值类型其后一次为参数的数据类型指针表示在其所指护具类型前 5.参数表后以“Z”标识整个名字的结束如果该函数无参数则以“Z”标识结束。
函数模板
为了代码重用代码就必须是通用的通用的代码就必须不受数据类型的限制。那么我们可以把数据类型改为一个设计参数。这种类型的程序设计我们称之为参数化程序设计。软件模块由模板构造包括函数模板和类模板。 函数模板可以用来创建一个通用功能的函数以支持多种不同形参简化重载函数的设计。函数模板定义如下
template模板参数表
返回类型 函数名形式参数表 {...;//函数体
}模板参数表尖括号中不能为空参数可以有多个用逗号分开模板参数主要是模板类型参数。 模板类型参数代表一种类型由关键字class或typenname后加一个标识符构成在这里两个关键字意义相同他们表示后面的参数名代表一个潜在的内置或用户设计的类型。
templateclass T
T my_max(T a,T b){return ab?a:b;
}
int main() {my_max(12,23);my_max(a,b);my_max(12.34,23,56);return 0;
}在编译过程中根据函数模板的实参构造出独立的函数这就是模板函数这个构造过程称之为模板实例化。
名字空间nameplace
在C中支持三种域局部域命名空间域类域。命名空间域即使随C引入的相当于给一个文件域用花括号把文件的一部分括起来并以关键字namespace开头给他起名。括起来的部分称为声明块可以包括类变量函数等。主要为了解决全局名字空间污染问题即命名冲突。举例
namespace yhp{int g_max10;int g_min0;int my_add(int a,int b){return a-b;}
}名字空间域可以进行分层嵌套同样有分层屏蔽作用同一个工程中允许存在多个相同的明明空间编译器最终会合成同一个命名空间。
命名空间的使用
1.加命名空间及作用域限定符
int main() {int ayhp::my_add(12,23);printf(%lf n,Primer::pi);Primer::MAtrix::my_max(a,b);return 0;
}2.使用using将名字空间中成员引入 使用using声明可只写一次限定修饰名。usiing声明以关键字using开头后面是别限定修饰的名字空间成员名
using yhp::pi;
using Primer::Matrix::my_max;
int main() {printf(%lf n,Primer::pi);printf(%f n,pi);my_max(a,b);return 0;
}3.使用using namespace名字空间名称引入 使用using指示符可以一次性的使用空间中的所有成员直接被使用比using声明方便。
//A.h
namespace yhp {int my_add(int, int);int my_sub(int,int);
}
//A.cpp
#includeA.h
namespace yhp {int my_add(int a, int b) {return a b;}int my_sub(int a, int b) {return a - b;}
}
//main.cpp
#includeA.h
#includeiostream
using namespace std;using namespace yhp;int main() {printf(%d n,my_add(12, 23));return 0;
}
使用using指示符标准C库中所有组件都在一个std的名字空间中声明和定义的所以使用标准C库只写一个using指示符using namespace std就可以直接使用标准C库中所有成员。
new/delete
可执行空间的虚拟地址空间
内核操作系统 栈区函数的形参非静态的局部变量函数现场保护数据等等 栈是向下增长的。 共享库的内存映射区域用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存做进程通信。 堆区用于程序运行时动态分配内存堆时可以向上增长的。 数据段存储全局数据和静态数据。 代码段可执行的程序和常量数据。 C语言的动态内存管理用malloccallocrealloc等函数。
int* ipa(int*)malloc(sizeof(int)*n);
int *ipb(int*)calloc(n,sizeof(int));
ipa(int*)realloc(ipa,sizeof(int)*n*2);C的动态内存管理
1.new的运算符使用
int main() {int n10;int* ipanew int(10);//直接进行赋值int* ipbnew int[3]{1,2,3};int* ipcnew int[n]{1,2,3,4,5,6,7};//数组进行赋值delete ipa;delete[]ipb;//释放delete[]ipc;return 0;
}2.new的函数方式使用
int main() {int n 10;int* ipa (int*)::operator new(sizeof(int));int* ipb (int*)::operator new(sizeof(int) * n);::operator delete(ipa);::operator delete(ipb);return 0;
}3.定位new(placement newexpression)的使用
int main() {int n10;int* ipa(int*)malloc(sizeof(int));int* ipb(int*)::operator new(sizeof(int)*n);new(ipa)int(20);new(ipb)int[]{1,2,3,4,5,6,7,8,9};free(ipa);::operator delete(ipb);return 0;
}4.对于内置类型new/delete/malloc/free可以混用。 区别 new/delete是C中的运算符。malloc/free是函数。 malloc申请内存空间时手动计算所需大小new只需类型名自动计算大小 malloc申请的内存空间不会初始化new可以初始化 malloc的返回值为void*,接收时必须强转new不需要 malloc申请内存空间失败时返回的是NULL使用时必须判空new申请内存空间失败时抛出异常所以要有捕获异常处理程序。
C11的新特性
类型推导
auto类型推导auto定义的变量可以根据初始化的值在编译时推导出变量名的类型。
int main() {auto x5;//x为int类型auto pinew auto(1);// pi被推到为int*const auto *xpxu6//xp是const int*类型u是const int类型static auto dx0.0//doubleauto int b;//C11中auto不再表示存储类型指示符auto s;//error 没有初始化 auto无法推导出s的类型
}int main() {auto x5;const auto *xpx,u;//errorconst auto* ipx,u6.0;//errorreturn 0;
}1.虽然经过前面const auto* xpx推导auto的类型可以确定为int了但是仍要写后面的6否则编译器不予通过。 2.u的初始化不能使编译器推导产生二义性。 auto的一些使用方法可以和同指针引用结合起来使用还可以带上cv限定。 1当不声明为指针或引用时auto的推到结果和初始化表达式抛弃引用和cv限定后类型一致. 2当声明为指针或引用时auto的推导结果将保持初始化表达式的cv属性。 auto不能作为函数形参类
auto的限制 C11中auto称为类型指示符 auto不能用于函数参数 auto不能用于非静态成员变量 auto无法定义数组 实例化模板时不能使用auto作为模板参数
可以推导函数返回值
templateclass T
T my_max(T a,T b){return ab?a:b;
}int main() {auto xmy_max(12,23);auto ymy_max(a,b);coutx yendl;return 0;
}decltype关键字
auto所修饰的变量必须被初始化即必须要定义变量若仅希望得到类型不需要定义变量应该怎么办呢C11新增了decltype关键字decltype(exp),exp表示一个表达式器推导过程是在编译器完成的不会真正的计算表达式的值。
int main() {int x10;decltype(x) y1;//y-intconst int ix;decltype(i) jy;//j-const int decltype(xy) z0;//z-intconst decltype(z)*ipz;//*ip-int,ip-int*
}也可以用于函数表达式但不会真正计算表达式的值。
基于范围的for循环
不同容器和数组遍历的方法不尽相同写法不同意也不够简洁而C11基于范围的fpr循环以统一简洁的方式来遍历容器和数组用起来更加方便了。
遍历数组容器
int main(){int arr[]{1,2,3,4,5};int lensizeof(arr)/sizeof(arr[0]);int* ipNULL;for(iparr;ip!iplen;ip){cout*ipendl;}
}基于范围的for循环
int main() {int arr[]{1,2,3,4,5};for(int x:arr){coutx ; }return end;return 0;
}模板
for(ElenType val:array){...//循环体
}
//ElenType:是范围变量的数据类型。他必须是数组元素相同的数据类型或者数组元素可以自动转换过来的类型。
//val是范围变量的名称。
//array是让该循环体进行处理的数组名称
//循环体中可以用continue结束本次循环也可以用break跳出循环可以使用auto自动推导出val类型
int main() {int ar[]{1,2,3,4,5};for(auto x:ar){x10;coutx ;}
}指针空值–nullptr nullptr是C11新引入的指针控制类型的常量在C中可以直接使用。 sizeof(nullptr)与sizeof((void*)0)所占字节数相同都48。 为了提高代码的健壮性在后续表示指针空值最好用nullptr。
typedef与using的使用
typedef unsigned int uint;
using uintunsigned int;templateclass _Ty
using pointer_Ty*;
int main() {int x10;double dx12.25;pointerintipx;pointerdoubledpdx;return 0;
}
string的使用
int main() {char str[] { tulun };int len strlen(str);string s1 tulun;for (int i 0; i len; i) {cout str[i];}for (int i 0; i s1.size(); i) {cout s1[i];}cout s1 endl;s1 hello;cout s1 endl;s1 tulun;cout s1 endl;cin s1;cout s1 endl;return 0;
}运行结果