C++

上午:

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大佬

    C++

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/341522

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 上午1:07
下一篇 2023年3月2日 上午1:07

相关推荐