Java 转 C++ 知识点

2023-10-26


适合具备一定基础的同学上手,都是一些个人觉得十分重要的小知识点。
解释性语句并不多,具体展开请读者自行搜索。

本人只是c++初学者,如有疏漏,敬请留言指正!


配置Clion同步远程环境

在这里插入图片描述

credentials是添加远程连接。
爆红Not found的,请自行在远程服务上下载好(我这里本来有cmake,但是由于版本过低,需要更新)


在这里插入图片描述

toolchain选择刚刚配置好的远程选项


回车确定之后,环境就跟远程同步了(windows下可以使用#include <sys/socket.h>测试,看是否找得到这个头文件,因为这个头文件是linux系统调用)


Clion远程小工具
远程路径Depolymentpath自动创建的为临时路径,这里可以自己指定,实现映射远程项目到本地。如下图:

在这里插入图片描述


在clion界面右侧打开远程文件系统。如下图:

在这里插入图片描述

打开一个类似git的版本差异比较窗口,可以在该窗口实现向远程同步,如下图

在这里插入图片描述

其他同步操作都在Tools中,具体操作见名知意即可。


附:更新\下载 cmake(注意clion的支持版本)

卸载旧版
sudo apt-get autoremove cmake

下载新版
cd ~
wget https://cmake.org/files/v3.19/cmake-3.19.8.tar.gz
tar xvf cmake-3.19.8.tar.gz
cd cmake-3.19.8

安装
./bootstrap --prefix=/usr
 make
 sudo make install

测试
cmake –version

0. 内存模型

1)stack 栈区:由编译器自动分配和释放
一般存放函数的参数值、局部变量的值等
2)heap 堆区:由程序员分配及释放。若程序员不释放,程序结束后可能由OS回收
3)register 寄存器区:用来保存栈顶指针和指令指针
4)全局区(静态区):全局变量和静态变量是存储在一起的。初始化的和未初始化的是分开的。
程序结束后由系统释放。分为data段(已初始化)和bss段(未初始化)
5)文字常量区:程序结束后由系统释放,存放常量字符串
6)text 程序代码区:存放函数体的二进制代码

与Java相比:

  • c++的heap需要程序员手动分配和释放
  • C++的堆和栈都可以用于存放对象,以哪种方式管理对象取决于开发人员的代码(比如栈上分配)

定义变量和数组时,Java默认初始化,C++不初始化;
在类中,方法中定义变量、动态数组时,Java默认初始化,C++不初始化

1. 变量的作用域

  • 全局变量:直接在函数外部定义的,可跨文件使用(跨文件使用的地方用extern声明使用的是其他文件中的,extren还表示不能改变被修饰的对象的类型以及值),有重名风险

  • static:

    • C语言:直接在函数外部定义的,但是加了static,表示仅在当前文件中全局使用(凡是加上了static的(包括函数),都不允许外部使用,相当于private),静态分配
    • CPP:跟Java用法一致

2. typedef与define的区别

在这里插入图片描述

define仅仅是做简单的替换(或者一些运算表达式),在编译时期完成
typedef 对于类型定义提供了更丰富的支持,在运行时完成

3. 类的继承范围

类的继承范围,指的是子类改变父类数据的最高权限:如果是public继承,则改变父类数据最高到public,如果是private,则把父类继承的所有数据改为private。

4. 常量与常函数

常量只可以访问常函数,所以对于只读函数必须加上const,不然会导致常量不可调用该函数。
传引用或者传指针,可以在函数内改变该对象,为了告知调用者函数是否真的做了改变,需要通过函数参数是否加了const来辨别(尤其是一些不开源的代码,这个尤为重要)。

const是编译时检查,运行时其实是可以修改的(如const_cast)
const在号前面,则值为常量,在后面,则指针为常量。

5. 传值、传引用

传值是新建副本传过去(整包传),如果数据太大,则也会消耗较大内存。
尽量不要传值。

传引用则仅仅是传该数据的地址。
传引用跟传指针是一样的,但是传引用更方便。
为了避免传引用被改,改成传const引用即可。

返回值也尽量传引用,但是在函数内部创建的res不能返回引用,栈上分配的“内存空间”结束后直接被回收,因此直接传值,或者将返回对象作为参数传入,最后返回该引用。(栈上分配的static函数调用完毕不会被释放,因此可以返回引用(如懒加载单例))

6. 友元的意义

友元避免了通过get获取private数据,提高速度;
不过友元破坏了封装性;
此外,友元关系不可传递(A是B的朋友,B是C的朋友,但A不是C的朋友);
友元关系不可继承;

7. 左移运算符重载

左移运算符只能写为全局函数(直接全局或者先友元定义然后类外实现),因为cpp所有操作符都是作用在左值(如果写在类内,则表示obj<<cout(obj是隐含的),这显然不合适),因此需要写在全局,控制cout在左边。

8. 析构函数

delete xx会被转化为两个语句:
调用析构:~xx()
释放名称空间:free(xx)

delete[] xx:

  1. 调用n次析构
  2. 释放名称空间

因此array new要与array delete一一对应,因为delete[] 表示会调用数组个数次析构函数,而delete仅仅是回收开辟的空间,但是析构函数只会调用一次。在析构函数中的释放动作就不会被完全执行。

9. 带指针的类

带指针的类,指的是属性成员中有指针变量。
必须重写三个方法:
拷贝构造:避免浅拷贝(指针直接指向同一块区域导致回收时重复回收)
赋值重载:避免浅拷贝(指针直接指向同一块区域导致回收时重复回收)
析构函数:指针开辟在堆,需要回收

赋值重载首先考虑可能存在自我复制,其次是清空原空间。

10. 转型

个人理解,cpp实质上仅仅允许向上转型。

即便看起来是向下转型转型成功,其实也必须运行时是向上转型。
比如继承链D->C->B->A (A为顶级父类)
A a=new D()
B b=dynamic_cast(a);
看起来是a向下转成了b,实质还是运行时的d向上转型成了b

11. 多态

必须在父类方法上加virtual,才能在通过父类指针指向子类对象的时候,调用子类重写的方法。
(多态是virtual的多态)

12. 右值引用与move

右值只能放在右边(左值两边都能放),临时变量一定是右值。
没有名称的变量一定是右值。

一般情况下,只能获得左值的引用(因为右值没有名称),如果要获得右值的引用,则使用&&。

move拷贝构造,会将所有指针打断然后替换,相当于废弃原有变量(因此临时变量比较合适move)。
如果不是临时变量,想用move拷贝构造,使用构造函数(std::move(变量))即可。
move指的是把参数当做右值使用。

13. 智能指针

具备特殊功能的指针类。

内置的智能指针:用于解决内存泄露的一种指针自动回收机制(引用计数法):
unique_ptr:只允许被引用一次,作用域结束后自动回收
shared_ptr:可以被共享引用,其内存在一个引用计数器,计数器为0时自动回收。
weak_ptr:类似弱引用,查看是否被回收,如果没有被回收,还能再用一次

14. iterator_traits特征萃取

每个容器都持有自己的itreator,调用iterator进行特征获取的时候,分两步走:

  1. 如果T是容器,会询问容器自己的itreator
  2. 如果T不是容器,走偏特化

15. IO(C语言)

分为两类:
sysio:系统io,由操作系统提供,不同的操作系统sysio接口不同。
stdio:标准io,屏蔽系统接口细节,移植性好。

每个FILE只有一个游标,比如打开之后开始写,游标后移;这时候用读函数,也是从游标往后读,想从头读需要移动游标。

打开模式:r和r+读的对象必须存在,其他的模式不存在则会创建。

fwrite\fread返回读写成功的字节数
如果只剩5个字节,fwrite(buf,)

16. 缓冲

全缓冲:满了刷新,如文件
行缓冲:遇到换行符刷新,当流涉及到一个终端时,一般是行缓冲
无缓冲:即时刷新,标准错误流是无缓冲,保证立即能够被看到

17. 文件描述符

文件载入逻辑:
物理文件 --> inode(FCB) --> 程序打开文件产生结构体X --> X数组 --> X数组的下标就是文件描述符

系统存在一个指针数组(ulimit查看默认长度为1024),该数组中保存着指向控制文件的一个结构体的指针,文件描述符就是指针数组的下标。其中数组的前三个0、1、2固定分别对应stdin、stdout、stderr。

该指针数组由进程独享,各自进程创建各自的。
如下图所示。

在这里插入图片描述

18. 钩子函数

钩子函数指的是触发某些动作的时候,调用一系列注册的函数。
钩子函数分两类:exit类与信号类
exit类:
exit与_exit,exit调用后还有调用各种处理逻辑如钩子函数,但是如果是一些非法异常,这会导致钩子函数的调用导致故障扩大,此时应该调用_exit(或者abort),立即终止,什么也不动。
信号类:
通过信号注册函数,实现触发信号的时候,触发对应的函数。比如SIGINT信号(ctrl+C会触发),最好关联到exit信号上,避免程序异常退出没有进行资源回收。

19. fork、exec、wait

fork、wait、exec简称few,基本构建了Unix世界的多进程。
fork用于创建进程(进程复制),wait用于进程收尸,exec用于进程转换。
fork有两个返回值pid,在子线程返回值为0,在父线程非零。
调用fork之前必须fflush(NULL)刷新所有缓冲区,不然可能会导致后面的流输出异常。(同理,线程切换之前需要先刷新缓冲区)

fork是复制父线程为子线程。

exec是替换父线程为执行目标(一般是fork子进程后用exec将子进程替换成别的进程执行)

如果父进程在子进程结束之前结束,子进程会被init接管。如果子进程结束而父进程长期不结束,所有子进程会变成僵尸进程(僵尸进程虽然占用资源很少,但是他们占用了宝贵的进程号,进程号是有上限的),因此在子进程结束的地方,使用n个wait(NULL)来回收子进程。

20. 守护进程

脱离父进程,直到系统中所有进程都死亡的时候才消亡。(守护线程则是等到所有线程都结束才结束)。

因此,守护进程有以下特点:
ppid为1:因为脱离了父进程,由init接管;
pid–pgid–sid:因为守护进程脱离父进程后,自己变成了leader,他单独成为了一个group,单独有一个session;
TTY为?:终端,表示没有终端

当我们用ps axj查看到ppid为1,pid–pgid–sid,TTY为?的就是守护进程。
通过调用setid()使子进程成为守护进程(必须是子进程调用,因为要脱离父进程),返回一个sessionid。
通过/var/run/name.pid锁文件实现守护进程单例。创建守护进程的时候会创建该文件,该文件中保存着守护进程的进程号,当重复创建守护进程的时候会检查该文件,若存在则禁止创建。

创建守护进程的步骤

  1. 后台运行(fork,结束父进程)
  2. 脱离会话终端,登录自己的会话和进程组(setsid())
  3. 关闭打开的文件描述符
  4. 改变工作目录(进程活动的时候,原有的工作目录无法卸载,比如原有进程使用了U盘,因此需要更改工作目录)
  5. 进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);
  6. 处理SIGCHLD信号,避免子进程成为僵尸进程,signal(SIGCHLD,SIG_IGN);

21. 信号

信号分为两类:标准信号和实时信号。
kill -l查看,非SIGRTMIN开头的都是标准信号,其余都是实时信号。
两者的最大区别:对于连续的相同信号,标准信号指处理最后一个信号,而实时信号会让他们排队然后逐一执行。

标准信号:

  • 标准信号会打断阻塞的系统调用:因此对于可能阻塞的系统调用,当使用标准信号机制的时候,记得判断是真的错误,还是仅仅是被标准信号打断了。
  • 标准信号的行为不可靠:因为标准信号发出之后的调用由内核接管,如果第一次标准信号尚未处理完毕,第二次标准信号到来,可能会导致第一次或者第二次出现不可预料的问题(不可重入)(比如第二次调用来了之后,内核让该函数的现场与第一次调用的现场重复了)
  • 可重入与不可重入:可以重复调用且互不影响就是可重入,系统调用全都是可重入的,有些库函数会提供两个版本的方法(比如rand不可重入,rand_r可重入,很多_r结尾都表示可重入版本)简单理解为:凡是让系统自动完成的,都可能存在不可重入问题,因为系统自动去找地址处理,第一次处理的未结束第二次就来了,可能导致第二次分配的地址与第一次相同。
  • 标准信号从收到到响应有一个不可避免的延迟:
     pending位:存不存在标准信号(默认为0)
     mask位:要不要响应标准信号(默认为1)
     收到:当收到标准信号之后,修改进程的pending位,进程被打断,进入内核态,等待调度;
     响应:进入用户态的时候查看mask&pending,发现标准信号具体内容,响应标准信号内容。
     这就可以解释sleep之类的为什么不精准,因为这些本质上也是标准信号。
  • 标准信号忽略:将对应的mask置为0即可,这会导致mask&pending对应位为0
  • 重复标准信号只有最后一个会被响应,因为mask和pending只给对应标准信号提供了一位。
  • alarm(n),默认行为是n秒后结束程序(可以通过signal(SIGALRM, 行为函数)修改),一个进程中有多个alarm,则只有最后一个生效。
     有些平台的sleep是alarm+ pause实现的,因此会跟其他alarm冲突(仅一个生效),因此避免使用sleep。

实时信号:
相同信号排队执行,解决了标准信号只能响应一次且响应顺序未定义的情况

22. mutex和cond

单纯使用mut信号量,会造成忙等,结合cond(条件变量)能够等到通知再抢锁/释放锁,避免忙等。

23. 线程

线程取消:pthread_cancel(pthread_t)
cancel点:可能引发阻塞的系统调用都是cancel点,pthread_cancel调用后,只会在遇到cancel点之后才真正取消线程(避免突然结束导致钩子函数未执行导致资源泄露)

join:意思类似wait,调用join的线程会等待他创建的所有子线程执行完毕,执行完毕后对其进行收尸。

yield:极短暂的出让调度权(短暂的sleep,但不会引起调度颠簸)

线程分离:指的是抛弃与该线程的关系(本来是谁创建谁回收,分离之后就不管了)

24. select poll epoll

  • select:
    int select(int nfds, fd_set *readfds, fd_set *writefds,
    fd_set *exceptfds, struct timeval *timeout);
    nfds:所有文件描述符中最大的那个再+1(因此处理的的文件有上限,int类型)
    三个set:读事件、写事件、异常事件,事件的装载和结果集都共同这三个,会出现覆盖,以事件驱动;
    timeout:超时

  • poll:
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    返回结果=有多少个事件
    nfds类型变成了nfds_t,因此理论上没有上限;
    传入结构体pollfd,因此是以文件描述符驱动事件。
    pollfd中将传入事件和事件结果分开存放,不会被覆盖了。
    struct pollfd {
    int fd; /* file descriptor */
    short events; /* requested events */
    short revents; /* returned events */
    };

  • epoll

    • int epoll_create(int size); 创建一颗红黑树,挂载所有文件描述符,根节点就是返回值epfd,size为树的初始化大小。
    • int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 监听fd的event事件,将fd op(增删改)到epfd红黑树上。
    • int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); events是满足上述条件的所有事件数组(其内包含了触发的fd),maxevents为events数组初始化大小,返回值是满足事件条件的个数(用于遍历events)

epoll_wait分为ET(边缘触发)和LT(水平触发,默认)两种模式。

  • ET:读一次之后就返回,不管剩余数据(fd必须是非阻塞模式,通过fctl调整)
  • LT:读一次如果还有剩余数据,epoll_wait会再次返回该fd

应用场景:
一个数据包是500字节,服务端第一次只读到了400字节,第二次客户端再发过来了100字节,此时想读到剩余的100字节,就必须使用LT模式。

右键打开一个文件查看他的属性,这时候只需要读取一个文件head,剩余的文件内容我们并不关心(全部舍弃),这就是ET。

25. 闭源隐藏

不开源的手法(隐藏代码细节的手法)

  • 对外只提供头文件,c/cpp文件编译成静态库或者动态库提供。
  • 头文件中的一些指针,全部使用void替换,屏蔽底层类的细节。在调用的时候才将void转为实际指针。

26. 进程间通讯概要

详见参考:https://blog.csdn.net/zhaohong_bo/article/details/89552188
或者参考Unix网络编程

  • 匿名管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

  • 命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

  • 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点

  • 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

  • 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

  • 套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

  • 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

如何选择通信方式?

  • 共享存储器系统
    1.基于共享数据结构的通信方式
    (仅适用于传递相对少量的数据,通信效率低,属于低级通信)
    2.基于共享存储区的通信方式

  • 管道通信系统
    管道是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件(pipe文件)
    管道机制需要提供一下几点的协调能力
    1.互斥,即当一个进程正在对pipe执行读/写操作时,其它进程必须等待
    2.同步,当一个进程将一定数量的数据写入,然后就去睡眠等待,直到读进程将数据取走,再去唤醒。读进程与之类似
    3.确定对方是否存在

  • 消息传递系统
    1.直接通信方式
    发送进程利用OS所提供的发送原语直接把消息发给目标进程
    2.间接通信方式
    发送和接收进程都通过共享实体(邮箱)的方式进行消息的发送和接收

  • 客户机服务器系统
    1.套接字 – 通信标识型的数据结构是进程通信和网络通信的基本构件
    基于文件型的 (当通信进程都在同一台服务器中)其原理类似于管道
    基于网络型的(非对称方式通信,发送者需要提供接收者命名。通信双方的进程运行在不同主机环境下被分配了一对套接字,一个属于发送进程,一个属于接收进程)
    2.远程过程调用和远程方法调用

27. socket

socket用于不同进程或者跨主机跨网络进程之间的通信。
socket的意义:屏蔽不同协议与不同数据传输类型的组合类型差异,全部抽象为文件来操作。

socket带来的问题:

字节序问题

字节序分为大端存储和小端存储,网络字节序一般为大端。
对于 0xA1A2
大端存储:0xA1 0xA2,符合人的阅读习惯,低位地址存高位字节
小端存储:0xA2 0xA1 低位地址存低位字节
注意字节序指的是字节的顺序,所以顺序对调的最小单位是字节(而不是bit)

解决办法,通过函数实现本地字节序(host)以及网络字节序(net)的转换。
如:
ntohs , ntohl
htons , htonl
其中ntohs指的是net to host short,其他缩写含义类似。

对齐问题

为了提高寻址效率,对于一个结构体(对象),其大小并非简单是所有对象所占字节数的总和,而是会进行对齐(比如算下来13字节的结构体会对齐为4的倍数,16字节)
对齐也不是简单的按倍数对齐,跟结构体对象声明顺序有关。

比如,若int占用4字节,char占用1字节:
{
int a;
int b;
char c;
char d;
}
上述对象总占用42+12 = 10,对齐为12字节。

如果换一下顺序:
{
int a;
char c;
int b;
char d;
}
会变成4+4+4+2=14字节,对齐为16字节。

对其规则必定满足:结构体的总大小是结构体最大成员体的整数倍,此外,对齐是按照(地址%sizeof(type))是否为0来判断的,具体扩展内容请自行搜索。

在socket中解决对齐问题的思路就是取消对齐

类型长度问题

int、short之类的基本类型的长度实际是未定义的,在不同的机器上会表现出不同的长度,因此最好的办法是指定长度,使用int32_t, uint32_t, int64_t 之类的来显示定义。

在这里插入图片描述

__attributs__((packed))表示取消对齐。
name其实是一个占位符,用于构建变长结构体,因为我们不能预估名字的长度,而通常使用char*指针表示字符串,但显然不可能传递一个地址到网络上去。
这样之后,
在发送端,操作msg_st实例的时候,初始化为指针,指针指向的空间大小为malloc(sizeof(msg_st))+strlen(name)。
在接收端,接收的msg_st大小直接定义为malloc(MAX),其中MAX为可能的最大值,比如udp建议的包大小为512,那么MAX=512-udp头部 = 512-8

socket函数

int socket(int domain, int type, int protocol);

domain:下层协议族
type:上层数据传输类型
protocol:协议(0表示使用协议族中的默认协议)

socket大致分为两类,流式套接字(如tcp)和报式套接字(如udp),由于流式面向连接,即点对点通信,因此如果要做广播、多播/组播,只能用报式套接字。
(广播和多播/组播的区别在于,广播是全网发送,所有人必须接收,多播/组播则是自己拉个群,发消息就群里大家自己看的到,但有个特殊的群224.0.0.1默认所有人都在这里面,如果往这里发消息也是广播。)

题外话:所有数据最好在声明的时候都进行初始化,即便看起来不必要。考虑这种情况:一个指针开辟的大小是16字节,这时候没有初始化,它其实指向的是内存中的一块空闲地址,是有内容的,如果不对其进行初始化(通过memset),如果后面给他赋值的时候只用了12个字节,那么剩余的4个字节依旧是脏数据,尽管我们感知不到,但在网络上传输的时候,可以通过抓包看见。这也算内存泄露。

socket编程流程

在这里插入图片描述

客户端不bind的话,系统会自动分配一个端口。

相对复杂且重要的一个函数:accept。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

  • int sockfd:创建的socket文件描述符
  • struct sockaddr *addr:传出参数,最终记录对端的addr信息
  • socklen_t *addrlen:传入的是sizeof(sockaddr),传出的是对端实际大小
  • 返回值:与对端进行数据通信的一个socket文件描述符,该文件用于与客户端进行数据通信。

需要注意的是,server端至少有2个socket文件描述符,其中一个socket文件描述符(由socket函数创建得到的)专门用于接收请求,其他的socket文件描述符(由accept得到的)用于与客户端通信。

client端只有一个socket文件描述符,就是socket函数创建的,用于跟server端通信。

28. udp丢包

TTL:time to life,并不是一个时间单位,而是指可以跳转的路由个数。
一般linux默认64,windows默认128,所以TTL通常来说是足够用的,不会因为TTL耗尽导致丢包。

丢包一般是因为阻塞导致的。

阻塞往往是因为包太多了,所以要进行流控。

通过确认机制、滑动窗口进行流控。
单独的确认机制会导致大量的时间耗费在等待ack上,通过滑动窗口+累计确认+拥塞控制,能够降低这个等待时间。

29. 第三方包的使用

在Java中,可以使用maven之类的构建工具,通过import关键字就可以实现第三方包的使用,但是对C/C++来说,需要自己下载编译源码包,形成静态/动态库,然后编译的时候使用。

ubuntu环境 ,以libevent为例

下载
官网https://libevent.org/下载tar.gz,上传到服务器
解压 tar zxvf xxxx.tar.gz
进入目录 cd xxx

安装
源码包安装三步走:
1.检查环境,生成makefile ./configure
2.编译 make 生成 .o 和可执行文件
3.[sudo] make install 将必要资源拷贝到系统指定目录(/usr/local/lib)

通常解压后的目录里面也有readme或者README,可以做参考。

验证安装
通常源码包中都会有样例,比如libevent目录下有个sample目录,可以尝试执行sample中的样例查看是否安装成功。

以编译执行hello-world.c为例

gcc hello-world.c -o hello -levent

注意-levent,表示连接libevent库(去掉lib,加上l)

编译完毕之后,执行./hello

如果报错 cannot open shared object file: No such file or directory ,应该是环境变量没有配置。
临时环境变量配置:
执行语句:export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH


当前用户环境配置:
vim ~/.bashrc
在末尾添加:
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
保存退出即可

执行hello阻塞,表示成功。

30. 函数指针与回调机制

在Java中,回调的实现一般是通过传递接口参数,然后调用接口的方法实现方法回调。

在C/C++中,由于函数指针的存在,可以将函数作为参数传递,这就实现了比较特别的回调机制。

函数指针的格式:返回值(* 函数指针名称)(函数参数)

例子:

#include <stdio.h>

void callbackA(int a){
    printf("callbackA :%d\n",a);
}

void test(void(*callback)(int),int arg){
    callback(arg);
}

int main(){
    test(callbackA,10);
    return 0;
}

31. 手写HTTP服务器(C)

待续

99. 思维上的补充

  • 在C中,会经常将返回值放在参数列表上,而返回值只返回一些成功与否的状态信息。个人理解这么做的目的是,将对象的创建与回收都交给用户(假设由方法自己返回,那么创建必定在方法内,但是回收却需要用户在外部考虑)

  • Java有很多自动回收机制,在C中,尽量保证“谁创建谁回收,谁开启谁关闭”

  • 写任意小模块的时候,都要有宏观思维,任何现场(状态)的改变,都要保存其原有现场(状态),结束操作后恢复其原有现场(状态),避免影响到其他模块。

  • 全缓冲与半缓冲同时出现,或者出现线程\进程切换的时候,需要刷新缓冲

  • 多从宏观上考虑问题,如果没有宏观上的把控,在细节上也无法做到尽善尽美,考虑周全。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 转 C++ 知识点 的相关文章

  • 在 C# 中创建具有单独列的分隔文本

    我一直在尝试在 C 中创建一个制表符限制的文本文件 以便数据正确显示在单独的列中 Firstname Lastname Age John Smith 17 James Sawyer 31 我尝试过 t 字符 但我得到的只是 Firstnam
  • 使用 Unity 在构造函数中使用属性依赖注入

    好的 我在基类中定义了一个依赖属性 我尝试在其派生类的构造函数内部使用它 但这不起作用 该属性显示为 null Unity 在使用 container Resolve 解析实例后解析依赖属性 我的另一种选择是将 IUnityContaine
  • 向 Nhibernate 发出 SQL 查询

    如何将此 SQL 查询发送给 Nhibernate SELECT Customer name FROM Company INNER JOIN Customer ON Company CompanyId Customer CompanyId
  • 在新的浏览器进程中打开 URL

    我需要在新的浏览器进程中打开 URL 当浏览器进程退出时我需要收到通知 我当前使用的代码如下 Process browser new Process browser EnableRaisingEvents true browser Star
  • XamlReader.Load 在后台线程中。是否可以?

    WPF 应用程序具有从单独的文件加载用户控件的操作 使用XamlReader Load method StreamReader mysr new StreamReader pathToFile DependencyObject rootOb
  • 读取文件特定行号的有效方法。 (奖励:Python 手册印刷错误)

    我有一个 100 GB 的文本文件 它是来自数据库的 BCP 转储 当我尝试导入它时BULK INSERT 我在第 219506324 行上收到一个神秘错误 在解决此问题之前 我想看看这一行 但可惜的是我最喜欢的方法 import line
  • 如何访问另一个窗体上的ListView控件

    当单击与 ListView 所在表单不同的表单中的按钮时 我试图填充 ListView 我在 Form1 中创建了一个方法以在 Form2 中使用 并将参数传递给 Form1 中的方法 然后填充 ListView 当我调试时 我得到了传递的
  • 获取 WPF 控件的所有附加事件处理程序

    我正在开发一个应用程序 在其中动态分配按钮的事件 现在的问题是 我希望获取按钮单击事件的所有事件 因为我希望删除以前的处理程序 我尝试将事件处理程序设置为 null 如下所示 Button Click null 但是我收到了一个无法分配 n
  • 批量更新 SQL Server C#

    我有一个 270k 行的数据库 带有主键mid和一个名为value 我有一个包含中值和值的文本文件 现在我想更新表格 以便将每个值分配给正确的中间值 我当前的方法是从 C 读取文本文件 并为我读取的每一行更新表中的一行 必须有更快的方法来做
  • 上下文敏感与歧义

    我对上下文敏感性和歧义如何相互影响感到困惑 我认为正确的是 歧义 歧义语法会导致使用左推导或右推导构建多个解析树 所有可能的语法都是二义性的语言是二义性语言 例如 C 是一种不明确的语言 因为 x y 总是可以表示两个不同的事物 如下所述
  • 如何使用 Mongodb C# 驱动程序连接多个集合

    我需要将 3 个集合与多个集合合并在一起 lookup我在 C 驱动程序中尝试过 它允许我 lookup用户采集但无法执行秒 lookup用于设置集合 有人可以帮忙吗 db Transactions aggregate lookup fro
  • 如何编写一个同时需要请求和响应Dtos的ServiceStack插件

    我需要提供本地化数据服务 所有本地化的响应 Dto 都共享相同的属性 IE 我定义了一个接口 ILocalizedDto 来标记那些 Dto 在请求端 有一个ILocalizedRequest对于需要本地化的请求 Using IPlugin
  • HttpWebRequest 在第二次调用时超时

    为什么以下代码在第二次 及后续 运行时超时 代码挂在 using Stream objStream request GetResponse GetResponseStream 然后引发 WebException 表示请求已超时 我已经尝试过
  • gcc 的配置选项如何确定默认枚举大小(短或非短)?

    我尝试了一些 gcc 编译器来查看默认枚举大小是否很短 至少一个字节 强制使用 fshort enums 或无短 至少 4 个字节 强制使用 fno short enums user host echo Static assert 4 si
  • 编译时“strlen()”有效吗?

    有时需要将字符串的长度与常量进行比较 例如 if line length gt 2 Do something 但我试图避免在代码中使用 魔法 常量 通常我使用这样的代码 if line length gt strlen Do somethi
  • memset 未填充数组

    u32 iterations 5 u32 ecx u32 malloc sizeof u32 iterations memset ecx 0xBAADF00D sizeof u32 iterations printf 8X n ecx 0
  • 如何将 Roslyn 语义模型返回的类型符号名称与 Mono.Cecil 返回的类型符号名称相匹配?

    我有以下代码 var paramDeclType m semanticModel GetTypeInfo paramDecl Type Type Where paramDeclType ToString returns System Col
  • 防止在工厂方法之外实例化对象

    假设我有一个带有工厂方法的类 class A public static A newA Some code logging return new A 是否可以使用 a 来阻止此类对象的实例化new 那么工厂方法是创建对象实例的唯一方法吗 当
  • 如何使用 Word Automation 获取页面范围

    如何使用办公自动化找到 Microsoft Word 中第 n 页的范围 似乎没有 getPageRange n 函数 并且不清楚它们是如何划分的 这就是您从 VBA 执行此操作的方法 转换为 Matlab COM 调用应该相当简单 Pub
  • 在客户端系统中安装后桌面应用程序无法打开

    我目前正在使用 Visual Studio 2017 和 4 6 1 net 框架 我为桌面应用程序创建了安装文件 安装程序在我的系统中完美安装并运行 问题是安装程序在其他计算机上成功安装 但应用程序无法打开 edit 在客户端系统中下载了

随机推荐

  • 波场链通过Tron JS SDK TronWeb发送带备注的TRC - 20 转账及使用简介

    波场链通过tronWeb发送带备注的TRC 20 转账 var contractAddress TRC 20 合约 选择合约 法 let functionSelector transfer address uint256 根据 法构造参数
  • 应对程序员面试,你必须知道的八大数据结构

    大数据文摘出品 编译 Hope 睡不着的iris 胡笳 云舟 瑞士计算机科学家Niklaus Wirth在1976年写了一本书 名为 算法 数据结构 编程 40多年后 这个等式仍被奉为真理 这就是为什么在面试过程中 需要考察软件工程师对数据
  • java在大量增强for循环中找到某个特定对象去分析的方法-推荐使用debug工具(idea为例)

    场景 代码中有一部分增强for循环 里面是很复杂的处理逻辑 并且处理的AObjectList列表数量非常大 在这串代码中想看一下某个name为 小白 的对象的处理过程 如果断点直接打在for循环体内 可能要重复百次甚至千次以上才能找到这个对
  • 毕业设计-基于深度学习的目标检测算法

    目录 前言 课题背景和意义 实现技术思路 一 两阶段深度学习算法 二 单阶段深度学习方法 实现效果图样例 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个
  • 华为OD机试 - 最大平分数组( Python)

    题目描述 给定一个数组nums 可以将元素分为若干个组 使得每组和相等 求出满足条件的所有分组中 最大的平分组个数 输入描述 第一行输入 m 接着输入m个数 表示此数组 数据范围 1 lt M lt 50 1 lt nums i lt 50
  • 使用C++的libcurl库实现HTTP的POST请求

    简介 libcurl库是由C语言编写的轻量级网络库 可以实现客户端的一些基本功能 本文使用libcurl库实现了HTTP的POST请求 代码 C 代码 使用POST方式完成对以下两个API的访问 localhost 8050 api dat
  • .NET框架介绍

    NET平台 如图所示最上层VB C C JScript等为编程语言 这些语言的基础是最底层的操作系统以及com组件和services服务程序 net语言的核心由 公共语言进行时 CLR 和基础类库 Base Class Library 两部
  • 【超分辨率】(EDSR)Enhanced Deep Residual Networks for Single Image Super-Resolution论文阅读笔记

    论文名称 Enhanced Deep Residual Networks for Single Image Super Resolution 论文下载地址 https arxiv org pdf 1707 02921 pdf 论文代码地址
  • 7.6、LSM6DSL_SENSOR_HUB模式

    7 6 LSM6DSL SENSOR HUB模式 文章目录 7 6 LSM6DSL SENSOR HUB模式 7 6 1 简介 7 6 2 LSM6DSL SENSOR HUB模式 7 6 3 sensor hub直连模式 7 6 4 测试
  • Flutter框架和原理剖析

    Flutter是Google推出并开源的跨平台开发框架 主打跨平台 高保真 高性能 开发者可以通过Dart语言开发Flutter应用 一套代码同时运行在ios和Android平台 不仅如此 flutter还支持web 桌面 嵌入应用的开发
  • 四象限运行模式_sem四象限分析法

    四象限分析法是SEM中一个常用方法 我们把账户内的关键词按照横坐标X 消费 纵坐标Y 转化 若无转化数据 替换为转化率 点击率均适用 按照以下四个象限进行分类 第一象限 高消费高转化 可能存在问题 账户设置待优化 主要 网站访问率 转化率低
  • 条件竞争漏洞Double Fetch

    前言 Double Fetch 双取 是一种条件竞争的漏洞 相关的论文发表在USENIX 论文链接 https www usenix org system files conference usenixsecurity17 sec17 wa
  • Qt基本控件及三大布局

    Qt基本模块 一 Qt的三大布局 QHBoxLayout 水平显示布局 所有在其上面摆放的控件只能水平排列下去 QVBoxLayout 垂直显示布局 所有在其上面摆放的控件只能垂直排列下去 QGridLayout 格子显示布局 可以按照表格
  • linux 权限字符串rwxr-xrw-所对应的八进制权限数字

    Linux下 查看某路径下用 ls l 或 ll 查看所有文件的详细属性列表时 会看到文件的操作权限 类似 drwxr xr x 的字符串 d 第一位表示文件类型 d 文件夹 普通文件 l 链接 b 块设备文件 p 管道文件 c 字符设备文
  • (三)最基础的分类算法-k邻近算法 KNN

    笔记 机器学习入门专栏笔记对应jupyternotebook以及封装的各种算法个人笔记 如有错误 感谢指出 机器学习文档类资源 CSDN文库 三 最基础的分类算法 k邻近算法 KNN 概括 根据经验取k值 找与k值最接近的k个点 进而判断预
  • 某网站前端页面性能测试

    1页面性能测试概述 页面性能测试则是针对于页面性能优化而开展的一种性能测试 目的是对Web系统的页面进行测试以确认系统页面是否会影响系统的性能并为页面的优化提供依据与建议 最终提升系统的整体性能表现 提高用户体验满意度 他的关注点是在与页面
  • Centos7/8 安装任意版本 python3 和 pip3

    一 准备工作 1 新环境更新系统 如果是新环境最好要先更新以下系统 yum update yum upgrade y 2 Centos8无法使用yum命令 如果是Centos8提示错误 repo appstream 下载元数据失败 Cann
  • sql 语句 将查询结果中数字等标示转成汉字

    使用case 语句 将sex 字段进行转换 0 1 2 男 女 未知 SELECT u userid u username CASE u sex WHEN 0 THEN 女 WHEN 1 THEN 男 ELSE null END 性别 FR
  • Spring : 缓存相关注解@EnableCaching、@CacheConfig、@Cacheable、@Caching

    1 美图 2 概述 缓存相关注解 解释 EnableCaching 开启缓存注解的支持 CacheConfig 用于统一制定一些配置参数 这样在其他缓存注解里面就不用重复指定 Cacheable 如果之前已经有缓存数据值直接返回缓存数据 否
  • Java 转 C++ 知识点

    目录 配置Clion同步远程环境 0 内存模型 1 变量的作用域 2 typedef与define的区别 3 类的继承范围 4 常量与常函数 5 传值 传引用 6 友元的意义 7 左移运算符重载 8 析构函数 9 带指针的类 10 转型 1