文章目录
linux c++学习笔记整理
环境准备
安装gcc编译环境
yum -y install gcc*
查看版本
gcc -v
yum -y install centos-release devtoolset-8-gcc*
安装库函数的帮助文档
yum -y install man-pages
帮助文档的使用
man 级别 命令或函数
1-用户命令 2-系统接口 3-库函数
--------------windows下vs code远程连接unbuntu环境---------
Visual Studio Code (VS Code) 连接到 Ubuntu 服务器的步骤:
1) Remote - SSH 插件:在 VS Code 的插件市场中搜索 "Remote - SSH" 并安装该插件。
2)打开远程窗口:在 VS Code 中按下 Ctrl + Shift + P(或者 Cmd + Shift + P 在 macOS 上),然后输入 "Remote-SSH: Connect to Host..." 并选择此选项。
3)点击 "Configure SSH Hosts",然后选择 "Add New SSH Host"。
配置连接信息:在弹出的输入框中输入您的 Ubuntu 服务器的连接信息,例如用户名和 IP 地址。按照提示输入密码或者选择公钥验证方式(如果您已经设置了 SSH 密钥)。
---- 问题:ssh连接Ubuntu报:设置ssh主机 正在初始化vs code服务器
在以管理员身份打开的 PowerShell 窗口中再次尝试运行以下命令来安装 OpenSSH 客户端功能:
命令:Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
检查文件权限:确保 C:/Users/hp/.ssh/config 文件的所有者和权限设置正确。通常情况下,该文件应该只有您的用户(hp)具有读写权限,其他用户应该没有权限访问该文件!!!
静态库与动态库
静态库与动态库理论
静态库
概念:
程序在编译时会把库文件的二进制代码链接到目标程序中,这种方式成为静态链接
特点:
链接在编译时期完成,执行的时候代码加载速度快
目标的可执行文件比较大,浪费空间
程序的更新和分布不方便,如果某一个静态库更新了,所有使用它的程序都需要重新编译
tree
静态库使用方法
// 编译静态库
g++ -c -o libpublic.a public.cpp
1、g++ -o demo01 demo01.cpp /home/wuca/tools/libpublic.a // 不推荐使用
2、g++ -o demo01 demo01.cpp -L/home/wuca/tools -lpublic // 推荐使用
// 示例:
g++ -o keep keep.cpp -L/home/pb/桌面/codekeep/ppp -lpublic
动态库
概念:
编译时不载入二进制代码,程序在运行时候才被载入
如果多个程序中用到了同一个动态库中的函数,那么在内存中只有一份,避免了空间浪费问题。
特点:
程序运行在运行的过程中,需要用到动态库的时候才把动态库的二进制代码载入内存
可以实现进程之间的代码共享,因此动态库也称为共享库
程序升级比较地点但,不需要重新编译程序,只需要更新动态库就行了。
// 编译动态库
g++ -fPIC -shared -o lib 库名.so 源代码文件清单
1、g++ 选项 源代码文件名清单 动态库文件名 // 不推荐使用
2、g++ 选项 源代码文件名清单 -L 库名 -l库文件所在的目录名 // 推荐使用
g++ -fPIC -shared -o libpublic.so public.cpp
g++ -o keep keep.cpp -L/home/pb/桌面/codekeep/ppp -lpublic
运行可执行程序的时候,需要提前设置LD_LIBRARY_PATH环境变量
echo $LD_LIBRARY_PATH // 查看环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/pb/桌面/codekeep/ppp
如果静态库和动态库同时存在,优先使用动态库
静态库与动态库示例代码
文件:public.h
#include<iostream>
void func();
文件:public.cpp
#include<iostream>
#include"/home/pb/桌面/codekeep/ppp/public.h"
// 声明 func 函数
void func(){
std::cout<<"上山打老虎,老虎打不到!"<<std::endl;
std::cout<<"code change world!"<<std::endl;
}
文件:keep.cpp
#include"public.h"
using namespace std;
int main(){
std::cout<<"test static lib"<<std::endl;
func();
std::cout<<"I'm a good man!"<<endl;
return 0;
}
make
makefile
–概念:make是一个强大的实用工具,用于管理项目的编译和链接。make需要一个编译规则文件mekefile,可现实自动化编译。
需要编译很多文件
简单的方法是放在sh文件里
添加可执行权限 或者 sh sh文件名
# 是说明文字 类型于c语言的//
all:libpublic.a libpublic.so # 指定编译的目标文件 静态库和动态库 如果需要多行书写 可以用\隔开
libpublic.a:public.h public.cpp # 表示编译目标文件libpublic.a需要文件public.h public.cpp
g++ -c -o libpublic.a public.cpp # 编译指令
libpublic.so:public.h public.cpp
g++ -fPIC -shared -o libpublic.so public.cpp
#clean 用于请忽略编译目标文件,仅在make clean才会执行。
clean:
rm -f libpublic.a libpublic.so
main函数的参数
main函数有三个参数,argc,argv和envp,它的标准写法如下:
int main(int argc,char* argv[], char* envp[]){
return 0;
}
argc 存放了程序参数的个数,包括程序本身
argv 字符串的数组,存放了每个参数的值,包括程序本身
envp 字符串的数组,存放了环境变量,数组的最后一个元素是空
在程序中,如果不关心main()的参数可以省略不写
cout<<"一共有"<<agrc<<"个参数";
argv从0开始输出文件传入参数的个数
setenv(const char *name, const char*value, int overwrite); // 设置环境变量
name 环境变量名 value 环境变量值 overwrite 0 无则建立,存在不替换 1 无则建立,存在替换
char *getenv(const char*name); // 获取环境变量的值
setenv("AA", "aaaa", 0);
cout<<"AA"<<getenv("AA");
gdb调试程序
yum -y install gdb // Ubuntu自己百度
前期准备:
g++ -g -o my_program my_program.cpp // 在终端中输入 gdb 命令启动GDB:
gdb ./my_program // 在终端中输入 gdb 命令启动GDB:
(gdb) break 10 // 在终端中输入 gdb 命令启动GDB:
(gdb) run // 在 GDB 中运行您的程序
在完成调试后,可以输入 quit 命令退出GDB。
一旦程序停在断点处,您可以逐步执行程序,观察程序的执行状态和变量的值。以下是一些常用的命令:
step 或 s:逐步进入函数
next 或 n:逐步执行不进入函数
continue 或 c:继续执行程序直到下一个断点
print 或 p:打印变量的值
backtrace 或 bt:显示当前的函数调用栈
set var 设置变量的值
info breakpoints 查看断点信息
delete breakpoints 取消断点
----gdb调试core文件
如果程序在运行的过程中发生了内存泄漏,会被内核强制终止,提示"段错误(吐核)",内存的状态将保存在core文件中,方便程序员进一步分析。
linux缺省不会生成core文件,需要修改系统参数
调试core文件的步骤如下:
1)用ulimit -a查看当前用户的资源限制参数;
2)用ulimit -c unlimited 把core file size改为unlimited
3)运行程序,产生core文件;不产生core文件 可以直接gdb调试也能看到栈溢出的地方
bt 函数调用栈
示例程序:
#include<iosteam>
#include<string.h>
using namespace std;
int main(){
char *p = null;
*p = 1;
return 0;
}
---- gdb调试正在运行中的程序
程序不动了,它正在干啥,明明有数据为什么没处理?
程序死了?怎么死的?死在哪里?
从头开始调试很麻烦,并且不一定能模拟出当时的运行环境
步骤 首先获取函数进程编号
ps -ef |grep demo
sudo gdb demo -p 进程号 // 此时程序停止下来 quit后,程序继续执行
使用bt 查看函数调用栈
set var 设置变量的值 点击下一步执行完
(gdb) set var i=1000000
示例代码:
#include<iostream>
#include<unistd.h>
using namespace std;
void printnum(){
for(int i = 0; i < 1000000; i++){
std::cout<<i<<endl;
sleep(1);
}
}
int main(){
printnum();
return 0;
}
linux的时间:
一、linux的时间操作
c++11提供了操作时间的chrono库,从语言级别提供了支持
chrono库屏蔽了时间操作的很多细节,简化了时间操作
如果对linux原生的时间操作不熟悉,很难用好chrono库
unix操作系统根据计算机产生的年代把1970年1月1日作为unix的纪元时间,将经过的秒数用一个整数存放
-、time_t 别名
time_t用于时间类型,它是一个long类型的别名,在<time.h>文件中定义,表示从1970年1月1日0时0分0秒到现在的秒数
typedef long time_t;
二、time()库函数
time()库函数用于获取操作系统的当前时间 头文件<time.h>
声明: time_t time(time_t *tloc);
有两种调用方法 效果一样:
1、time_t now=time(0);
2、time_t now; time(&now);
三、tm结构体
time_t是一个长整型,不符合人类的使用习惯,需要转换成tm结构体,tm结构体在<time.h>中,声明如下:
struct tm{
int tm_year; //年份 其值等于实际年份减去1990
int tm_mon; // 月份:取值区间为[0,11],其中0代表一月,11代表12月
int tm_mday; // 日期:一个月中的日期,取值范围为[0,31]
int tm_hour; // 时取值范围[0,23]
int tm_min; // 分:取值范围为[0,59]
int tm_sec; // 秒:取值范围为[0,59]
int tm_wday; // 星期:取值区间为[0.6],其中0代表星期天,6代表星期六
int tm_yday; // 从每年的1月1日开始算起的天数,取值区间为[0,365]
int tm_isdst; // 夏令时标识符,该字段意义不大
}
四、loacltime()函数
用于把time_t表示的时间转换为tm结构体表示的时间 不是线程安全,localtime_r()是线程安全的
函数声明:
struct tm *localtime(const time_t *timep)
struct tm *localtime_r(const time_t *timep, struct tm *result)
示例代码:
#include<iostream>
#include<unistd.h>
#include<time.h>
using namespace std;
void printnum(){
time_t now = time(0);
cout<<now<<endl;
tm tmnow;
localtime_r(&now, &tmnow); // 把整数的时间转换成tm结构体
string stime = to_string(tmnow.tm_year+1900)+"-"
+to_string(tmnow.tm_mon+1)+"-"
+to_string(tmnow.tm_mday)+" "
+to_string(tmnow.tm_hour)+":"
+to_string(tmnow.tm_min)+":"
+to_string(tmnow.tm_sec);
cout<<"stime="<<stime<<endl;
}
int main(){
printnum();
return 0;
}
五、mktime()库函数
mktime()函数的功能与loacltime()函数相反,用于把tm结构体时间转换为time_t时间
函数声明: time_t mktime(struct tm *tm);
```cpp
/*该函数主要用于时间的运算,例如:把2022-10-01 15:30:25加30分钟
思路:1)解析字符串格式的时间,转换成tm结构体;2)用mktime()函数把tm结构体转换成time_t时间;
3)把time_t时间加30*60秒;4)把time_t时间转换成tm结构体;5)把tm结构体抓换成字符串*/
#include <iostream>
#include <iomanip>
#include <sstream>
#include <ctime>
// 解析日期时间字符串
std::tm parseDateTime(const std::string& datetimeStr) {
std::tm tm = {
};
std::istringstream ss(datetimeStr);
ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
return tm;
}
// 增加分钟并返回新的日期时间字符串
std::string addMinutes(const std::string& datetimeStr, int minutesToAdd) {
std::tm tm = parseDateTime(datetimeStr);
// 增加分钟
std::time_t time = std::mktime(&tm);
time += minutesToAdd * 60;
tm = *std::localtime(&time);
// 格式化为字符串
std::ostringstream oss;
oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
return oss.str();
}
int main() {
std::string datetimeStr = "2022-10-01 15:30:25";
int minutesToAdd = 30;
std::string newDatetimeStr = addMinutes(datetimeStr, minutesToAdd);
std::cout << "Original datetime: " << datetimeStr << std::endl;
std::cout << "New datetime after adding 30 minutes: " << newDatetimeStr << std::endl;
return 0;
}
六、gettimeofday()库函数
用于获取从1970年1月1日到现在的秒和当前秒中已逝去的微秒数,可以用于程序发计时。
包含头文件:<sys/time.h>
函数声明:
int gettimeofday(struct timeval *tv, struct timezone *tz)
struct timeval{
time_t tv_sec; // seconds 1970-1-1 到现在的秒数
suseconds_t tv_usec; //microseconds 当前秒中,已逝去的微秒数
}
struct timezone{
// 在实际开发中,派不上用场
int tz_minuteswest; // minutes west of Greenwich
int tz_dsttime; // type 0f DST correction
}
七、程序睡眠
如果现需要把程序挂起一段时间,可以使用sleep()和usleep()两个库函数
包含头文件:<unistd.h>
函数声明:
unsigned int sleep(unsigned int seconds) // 形参为秒
int usleep(usecond_t usec) // 形参为微秒
linux目录操作
一、几个简单的目录操作函数
1) 获取当前工作目录
包含头文件:<unistd.h>
char *getcwd(char *buf, size_t size)
char *get_current_dir_name(void)
2) 切换工作目录
包含头文件:<unistd.h>
int chdir(const char *path); // 返回值:0-成功;其它-失败(目录不存在或没有权限)
3) 创建目录
包含头文件:<sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
pathname -目录名
mode -访问权限,如0755, 不要省略前置的0
返回值:0-成功;其它失败(上级目录不存在或没有权限)
4)删除目录
包含头文件:<unistd.h>
int rmdir(const char *path); // 返回值:0-成功;其它-失败(目录不存在或没有权限)
#include<iostream>
#include<unistd.h>
using namespace std;
int main(){
char path1[256]; // linux系统目录的最大长度是255
getcwd(path1, 256);
cout<<"path1="