C/C++常用方法

常用IDE调试方式

调试命令
Clion:F7进入函数,F8单步调试,F9跳转到下个断点(也可以在GDB界面使用gdb命令行调试)。
Qt:F10单步调试,F11进入函数体,F5跳转到下个断点。

gdb

gdb exe	#启动gdb
core-file core.1882	#指定core文件
bt					#查看crash调用栈

b UdpServer::start_l	#在函数打断点
b UdpServer.cpp:180		#在文件的多少行打断点
b 250	#在当前调试文件的第250行打断点,刚启动时默认在main函数所在文件
i b		#查看断点
r 1		#运行,1是参数,遇断点停留
l		#查看源码(list)
n   	#单步调试
s		#进入函数体
c		#跳转到下个断点
d 1 	#删除第一个断点
p		#打印变量
q		#退出gdb
finish	    #返回当前函数,打印返回值
winheight	#窗口化调试

C/C++开发常用github开源项目

C++流媒体服务器SRS
https://github.com/ossrs

C++流媒体服务器ZLM
https://github.com/ZLMediaKit/ZLMediaKit

C/C++/QT音视频客户端
https://github.com/metartc/metaRTC

SocketIO
https://github.com/socketio/socket.io-client-cpp

C++操作JSON库:jsoncpp和picojson
https://github.com/open-source-parsers/jsoncpp/tree/master/src/jsontestrunner
https://github.com/kazuho/picojson/tree/master

云风写的C语言协程库
https://github.com/cloudwu/coroutine

京东青龙
https://github.com/6dylan6/jdpro

C++连接redis数据库
https://github.com/redis/hiredis
https://github.com/sewenew/redis-plus-plus

C语言实现traceroute功能
https://github.com/bgylde/traceroute.git

C语言实现ping
https://github.com/coding-fans/linux-network-programming/blob/master/src/c/icmp/ping/ping.c

C语言实现netstat命令功能
https://github.com/LipiLee/netstat/blob/master/netstat.c

C++表示二进制,八进制,十进制和十六进制

十进制,必须以非零数字开头。
八进制,任何以 0 开头的数字(包括普通 0)。
二进制,需要前缀 0b 或 0B。
十六进制,需要前缀 0x 或 0X。
demo:

int aa = 1001;//十进制
printf("aa=%d\n",aa);
    
int bb = 0b1001;//二进制
printf("bb=%d\n",bb);

int cc = 040;//八进制
printf("cc=%d\n",cc);

int dd = 0x20;//十六进制
printf("dd=%d\n",dd);

打印

aa=1001
bb=9
cc=32
dd=32

两个结构体一起new

两个结构体分配的空间是连续的,可以共用一个起始地址指针pBuff 。

struct HeadDef{
    int      MsgType;
    int      TotalLen;
    uint8_t     pData[0];	//地址指针标识,不占用空间
};

struct MsgDef{
    char        name[64];
    int         age;
};

    void *pBuff = new char(sizeof(HeadDef) + sizeof(MsgDef));
    HeadDef *pMsg = (HeadDef*)pBuff;
    pMsg->MsgType = 102;
    pMsg->TotalLen = sizeof(HeadDef) + sizeof(MsgDef);

    MsgDef *pMsg_body = (MsgDef*)pMsg->pData;
    pMsg_body->age = 20;
    std::string strname = "chw";
    memcpy(pMsg_body->name,strname.c_str(),strname.size());

    printf("pMsg->MsgType=%d\n",pMsg->MsgType);
    printf("pMsg->TotalLen=%d\n",pMsg->TotalLen);
    printf("pMsg_body->age=%d\n",pMsg_body->age);
    printf("pMsg_body->name=%s\n",pMsg_body->name);

打印:

pMsg->MsgType=102
pMsg->TotalLen=76
pMsg_body->age=20
pMsg_body->name=chw

函数传指针和指针引用

//传指针
//传的指针是行参,pNum指针自己的地址和实参指针地址不同,但指针指向的地址相同,因此可以使用行参改变指向地址的内容
//在这里对行参pNum做赋值操作是不会改变实参的
void testformal(int *pNum)
{
    printf("行参,指针自己的地址:%p\n",&pNum);
    printf("行参,指针指向的地址:%p\n",pNum);
    pNum = nullptr;//不会改变实参
}

//传指针引用
//传的是指针的引用,pNum指针地址、指针指向的地址均和实参一致
//在这里,不但可以改变实参指向地址的值,也可以改变实参本身
void testquote(int *&pNum)
{
    printf("引用,指针自己的地址:%p\n",&pNum);
    printf("引用,指针指向的地址:%p\n",pNum);
    pNum = nullptr;//会改变实参
}

    int *p = new int;
    printf("实参:指针自己的地址:%p\n",&p);
    printf("实参:指针指向的地址:%p\n",p);
    testformal(p);
    printf("%s\n",p == nullptr ? "p == nullptr" : "p != nullptr");
    testquote(p);
    printf("%s\n",p == nullptr ? "p == nullptr" : "p != nullptr");

打印

实参:指针自己的地址:0x7ffd0fe22f30
实参:指针指向的地址:0x562c56ea1e00
行参,指针自己的地址:0x7ffd0fe22f08
行参,指针指向的地址:0x562c56ea1e00
p != nullptr
引用,指针自己的地址:0x7ffd0fe22f30
引用,指针指向的地址:0x562c56ea1e00
p == nullptr

1字节对齐

当数据用于网络发送时,需要进行字节对齐

#pragma pack(push, 1)
//struct{...}
#pragma pack(pop)

四字节转int

int byte2int(uint8_t *data)
{
    int value = (int)((data[3] & 0xFF)
        | ((data[2] & 0xFF) << 8)
        | ((data[1] & 0xFF) << 16)
        | ((data[0] & 0xFF) << 24));
    return value;
}

调用

    char arr[4];
    arr[0] = 1;
    arr[1] = 1;
    arr[2] = 1;
    arr[3] = 1;

    int num = byte2int((uint8_t *)arr);
    printf("num=%d\n",num);

打印

num=16843009

C++十六进制字符串转换为十进制

    char *str = (char*)"0xff";//不区分大小写,0x可以不写
    int n = strtol(str, NULL, 16);
    printf("%d\n", n);

打印输出:255

模拟main函数传参

void parse_param(int argc, char **argv)
{
    for(int index=0;index<argc;index++)
    {
        printf("the %d param is [%s]\n",index,argv[index]);
    }
}

int argc = 2;
char *argv[2];
argv[0] = (char*)"chw";
argv[1] = (char*)"192.168.1.108";

parse_param(argc, argv);

打印:

the 0 param is [chw]
the 1 param is [192.168.1.108]

模拟main函数传参2

#include <unistd.h>
    int argc = 3;
    char *argv[3];
    argv[0] = (char*)"testPro";
    argv[1] = (char*)"-b";
    argv[2] = (char*)"chw";

    int opt;
    char *c;
    while ((opt = getopt(argc, argv, "b:p")) != EOF)
    {
        switch (opt)
        {
        case 'b':
            c = strdup(optarg);
            printf("b=%s\n",c);
            free(c);
            break;
        }
    }

打印

b=chw

va_list可变参数列表

va_list:一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
va_start:对ap进行初始化,让ap指向可变参数表里面的第一个参数。第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“…”之前的那个参数;
va_arg: 获取参数。它的第一个参数是ap,第二个参数是要获取的参数的指定类型。按照指定类型获取当前参数,返回这个指定类型的值,然后把 ap 的位置指向变参表中下一个变量的位置;
va_end:释放指针,将输入的参数 ap 置为 NULL。通常va_start和va_end是成对出现。

void test_printf1(char *headData, char *format,...)
{
    char buff[4096];
    va_list args;

    memcpy(buff, headData, strlen(headData));
    va_start(args, format);
    //将格式化数据从可变参数列表写入缓冲区
    vsnprintf(buff+strlen(headData), 1024, format, args);
    va_end(args);

    printf("%s\n", buff);
}

void test_printf2(int num,...)
{
    va_list args;
    va_start(args,num);
    for(int i = 0; i < num; i++)
        printf("%d\n", va_arg(args, int));
    va_end(args);
}

    test_printf1((char *)"data", (char *)"len is %d, data is %s", 10, "helloworld");
    test_printf2(3,100,200,300);

打印

datalen is 10, data is helloworld
100
200
300

fork()多进程

fork是复制进程的函数,通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,也可以根据传入参数的不同,做不同的事。

pid_t  fork(void);

函数返回值pid_t, 就是int型。

在父进程中,返回新建子进程的进程ID;
在子进程中,返回0;
创建失败,返回负值。

多进程特点
1、fork 子进程后,一般情况是子进程先调度。
2、子进程和父进程共享代码段,堆栈数据和全局数据是独立的,进程空间的各段采用了写时复制技术。
3、父子进程之间可通过信号通信,比如退出时会发信号。

测试代码,循环创建10个子进程。

#include <unistd.h>
#include <stdio.h>
int main()
{
    int pid = 0;
    int cnt = 10;
    do
    {
        pid = fork();
        if(pid < 0)
        {
            continue;
        }

        if(pid == 0)
        {
            break;
        }

        if(pid > 0)
        {
            cnt --;
        }
    }
    while(cnt > 0);
    printf("pid=%d,rpid=%d\n",pid,getpid());

    while(1){}
}

打印

pid=0,rpid=3622
pid=3628,rpid=3618
pid=0,rpid=3628
pid=0,rpid=3623
pid=0,rpid=3619
pid=0,rpid=3627
pid=0,rpid=3624
pid=0,rpid=3625
pid=0,rpid=3621
pid=0,rpid=3626
pid=0,rpid=3620

指针类型占多少字节

内存是由字节组成的,每个字节都有一个地址编号。
指针变量主要是存放相同数据类型的变量的首地址,这里的地址就是指内存中某个字节的编号,而这个编号的确定是由地址总线决定的,操作系统的位数决定了指针变量所占的字节数。
如果是32位操作系统,也就是地址总线是32位,则它的寻址范围就是02^32(4GB,所以32位系统最多配4G内存,多了也没有用),每一个字节的编址就会由32个0或者1组成。
例:第1个字节的编址是32个0,最后1个的编址是32个1。一个字节有8位,32位则需要4个字节。
64 位 CPU 寻址范围则很大,理论最大的寻址空间为 2^64。

不管是什么数据类型:
1、在32位操作系统下,指针是占4个字节空间大小。
2、在64位操作系统下,指针是占8个字节空间大小。

指针一维数组和二维数组的分配与释放

以int型指针为例。

    //1.int型指针
    int *p=new int;
    *p = 12;
    printf("*p=%d\n",*p);
    //释放
    delete p;
    p = nullptr;

    //2.int型一维数组
    int iArrNum = 5;
    int *pArr=new int [iArrNum];//只动态分配空间,未初始化
    memset(pArr,0,iArrNum*sizeof(int));
    for(int index=0;index<iArrNum;index++)
        printf("pArr[%d]=%d\n",index,pArr[index]);
    //释放
    delete []pArr;
    pArr = nullptr;

    //3.int型二维数组
    int row = 3;//行
    int col = 4;//列
    //分配一个指针数组,首地址保存在pInt
    int  **pInt=new int* [row];
    //为指针数组的每个元素分配一个数组
    for(int i=0;i<row;i++)
    {
        pInt[i]=new int [col];
    }
    //遍历
    int n = 0;
    for(int i=0;i<row;i++)
        for(int j=0;j<col;j++)
            pInt[i][j] = n++;
    for(int i=0;i<row;i++)
        for(int j=0;j<col;j++)
             printf("pInt[%d][%d]=%d\n",i,j,pInt[i][j]);
    //释放
    for(int i=0;i<row;i++)
    {
        delete []pInt[i];
    }
    delete []pInt;

打印

*p=12
pArr[0]=0
pArr[1]=0
pArr[2]=0
pArr[3]=0
pArr[4]=0
pInt[0][0]=0
pInt[0][1]=1
pInt[0][2]=2
pInt[0][3]=3
pInt[1][0]=4
pInt[1][1]=5
pInt[1][2]=6
pInt[1][3]=7
pInt[2][0]=8
pInt[2][1]=9
pInt[2][2]=10
pInt[2][3]=11

负数的二进制表示法

正数的表示法,先转换成二进制,在前面补0。
例如5的表示法:
00000000 00000000 00000000 00000101

在计算机中,负数以原码的补码形式表达
1、原码
用符号位和数值表示带符号数,正数的符号位用“0”表示,负数的符号位用“1”表示,数值部分用二进制形式表示。
例如

00000000 00000000 00000000 00000101 是 5的原码。
10000000 00000000 00000000 00000101 是-5的原码。

2、反码
正数的反码与原码相同,负数的反码为对该数的原码除符号位外各位取反。
例如

正数00000000 00000000 00000000 00000101 的反码还是 00000000 00000000 00000000 00000101
负数10000000 00000000 00000000 00000101 的反码则是 11111111 11111111 11111111 11111010。

3、补码
正数的补码与原码相同,负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1。
例如

10000000 00000000 00000000 00000101 的反码是:11111111 11111111 11111111 11111010
补码为:
11111111 11111111 11111111 11111010 + 1 = 11111111 11111111 11111111 11111011
-5 在计算机中表达为:11111111 11111111 11111111 11111011。转换为十六进制:0xFFFFFFFB。

举例,int类型-1在计算机中表示法:

-1的原码:10000000 00000000 00000000 00000001
得反码: 11111111 11111111 11111111 11111110(除符号位按位取反)
得补码: 11111111 11111111 11111111 11111111

C++位运算

位是数据存储的最小单位,简记为b,也称为比特,每个0或1就是一个位(bit)。
位运算操作符:& (按位与)、| (按位或)、^ (按位异或)、~ (按位取反)、>> (按位右移)、<< (按位左移)。

&(按位与)
如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。

| (按位或)
如果两个相应的二进制位只要有一个是1,结果就是1;否则为0。

~(按位取反)
每一位进行取反运算,1变成0,0变成1。

^(按位异或)
两个相同的数会变成0,反之是1

>>(按位右移)
把二进制位整体向右移动,右移等于除以2的N次方,N为右移的位数。

<<(按位左移)
把二进制位整体向左移动,左移等于乘以2的N次方,N为左移的位数

demo

    int aa = 0b1001;//9
    int bb = 0b1110;//14

    int yu    = aa & bb;//1000,8
    int huo   = aa | bb;//1111,15
    int fei   = ~aa;    //-10
    int yihuo = aa ^ bb;//111,7
    int youyi = aa >> 1;//100,4
    int zuoyi = aa << 1;//10010,18

    printf("aa=%d\n",aa);
    printf("bb=%d\n",bb);
    printf("yu=%d\n",yu);
    printf("huo=%d\n",huo);
    printf("fei=%d\n",fei);
    printf("yihuo=%d\n",yihuo);
    printf("youyi=%d\n",youyi);
    printf("zuoyi=%d\n",zuoyi);

打印

aa=9
bb=14
yu=8
huo=15
fei=-10
yihuo=7
youyi=4
zuoyi=18

打印当前系统时间

linux系统

#include <sys/time.h>
/* 获取时间,1970年1月1日到现在的时间 */
struct timeval time;
gettimeofday(&time, NULL);
printf("s: %ld, ms: %ld\n", time.tv_sec, (time.tv_sec*1000 + time.tv_usec/1000));

windows系统

#include <windows.h>
SYSTEMTIME timenow;
GetLocalTime(&timenow);   printf("hour=%u,minute=%u,second=%u,Milliseconds=%d\n",timenow.wHour,timenow.wMinute,timenow.wSecond,timenow.wMilliseconds);

睡眠sleep

linux

#include <iostream>
#include <sys/time.h>
#include <unistd.h>
int sleep_time = 5;
cout << "sleep before time: " << time(NULL) << endl;
sleep(sleep_time);//睡眠单位,秒
cout << "sleep after time:  " << time(NULL) << endl;

sleep_time = 10*1000;
cout << "======================" << endl;
struct timeval tv;
gettimeofday(&tv,NULL);
cout << "usleep before time: " << tv.tv_sec<<"s,"<<tv.tv_usec<<" 微秒"<<endl;
usleep(sleep_time);//睡眠单位,微秒
gettimeofday(&tv,NULL);
cout << "usleep after time:  " << tv.tv_sec<<"s,"<<tv.tv_usec<<" 微秒"<<endl;

打印

sleep before time: 1675737566
sleep after time:  1675737571
======================
usleep before time: 1675737571s,340562 微秒
usleep after time:  1675737571s,351354 微秒

windows

#include <iostream>
#include <windows.h>
SYSTEMTIME timenow;
GetLocalTime(&timenow);
printf("hour=%u,minute=%u,second=%u,Milliseconds=%d\n",timenow.wHour,timenow.wMinute,timenow.wSecond,timenow.wMilliseconds);
Sleep(10);//单位毫秒
GetLocalTime(&timenow);
printf("hour=%u,minute=%u,second=%u,Milliseconds=%d\n",timenow.wHour,timenow.wMinute,timenow.wSecond,timenow.wMilliseconds);

打印

hour=10,minute=32,second=23,Milliseconds=515
hour=10,minute=32,second=23,Milliseconds=526

C++生成随机数

方法1:rand()
使用srand()设置种子,rand()生成随机数,如果种子一样则会生成相同的随机数;如果不设置种子则默认初始种子为1,生成的随机数做为新种子用于下次生成随机数。rand()生成的是伪随机数。

struct timeval time;
gettimeofday(&time, NULL);
unsigned long ms = (time.tv_sec*1000 + time.tv_usec/1000);

srand(ms);//设置种子
int random = rand();
printf("random=%d\n",random);
usleep(100*10);

方法2:random_device
random_device 是标准库提供到一个非确定性随机数生成器,使用硬件作为随机数来源,故其调用代价较高,一般用来产生随机数种子。
random_device提供()操作符,用来返回一个min()到max()之间的一个高质量随机数,可以理解为真随机数。

std::random_device rd;//做为随机数种子

//下面几种是标准库提供的伪随机数生成器(种子相同则随机数相同),使用random_device生成的真随机数做为种子是常用方法。
///1.mt19937 是标准库提供的采用梅森旋转算法的伪随机数生成器,可以快速产生高质量到随机数。
std::mt19937 mt(rd());
for(int n = 0; n < 10; n++)
    std::cout << mt() << std::endl;

///2.default_random_engine 是标准库提供的默认随机数生成器,其实现和编译器有关。
std::default_random_engine r_eng(rd());
for (int i = 0; i < 10; ++i)
  std::cout << r_eng() << std::endl;

///3.minstd_rand 是标准库提供的采用线性同余算法的伪随机数生成器。
std::minstd_rand   r_minstd_rand(rd());
for (int i = 0; i < 10; ++i)
  std::cout << r_minstd_rand() << std::endl;

///4.ranlux24_base 是标准库提供的采用带进位减法的伪随机数生成器。
std::ranlux24_base  r_ranlux24_base(rd());
for (int i = 0; i < 10; ++i)
  std::cout << r_ranlux24_base() << std::endl;

C++取模均匀分配下标

    int _Index = -1;
    int _taskpoll = 6;
    for(int index=0;index<10;index++)
    {
        _Index++;
        _Index %= _taskpoll;
        printf("_Index=%d\n",_Index);
    }

打印

_Index=0
_Index=1
_Index=2
_Index=3
_Index=4
_Index=5
_Index=0
_Index=1
_Index=2
_Index=3

fprintf()中的stdout、stderr

#include<stdio.h>
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);

printf将格式化写到标准输出,fprintf写至指定的流,默认输出到屏幕。

#include <stdio.h>
setbuf(stdout,nullptr);//设置无缓存区,直接打印
setbuf(stderr,nullptr);
int day = 27;
fprintf(stdout, "Hello:%d \n",day);
const char *msg = "error hapd";
fprintf(stderr, "World!:%s\n",msg);

打印

Hello:27 
World!:error hapd

可以把流重定向到指定文件,下面运行可执行文件,输出流重定向到tmp.txt文件。

./fpint > tmp.txt

打印进程ID和线程ID

linux系统

printf("mainwindow thread tid = %lu\n", pthread_self());
#include<unistd.h>
printf("mainwindow,pid:%u, tid:%u\n",(unsigned int)getpid(), (unsigned int)gettid());
#include <thread>
printf("mainwindow,ThreadID: %lu\n", std::this_thread::get_id());

windows系统

#include <thread>
printf("mainwindow,ThreadID: %lu\n", std::this_thread::get_id());

char*与string相互转换

#include<iostream>//cout需要的头文件
using namespace std;
//char*转string
char ary[1024]="abcdefg";
string str=ary;
cout<<"str="<<str<<endl;//str=abcdefg

//string转char*
char ary2[1024];
strcpy(ary2,str.c_str());//建议用strcpy_s
printf("ary2=%s\n",ary2);//ary2=abcdefg

字符串和数字相互转换

//int转string,其他类型的整型和浮点型均可使用
int i = 42;
double d = 42.7;
string str = to_string(i);
string str2 = to_string(d);
printf("str=%s,str2=%s\n",str.c_str(),str2.c_str());//str=42,str2=42.700000

//string转int,也可转为其他类型的整型和浮点型
int ii = stoi(str);                     //int
long ll = stol(str);                    //long
unsigned long ull = stoul(str);         //unsigned long
long long lll = stoll(str);             //long long
unsigned long long ulll = stoull(str);  //unsigned long long
float ff = stof(str);                   //float
double dd = stod(str);                  //double
long double ldd = stold(str);           //long double

获取可执行文件绝对路径

#include <unistd.h>//linux(全路径,不包含可执行文件名)
#include <direct.h>//win(包含可执行文件名的全路径)
#define MAXPATH 1000
char buffer[MAXPATH];
getcwd(buffer,MAXPATH);
printf("The current directoryis:%s\n",buffer);

int、short、long、long long、 unsigned int、unsigned short、unsigned long、unsigned long long字节数,取值范围,打印方式

//int型大小4字节,数值范围:-2^(32-1) – 2^(32-1)-1(即 -2147483648 ~ 2147483647)
int _int_max = 2147483647;
int _int_min = -2147483648 ;
printf("_int_max=%d,_int_max+1=%d\n",_int_max,_int_max+1);//_int_max=2147483647,_int_max+1=-2147483648
printf("_int_min=%d,_int_min-1=%d\n",_int_min,_int_min-1);//_int_min=-2147483648,_int_min-1=2147483647

//short型大小2字节,数值范围:-2^(16-1) – 2^(16-1) -1 (即 -32768 ~ 32767)
short _short_max = 32767;
short _short_min = -32768;
printf("_short_max=%hd,_short_max+1=%hd\n",_short_max,_short_max+1);//_short_max=32767,_short_max+1=-32768
 printf("_short_min=%hd,_short_min-1=%hd\n",_short_min,_short_min-1);//_short_min=-32768,_short_min-1=32767

//long型大小4字节,数值范围:-2^(32-1) – 2^(32-1)-1(即 -2147483648 ~ 2147483647)
long _long_max = 2147483647;
long _long_min = -2147483648 ;
printf("_long_max=%ld,_long_max+1=%ld\n",_long_max,_long_max+1);//_long_max=2147483647,_long_max+1=-2147483648
printf("_long_min=%ld,_long_min-1=%ld\n",_long_min,_long_min-1);//_long_min=-2147483648,_long_min-1=2147483647

//long long型大小8字节,数值范围:-2^(64-1) ~ 2^(64-1)-1(即 -9223372036854775808 ~ 9223372036854775807
long long _long_long_max = 9223372036854775807;
long long _long_long_min = -9223372036854775808;
//_long_long_max=9223372036854775807,_long_long_max+1=-9223372036854775808
//_long_long_min=-9223372036854775808,_long_long_min-1=9223372036854775807
printf("_long_long_max=%lld,_long_long_max+1=%lld\n",_long_long_max,_long_long_max+1);
printf("_long_long_min=%lld,_long_long_min-1=%lld\n",_long_long_min,_long_long_min-1);


//unsigned int型大小4字节,数值范围:0 – 2^(32)-1 (即 0~4294967295)
unsigned int _unsigned_int_max = 4294967295;
unsigned int _unsigned_int_min = 0 ;
printf("_unsigned int_max=%u,_unsigned int_max+1=%u\n",_unsigned_int_max,_unsigned_int_max+1);//_unsigned int_max=4294967295,_unsigned int_max+1=0
printf("_unsigned int_min=%u,_unsigned int_min-1=%u\n",_unsigned_int_min,_unsigned_int_min-1);//_unsigned int_min=0,_unsigned int_min-1=4294967295

//unsigned short型大小2字节,数值范围:0 ~ 2^16 -1 (即 0~65535)
unsigned short _unsigned_short_max = 65535;
unsigned short _unsigned_short_min = 0;
printf("_unsigned_short_max=%hu,_unsigned short_max+1=%hu\n",_unsigned_short_max,_unsigned_short_max+1);//_unsigned_short_max=65535,_unsigned short_max+1=0
printf("_unsigned_short_min=%hu,_unsigned short_min-1=%hu\n",_unsigned_short_min,_unsigned_short_min-1);//_unsigned_short_min=0,_unsigned short_min-1=65535

//long型大小4字节,数值范围:0 – 2^(32)-1 (即 0~4294967295)
unsigned long _unsigned_long_max = 4294967295;
unsigned long _unsigned_long_min = 0;
printf("_unsigned_long_max=%lu,_unsigned_long_max+1=%lu\n",_unsigned_long_max,_unsigned_long_max+1);//_unsigned_long_max=4294967295,_unsigned_long_max+1=0
printf("_unsigned_long_min=%lu,_unsigned_long_min-1=%lu\n",_unsigned_long_min,_unsigned_long_min-1);//_unsigned_long_min=0,_unsigned_long_min-1=4294967295

//unsigned long long型大小8字节,数值范围:0~2^64-1(即 0 ~ 18446744073709551615)
unsigned long long _unsigned_long_long_max = 18446744073709551615;
unsigned long long _unsigned_long_long_min = 0;
//_unsigned_long_long_max=18446744073709551615,_unsigned_long_long_max+1=0
//_unsigned_long_long_min=0,_unsigned_long_long_min-1=18446744073709551615
printf("_unsigned_long_long_max=%llu,_unsigned_long_long_max+1=%llu\n",_unsigned_long_long_max,_unsigned_long_long_max+1);
printf("_unsigned_long_long_min=%llu,_unsigned_long_long_min-1=%llu\n",_unsigned_long_long_min,_unsigned_long_long_min-1);
///   常用整型typedef
//    typedef short               int16_t;
//    typedef unsigned short      uint16_t;
//    typedef int                 int32_t;
//    typedef unsigned            uint32_t;
//    typedef long long           int64_t;
//    typedef unsigned long long  uint64_t;

纯C语言tcp客户端

g++ tcp_client.c -lpthread -o cli
./cli

tcp_client.c

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <malloc.h>
#include <string>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define socklen_t int
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#endif

typedef struct {
    int32_t fd;
    int32_t localPort;
    int32_t remotePort;
    int32_t isStart;
    int32_t isLoop;
    pthread_t threadId;

    //char serverIp[30];
    void* user;
    struct sockaddr_in local_addr;
    struct sockaddr_in remote_addr;
    void (*receive)(char *data, int32_t nb_data,void* user);
}TcpHandle;

int32_t create_tcpClient(TcpHandle *tcpClient, char *serverIp, int32_t serverPort, int32_t plocalPort)
{
    if (tcpClient == NULL)		return -1;

    tcpClient->localPort = plocalPort;
    tcpClient->fd = -1;
    tcpClient->remotePort = serverPort;

#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2, 2);
    WSAStartup(wVersionRequested, &wsaData);//初始化socket库
#endif

    tcpClient->local_addr.sin_family = AF_INET;
    tcpClient->local_addr.sin_port = htons(tcpClient->localPort);
    // lcl_addr.sin_addr.s_addr=inet_addr(ip);
    tcpClient->local_addr.sin_addr.s_addr = INADDR_ANY;
    if((tcpClient->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        return -2;
    }

    tcpClient->remote_addr.sin_family = AF_INET;
    tcpClient->remote_addr.sin_port = htons(tcpClient->remotePort);

#ifdef _WIN32
    tcpClient->remote_addr.sin_addr.S_un.S_addr=inet_addr(serverIp);
#else
    tcpClient->remote_addr.sin_addr.s_addr = inet_addr(serverIp);//将点分十进制IP转换成网络字节序IP
#endif

    if(connect(tcpClient->fd, (struct sockaddr *)&tcpClient->remote_addr, sizeof(tcpClient->remote_addr)) < 0)
    {
        perror("connect");
        return -3;
    }
    
    printf("connect success\n");

    return 0;
}

void* recv_thread_tcpClient(void *obj) {
#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2, 2);
    WSAStartup(wVersionRequested, &wsaData);
#endif
    TcpHandle *ptcpClient = (TcpHandle*) obj;
    ptcpClient->isStart = 1;
#ifdef _WIN32
    int32_t timeout=200;
    setsockopt(ptcpClient->fd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout,  sizeof(timeout));
#else
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 20000;  // 20 ms
    setsockopt(ptcpClient->fd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv,	sizeof(struct timeval));
#endif
    char buffer[2048] = { 0 };
    ptcpClient->isLoop = 1;

    int32_t len = 0;
    socklen_t src_len = sizeof(struct sockaddr_in);

    while (ptcpClient->isLoop)
    {
        struct sockaddr_in src;
        memset(&src, 0, src_len);
        memset(buffer, 0, 2048);
        if ((len = recvfrom(ptcpClient->fd, buffer, 2048, 0,	(struct sockaddr*) &src, &src_len)) > 0)
        {
			printf("buffer=%s\n",buffer);
        }
    }
    ptcpClient->isStart = 0;
    #ifdef _WIN32
    closesocket(ptcpClient->fd);
    #else
    close(ptcpClient->fd);
    #endif
    ptcpClient->fd = -1;

    return NULL;
}

TcpHandle *pTcpHandle;
int main()
{
	pTcpHandle = (TcpHandle *)malloc(sizeof(TcpHandle));
    	create_tcpClient(pTcpHandle,(char*)"127.0.0.1",9090,0);
	
	pthread_create(&pTcpHandle->threadId, 0, recv_thread_tcpClient, pTcpHandle);
	
	std::string msg = "hello";
	send(pTcpHandle->fd,msg.c_str(),msg.size(),0);
	
	while(1){}
}

纯C语言tcp服务端

g++ tcp_server.c -lpthread -o ser
./ser

tcp_server.c

#include <stdint.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <string>

#ifdef _WIN32
#define _WIN32_WINNT 0x0600
#include "windows.h"
#include <winsock2.h>
#include <ws2tcpip.h>

#define socklen_t int
#else
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#endif

#ifndef _WIN32
#include <stddef.h>
#define GetSockError()	errno
#define SetSockError(e)	errno = e

#define closesocket(s)	close(s)
#else
#define GetSockError()	WSAGetLastError()
#define SetSockError(e)	WSASetLastError(e)
#define setsockopt(a,b,c,d,e)	(setsockopt)(a,b,c,(const char *)d,(int)e)

#endif

typedef struct {
    int32_t serverfd;
    int32_t connFd;
    int32_t isStart;
    int32_t isLoop;
    pthread_t threadId;
    int32_t serverPort;
    void* user;
    char remoteIp[32];

    struct sockaddr_in local_addr;
    struct sockaddr_in remote_addr;
    void (*receive)(char *data, int32_t nb_data,void* user,int32_t clientFd);
    void (*startStunTimer)(void* user);
}TcpServer;

int32_t create_tcpServer(TcpServer *tcpServer, int32_t listenPort)
{
    if (tcpServer == NULL)
        return 1;

    tcpServer->serverfd = -1;
    tcpServer->serverPort = listenPort;
    tcpServer->local_addr.sin_family = AF_INET;
    tcpServer->local_addr.sin_port = htons(listenPort);
#ifdef _WIN32
    tcpServer->local_addr.sin_addr.S_un.S_addr=INADDR_ANY;
#else
    tcpServer->local_addr.sin_addr.s_addr = INADDR_ANY;
#endif
    return 0;
}

void* run_tcpConnClient_thread(void *obj)
{
    TcpServer* tcpServer=(TcpServer*)obj;
    int connfd=tcpServer->connFd;
    char remoteIp[32]={0};
    strcpy(remoteIp,tcpServer->remoteIp);

    int32_t nBytes =0;
    char buffer[2048] = { 0 };
    while (tcpServer->isLoop)
    {
        memset(buffer, 0, 2048);
        nBytes = recv(connfd, buffer, 2048, 0);

        if (nBytes > 0)
        {
            printf("buffer=%s\n",buffer);
            
            std::string msg = "back";
            send(connfd,msg.c_str(),msg.size(),0);
        }
        else if (nBytes == -1)
        {
            int32_t sockerr = GetSockError();
            if (sockerr == EINTR)
                continue;
            if (sockerr == EWOULDBLOCK || sockerr == EAGAIN)
            {
                nBytes = 0;
                continue;
            }
#ifdef Q_OS_LINUX
            printf("%s, recv returned %d. GetSockError(): %d (%s)\n",  __FUNCTION__, nBytes, sockerr, strerror(sockerr));
#endif

            continue;
        }
    }
    closesocket(connfd);

    return NULL;
}

void* run_tcpServer_thread(void *obj) {
#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2, 2);
    WSAStartup(wVersionRequested, &wsaData);
#endif
    TcpServer *tcpServer = (TcpServer*) obj;
    tcpServer->isStart = 1;
    tcpServer->serverfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#ifdef _WIN32
    int32_t timeout=200;
    setsockopt(tcpServer->serverfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout,  sizeof(timeout));
#else
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 200000;  // 200 ms
    setsockopt(tcpServer->serverfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv,
            sizeof(struct timeval));
#endif
    printf("http tcp server is starting,listenPort==%d\n", tcpServer->serverPort);
    int bReuseaddr = 1;
    setsockopt( tcpServer->serverfd, SOL_SOCKET, SO_REUSEADDR, ( const char* )&bReuseaddr, sizeof( int ) );

    if (bind(tcpServer->serverfd, (struct sockaddr*) &tcpServer->local_addr,sizeof(struct sockaddr_in)) < 0) {
        printf("http server bind error(%d)",GetSockError());
        exit(1);
    }
    listen(tcpServer->serverfd, 5);

    tcpServer->isLoop = 1;
    socklen_t src_len = sizeof(struct sockaddr_in);
    while (tcpServer->isLoop)
    {
        struct sockaddr_in src;
        memset(&src, 0, src_len);
        int connfd = accept(tcpServer->serverfd, (struct sockaddr*) &src, &src_len);
        if(connfd>-1)
        {
            pthread_t th;
            tcpServer->connFd=connfd;
            memset(tcpServer->remoteIp,0,sizeof(tcpServer->remoteIp));
#ifdef _WIN32
            inet_ntop(AF_INET,&src.sin_addr.s_addr, tcpServer->remoteIp, sizeof(tcpServer->remoteIp));
#else
            inet_ntop(AF_INET,&src.sin_addr.s_addr, tcpServer->remoteIp, sizeof(tcpServer->remoteIp));
#endif

            pthread_create(&th, 0, run_tcpConnClient_thread, tcpServer);
        }
    }
    tcpServer->isStart = 0;
#ifdef _WIN32
        closesocket(tcpServer->serverfd);
#else
        close(tcpServer->serverfd);
#endif
    tcpServer->serverfd = -1;

    return NULL;
}

int main()
{
	TcpServer* pTcpServer = (TcpServer *)malloc(sizeof(TcpServer));
    	create_tcpServer(pTcpServer,9090);

	pthread_create(&pTcpServer->threadId, 0, run_tcpServer_thread, pTcpServer);
	
	while(1){}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C/C++算法常用手册是程序员日常工作中不可或缺的工具书之一。该手册主要收录了程序员在开发过程中常用算法,以及相应的代码实现。该手册涵盖了诸如数据结构、排序、查找、递归、贪心、动态规划、字符串等算法,帮助程序员快速掌握这些算法的基本原理和实现方式。简单地说,该手册将算法的核心原理和实现细节集中在了一起,兼顾了易懂性和实用性。 随着程序员需求的不断增加,该手册逐渐扩充了更多的算法类型。同时,该手册还根据算法的不同应用场景进行分类,方便程序员快速查找和使用。例如,程序员可以通过该手册快速了解不同数据结构的原理和实现方法,了解常见算法的时间复杂度和空间复杂度,还可以查找常见的实际问题中的算法实现方式。 总的来说,C/C++算法常用手册是程序员必备的工具之一,帮助程序员提高算法的实现能力和解决实际问题的能力,提高程序的效率和质量。 ### 回答2: C/C++常用算法手册是一本介绍计算机算法的参考手册,主要面向C/C++语言程序员。该手册总结了各种常用算法,包括排序、查找、图论、字符串等。通过该手册的学习,可以让程序员更好地掌握C/C++编程的技巧和方法。 该手册中介绍了排序算法,包括冒泡排序、插入排序、选择排序、快速排序、归并排序等。对于不同的排序算法,手册详细介绍了它们的思路和实现方法,同时也对它们的时间复杂度和效率进行了分析和比较。 在查找方面,手册介绍了常用的顺序查找和二分查找算法,它们可以帮助程序员快速地定位和查找数据。 在图论和字符串方面,手册介绍了很多有用的算法,如最短路径算法、最小生成树算法、字符串匹配算法等。这些算法可以帮助程序员更好地解决实际问题。 总之,C/C++常用算法手册是一本非常实用和有价值的参考书,它可以让程序员掌握更多的C/C++算法技巧,提高程序员的编程能力和开发效率。 ### 回答3: C/C++ 常用算法手册是一本总结了 C/C++ 编程语言常用算法、数据结构、设计模式等知识的参考书籍。 相对于其他语言,C 和 C++ 语言有着更高的执行效率和更多的编程自由度,也因此被广泛应用于开发高性能、底层的软件程序。在这样的应用场景下,对算法和数据结构的掌握显得尤为重要。 C/C++ 常用算法手册涵盖了各种基础的算法和数据结构,比如排序、查找、链表、树等。同时,它也介绍了一些常用的高级算法,比如动态规划、贪心算法和回溯算法。 此外,该手册还详细说明了面向对象编程领域中常用的设计模式和其实现方式,例如工厂模式、装饰器模式等。 阅读 C/C++ 常用算法手册不但能够让读者掌握常用算法的实现方法,更能提高编程思维和技巧。另外,在实际应用中,编写高效的程序不仅需要算法的巧妙运用,更需要细致、严谨的代码风格和设计思路。此时,该手册中丰富的示例代码和编码规范性的讲解也能为读者提供很大的帮助。 总之,C/C++ 常用算法手册是一本既实用又深入的参考书,适合广大 C/C++ 开发者和算法学习者阅读。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值