上午:
1、C++简介【了解】
2、编译C++程序【重点】
A、程序基本结构 看PPT范例
B、源码命名及编译工具 看PPT范例
C、编译流程
【预处理及编译的作用,链接的作用】 上课代码及注释
1 #include <stdio.h> 2 3 #if 0 4 指定标准C++库 5 gcc 源文件 -l stdc++ 6 默认输出就是a.out 7 8 g++ 源文件 9 默认输出就是a.out 10 11 g++ 源文件 -o 可执行文件名 12 指定输出可执行文件 13 #endif 14 15 int main() 16 { 17 printf("hello C++.\n"); 18 return 0; 19 }
结论: 头文件只能声明不能定义,内联函数除外
编译错误,是语法问题,编译器明确告诉哪个源文件,多少行,什么原因
链接错误:使用的不存在,库没有指定,写错了,等等
3、C和C++兼容及差异【重点】
A、常变量 上课代码及注释
1 #include <stdio.h> 2 3 //全局变量是静态数据,不能修改,没有写的权限,C/C++都会段错误 4 //const int i = 1; 5 //int i = 1; 6 7 int main() 8 { 9 //局部时来自栈区,可以读写的 10 //加const,C:仅仅语法上检查有没有修改的,修改的语法错误 11 // i = 123;语法错误 12 //C++不允许const被修改,故此这样错误的代码运行也不符合逻辑的 13 const int i = 1; 14 int *p = (int *)&i; //这个强制转换是不对的!!! 15 *p = 123; 16 printf("%d, %d\n", i, *p); //C打印123 123;C++打印1(改不了!!!),123 17 18 return 0; 19 }
B、强制转换 看PPT范例
C、类型检查更为严格 看PPT范例
隐式转换是有风险的,故此C++很多转换不允许,结论就是:转换最好显示,且不能乱转!!!
譬如常变量被转换成非const指针访问,这样是错误的!!!
D、C++未初始化全局变量不能同名 看PPT范例
E、C++不允许函数隐式声明【声明的作用及目的】 看PPT范例
声明函数的目的是让编译器检查有没有用错,故此一定要声明函数!!!
F、C++函数参数列表是空就是void 看PPT范例
G、C++函数不允许有默认类型 看PPT范例
H、引用reference 上课代码及注释
1.引用就是起别名
1 #include <stdio.h> 2 3 //实际一个整型量有两个及以上的变量名。语法叫第一个是变量名,其他的都是变量名的别名 4 int i = 0; 5 //声明定义初始化 6 //声明j是i的别名,这里&和代码中的&不一样的意义!!! 7 int &j = i; 8 int &k = i;//再来一个别名 9 int &l = j; //别名的别名 10 11 int main() 12 { 13 printf("%d, %d\n", i, j); 14 printf("%p, %p\n", &i, &j); //取地址操作,打印ij的地址 15 16 i = 123; //通过变量名i修改整型值 17 printf("%d, %d\n", i, j); 18 19 j = 456;//通过别名j修改整型值 20 printf("%d, %d\n", i, j); 21 22 return 0; 23 }
2.引用最常用的地方就是函数参数和返回值
1 #include <stdio.h> 2 3 //形参是实参的别名,那么就不存在C说的传递值和传递地址 4 //C语言:实参 --拷贝值/地址--> 形参 5 //如果形参是别名,那么就不存在这样的拷贝,仅仅函数内外都操作同一个变量 6 void xchang(int &x, int &y) 7 { 8 int t = x; 9 x = y; 10 y = t; 11 printf("x=%d, y=%d\n", x, y); 12 } 13 14 int main() 15 { 16 int a = 1, b= 2; 17 printf("a=%d, b=%d\n", a, b); 18 xchang(a, b); 19 printf("a=%d, b=%d\n", a, b); 20 21 return 0; 22 }
I、函数重载 上课代码及注释
1.C语言没有函数重载使用不定参数达到雷同的效果:但是不好!!!
1 #include <stdio.h> 2 3 //...就是参数不定的意义 4 //add调用时,有且至少要一个参数,后面有几个根据实际情况来 5 //最大的问题是参数的数量及类型,编译器无法检查,C++的函数重载就解决了这些问题!!! 6 int add(int x, ...) 7 { 8 //1.得到参数数量及列表 9 //2.提取参数 10 //printf就是不定参数,open也是,ioctl也是,等等!!! 11 printf("1 int.\n"); 12 return x+x; 13 } 14 15 int main() 16 { 17 printf("add=%d\n", add(1,2)); 18 printf("add=%d\n", add(1)); 19 printf("add=%d\n", add(1,2.3)); 20 21 return 0; 22 }
2.C++函数重载
1 #include <stdio.h> 2 3 //函数同名参数不同,就是函数重载 4 //目的是想解决:函数过程相似!!!不用多个函数名,便于使用!!! 5 6 int add(int x) 7 { 8 printf("1 int.\n"); 9 return x+x; 10 } 11 12 int add(int x, int y) 13 { 14 printf("2 int.\n"); 15 return x+y; 16 } 17 18 //什么时候该函数重载呢?函数过程相似,而不是一致!!!【后面模板来讨论】 19 //先记住:一致应该是模板,相似才是函数重载!!! 20 double add(double x, double y) 21 { 22 printf("2 double.\n"); 23 return x+y; 24 } 25 26 #if 0 27 过程一致,数据类型不同而已 28 //typedef int T; 29 typedef double T; 30 T add(T x, T y) 31 { 32 return x+y; 33 } 34 35 int add(int x) 36 int add(int x, int y) 37 才是相似:多数情况是不一样的,一个x+x,一个是x+y 38 #endif 39 40 int main() 41 { 42 printf("add=%d\n", add(1,2)); //add(int, int) 43 printf("add=%d\n", add(1)); //add(int) 44 printf("add=%g\n", add(1.1,2.2));//add(double,double) 45 46 //根据语法而不是逻辑,编译器自动选择该是哪个版本的函数 47 //没有逻辑的事情!!!2不能理解成会转换为double!!! 48 printf("add=%g\n", add(1.1,2));//add(double, int),没有,则报错!!! 49 #if 0 50 不要想当然的认为2会是double,如下代码验证,2实际有转换的效果!!! 51 double db = double(2); 52 double db = (2); //类型实际有个转换,叫隐式转换,故此不建议,转换最好显示 53 #endif 54 55 return 0; 56 }
J、函数的默认参数 上课代码及注释
1.C++中函数的默认参数
1 #include <stdio.h> 2 3 #if 0 4 1.函数参数可以有默认值 5 且只能是声明时有默认值,定义时不能有 6 如果声明定义在一起 7 8 2.默认值必须从右边开始,且不能跳 9 int add(int x=0, int y=0, int z) 错误:右边z没有默认值!!! 10 int add(int x=0, int y, int z=0) 错误:y被跳过了,语法不允许!!! 11 12 默认只能如下: 13 int add(int x, int y, int z=0) ok 14 int add(int x, int y=0, int z=0) ok 15 int add(int x=0, int y=0, int z=0) ok 16 17 3.参数传递的规则 18 实参给最左边的形参赋值,不能跳 19 add(1,2,3) 20 1--->x 21 2--->y 22 3--->z 23 24 add(1,2) 25 1--->x 26 2--->y 27 z默认值 28 29 #endif 30 31 int add(int x=0, int y=0, int z=0) 32 { 33 return x+y+z; 34 } 35 36 int main() 37 { 38 printf("add=%d\n", add()); //x默认值0,y默认值0,z默认值0 39 printf("add=%d\n", add(1));//x=1,y默认值0,z默认值0 40 printf("add=%d\n", add(1,2));//x=1,y=2,z默认值0 41 printf("add=%d\n", add(1,2,3));//x=1,y=2,z=3 42 43 return 0; 44 }
2.函数参数的默认只能在声明时有
#include <stdio.h> //只能声明时有默认 //函数原型声明:函数名、参数、返回值说明下 int add(int x=0, int y=0, int z=0); int main() { //在C++中,调用函数必须有声明 //一般不好的做法是,把函数的声明定义放在使用前面,那么不用单独声明了 printf("add=%d\n", add()); printf("add=%d\n", add(1)); printf("add=%d\n", add(1,2)); printf("add=%d\n", add(1,2,3)); return 0; } //错误!!!定义时候不能有 //int add(int x=0, int y=0, int z=0) //定义函数:把函数体的逻辑写出来。ok int add(int x, int y, int z) { return x+y+z; }
K、重载+默认参数引起的歧义 (函数默认参数和函数重载在一起:小心!!!) 上课代码及注释
1 #include <stdio.h> 2 //越抽象高级的编程语言:可能多种语法在一起会有歧义,小心 3 4 //函数同名了,且参数数量不一样,故此是重载,ok的 5 int add(int x=0, int y=0, int z=0) 6 int add(int x=0, int y=0) 7 8 //默认参数的调用可能如下: 9 //add() ok:x=0,y=0,z=0 10 //add(1) ok:x=1,y=0,z=0 11 //add(1,2) ok:x=1,y=2,z=0 12 //add(1,2,3) ok:x=1,y=2,z=3 13 int add(int x=0, int y=0, int z=0) 14 { 15 return x+y+z; 16 } 17 18 //默认参数的调用可能如下: 19 //有且至少传递一个参数 20 //add(1) ok:x=1,y=0 21 //add(1,2)ok:x=1,y=2 22 int add(int x, int y=0) 23 { 24 return x+y; 25 } 26 27 int main() 28 { 29 //只能选int add(int x=0, int y=0, int z=0) 30 // printf("add=%d\n", add()); 31 //int add(int x=0, int y=0, int z=0)和int add(int x, int y=0)都可选:歧义!!! 32 // printf("add=%d\n", add(1)); 33 //int add(int x=0, int y=0, int z=0)和int add(int x, int y=0)都可选:歧义!!! 34 // printf("add=%d\n", add(1,2)); 35 //只能选int add(int x=0, int y=0, int z=0) 36 // printf("add=%d\n", add(1,2,3)); 37 38 return 0; 39 }
4、再谈结构体【了解】
C++的结构体中可以定义函数,其余与C的用法相同。
下午:
5、内存模型和名字空间【重点】
A、作用域
1 #include <stdio.h> 2 3 //实际写代码时,必须保持形参名一致,不是语法要求,而是设计要求 4 int add(int x=0); //x的作用域仅仅是声明函数的(),跟定义的形参是两个作用域 5 6 int main() 7 { 8 int m=0; //局部变量,作用范围是声明开始后以后区域 9 10 for(int i = 0; i < 100; i++){ //i仅仅在for及循环体内可见 11 int k = 0; //k仅仅循环体内可见,强调这里不是多次定义的意思,是说作用域是循环体内 12 k = i; 13 } 14 15 switch(m){ 16 case 1: 17 { 18 int i=0; //加{}局部化,仅仅在代码块可见,那么跳转就不受影响 19 } 20 break; 21 case 2: 22 int j=0;//错误!j的作用域在声明后的所有switch区域可见,但是由于跳转出现25行不存在的问题!!! 23 break; 24 case 3: 25 //j++; //即使这样不写这个代码22行的做法不符合语法,强调:不是逻辑,而是语法 26 break; 27 }; 28 29 { 30 //一般{}括起来的,就是局部作用域 31 } 32 33 34 return 0; 35 } 36 37 int add(int a) 38 { 39 return a+a; 40 }
B、链接及存储性
重点:生命期,作用域
a.out加载到内存变成进程,故此a.out有的进程也有唯一的一份
静态数据或函数才存在链接的概念,动态的不存在链接
1、全局变量静态数据:生命是程序的唯一的一份
作用域:所有文件可见
int i ;//未初始化,语法意义是告诉编译在生成的a.out中开辟4字节空间,没有初始值【gcc一般初值设置为0】
int i=0 ;//初始化,语法意义是告诉编译在生成的a.out中开辟4字节空间,初始值为0
作用域:本文件可见
static int i = 0;//静态全局变量:本文件可见【作用域】语法意义是告诉编译在生成的a.out的数据开辟4字节空间,初始值为0
2、常变量
const char *p = "hello"; “hello”在a.out的只读数据区开辟空间
const int i = 0;语法意义是告诉编译在生成的a.out中开辟空间,常变量必须有初值
3、静态局部变量
int main()
{
//作用域:代码块可见
static int i = 0; //初始化,语法意义是告诉编译在生成的a.out中开辟4字节空间,初始值为0
}
4、局部变量:动态数据,生命期是局部的,自动完成,不一定是一份
int test()
{//调用test才产生i,同时调用多次,就产生多份i,验证仅仅打印地址就可以了
//i不名字冲突原因是局部域
//局部变量,a.out中不存在,运行后代码走这里才压栈产生,函数退出自动回收,故此是动态数据
int i = 0;
}
5、堆变量:动态数据,生命期是程序员控制
int test()
{
//a.out中不存在,运行后代码走这里才从堆分配,不free就一直在,直到进程空间被系统回收,故此是动态数据
//内存泄漏的问题!!!
int *p = malloc(4);
}
C、关键字
static、mutable、volatile
1 #include <stdio.h> 2 3 //static修饰都是静态的 4 //数据是静态数据:一份程序生命期,作用域看是什么变量 5 //函数一定是静态的【是有还是无的问题】 6 //static 是限定作用域的!!! 7 8 //限制函数仅仅能在本文件可见 9 static void func() 10 { 11 } 12 13 //限制变量i仅仅能在本文件可见 14 static int i = 111111; 15 16 int main() 17 { 18 //限制变量j仅仅能在代码块可见 19 static int j = 333333; 20 }
1 #include <stdio.h> 2 3 struct data{ 4 mutable int x; 5 //如下的成员较多,且经常出行x要被修改,其他成员不被改的时候 6 //mutable修饰x,整个结构体变量用const修饰,那么就比较简洁 7 8 int y; 9 //...这里几十个,假设不被修改,如果都加const修饰很麻烦!!! 10 }; 11 12 //data的第一种使用x,y都要修改,那么x,y都不应该被const修饰 13 //data的第二中使用x要改,y不被改,可以像这个演示代码这样做 14 //这里就可以较为灵活 15 16 int main() 17 { 18 const data a = { 19 .x = 123, 20 .y = 456, 21 }; 22 23 a.x = 123; //ok,结构体变量对象是只读的,当x成员加mutable就可以改 24 a.y = 123;//error!!!a对象只读,故此y不能修改 25 26 }
#if 0 volatile关键字会阻止编译器进行优化,不会让上面优化结果出现【当优化是个问题的时】 test.cc volatile int i = 0;//阻止编译器优化涉及操作i的代码 int k = 0; //涉及操作k的代码会被编译器优化 #endif int main() { //如果要使用一次i就必须从内存读一次,这个就是volatile的作用 //一般底层硬件开发时候常用!!! for(i = 0; i < 100; i++) k++; }
D、语言链接性
1.可执行程序
g++ test.cc
默认生成a.out
2.制作C库目录的代码
1 #include <stdio.h> 2 #include <demo.h> 3 4 //1.没有main,只能编译成库 5 //2.没有加static,函数名导出符号表,外部模块可以使用 6 int prnmsg(char *str) 7 { 8 if(NULL != str) 9 return printf("shared object library: %s\n", str); 10 return (-1); 11 }
make编译生成C库libdemo.so
1 CC = gcc 2 LIBS = libdemo.so 3 OBJS = $(patsubst %.c, %.o, $(wildcard *.c)) 4 5 #-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code), 6 # 则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意 7 # 位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的 8 CFLAGS += -Wall -O2 -g -fPIC -std=gnu99 -DMYDBG -I. 9 LDFLAGS += -shared 10 11 $(LIBS): $(OBJS) 12 $(CC) $(LDFLAGS) -o $@ $^ 13 14 clean: 15 rm -f $(OBJS) $(LIBS)
3.libdemo.so提供函数供a.out使用
1 #include <stdio.h> 2 3 extern "C" int prnmsg(char *str); 4 5 int main() 6 { 7 char str[] = "hello"; 8 prnmsg(str); 9 }
①编译时
a.out的调用函数prnmsg(), 链接时要通过符号表【函数名<--->地址】查找函数prnmsg()在libdemo.so中哪里【地址】
C++的符号表和C符号表规则上不一样,故此必须声明
extern "C" int prnmsg(char *str);
意思prnmsg()是C函数
②运行时
要指定库
拷贝到运行时候库目录下
修改库的配置文件/etc/ld.so.conf.d
环境变量登记
假设库在当前目录下
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
E、运算符:new、delete
1 //强调:分配堆后,使用完应该要释放,否则会内存泄漏 2 //内存泄漏:程序不退出,堆使用不释放,且不再使用,这样的情况就是一种内存泄漏!!! 3 //进程空间是有限的,这样的话,就可能会造成空间不足,起码是资源浪费!!! 4 int main() 5 { 6 //new分配一个整型堆空间 7 int *p1 = new int; 8 9 //使用堆的逻辑:根据代码逻辑来 10 11 //释放一个整型堆空间 12 delete p1; 13 14 //new分配10个整型堆空间 15 int *p2 = new int[10]; 16 17 //使用堆的逻辑:根据代码逻辑来 18 19 //p2指向的N个整型堆空间 20 delete [] p2; 21 }
F、声明区及作用域
G、名字空间
1.名字空间:解决重名【名字冲突】
1 #include <stdio.h> 2 3 //func()和i有两份,重名了 4 //可以使用名字空间来解决重名:就是限定了作用域!!! 5 6 //static也是限定作用域,但是针对全局变量仅仅是本文将限定,故此范围太宽!!! 7 8 namespace zhang{ 9 void func(){ 10 printf("zhang: hello.\n"); 11 } 12 13 //静态数据不变,作用域发生变化!!! 14 int i = 123; 15 } 16 17 namespace li{ 18 void func(){ 19 printf("li: hello.\n"); 20 } 21 int i = 456; 22 } 23 24 int main() 25 { 26 //1.使用时说明其名字空间,如果名字空间的名字较多,使用频繁就麻烦 27 li::func(); 28 29 printf("zhang: i = %d.\n", zhang::i); 30 }
2.一次性说明某个名字空间的名字
1 #include <stdio.h> 2 3 namespace zhang{ 4 void func(){ 5 printf("zhang: hello.\n"); 6 } 7 int i = 123; 8 } 9 10 namespace li{ 11 void func(){ 12 printf("li: hello.\n"); 13 } 14 int i = 456; 15 } 16 17 int main() 18 { 19 //即使一次性说明,如果名字空间的名字较多,都说明起来还是麻烦 20 21 //一次性说明,func是来自zhang名字空间的名字 22 using zhang::func; 23 //一次性说明,i是来自zhang名字空间的名字 24 using zhang::i; 25 26 func(); //zhang名字空间的 27 printf("zhang: i = %d.\n", i);zhang名字空间的 28 29 li::func(); //要使用li的func就必须说明下 30 printf("li: i = %d.\n", li::i); 31 }
3.仅仅说明当前名字空间名
1 #include <stdio.h> 2 3 namespace zhang{ 4 void func(){ 5 printf("zhang: hello.\n"); 6 } 7 int i = 123; 8 } 9 10 namespace li{ 11 void func1(){ 12 printf("li: hello.\n"); 13 } 14 int i1 = 456; 15 } 16 17 //声明没有使用名字空间的名字都是zhang名字空间的 18 using namespace zhang; 19 //using namespace li;如果zhang和li没有名字冲突,则可以,存在则不可以 20 21 int main() 22 { 23 func(); //没有指定名字空间,则来自zhang 24 printf("zhang: i = %d.\n", i);//没有指定名字空间,则来自zhang 25 26 li::func(); 27 printf("li: i = %d.\n", li::i); 28 }
6、标准输入输出流【熟悉常用】
A、输出流
1 #include <iostream> //输入输出流的头文件 2 //标准C++库的名字空间一般都是std 3 using namespace std; 4 5 int main() 6 { 7 //把字符串交给输出流,endl就是回车输出 8 cout << "hello world\n"; 9 10 cout << "hello world" << endl; 11 12 cout << "hello " 13 "world" << endl; 14 15 cout << "hello "; 16 cout << "world" << endl; 17 18 //类型狭义的语义:数据格式+存储大小 19 //int:有符号整型。强调:实际使用不是必须是整型,类型转换,关注4字节空间!!! 20 int a = 123; 21 double db = 1.23; 22 char ch = 'a'; 23 //cout有很多格式符,a想以什么格式输出都可以的:类似printf的%.3x 24 //格式符号不讲了!!!因为一般输入输出仅仅是程序跟踪打印使用 25 //不太会制作界面,界面往往是GUI【图形界面】,故此要用时再熟悉下就好了!!! 26 cout << "a = " << a << " ; db = " << db << "; ch = " << ch << endl; 27 }
B、输入流
1 #include <iostream> 2 using namespace std; 3 4 int main() 5 { 6 int a = 123; 7 double db = 1.23; 8 char ch = 'a'; 9 10 cout << "please input: \n"; 11 12 //从输入流中读出整型到a, 读出浮点数到db, 读出一个字符到ch 13 //如果输入是正确的,那么通过空格或回车【用户输入时】 分割a、db、ch的内容 14 //如果输入的不正确,那么cin马上报错返回,这里,怎么得到错误码【异常】 15 //cin不是函数!!! 16 cin >> a >> db >> ch; 17 18 cout << "a = " << a << "; db = " << db << "; ch = " << ch << endl; 19 }
#include <stdio.h>
namespace zhang{
void func(){
printf("zhang: hello.\n");
}
int i = 123;
}
namespace li{
void func(){
printf("li: hello.\n");
}
int i = 456;
}
int main()
{
//即使一次性说明,如果名字空间的名字较多,都说明起来还是麻烦
//一次性说明,func是来自zhang名字空间的名字
using zhang::func;
//一次性说明,i是来自zhang名字空间的名字
using zhang::i;
func(); //zhang名字空间的
printf("zhang: i = %d.\n", i);zhang名字空间的
li::func(); //要使用li的func就必须说明下
printf("li: i = %d.\n", li::i);
}
原文链接: https://www.cnblogs.com/Jiagege/p/12669141.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;
也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/341522
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!