杂记,主要包含各种锁

① 【shell脚本】

DT:%G(标准计数周的年份)%m(月份)%d(日)_%H(小时)%M(分钟)%S(秒)
linux命令大全:
参考链接:https://www.runoob.com/linux/linux-command-manual.html

②【条件编译】

ifeq:判断两个参数是够相等,相等时条件成立为true,不相等为false
ifdef:判断变量是否被定义,非空则为真(没有定义的变量为空)
ifneq:ifeq关键字相反,用来判断参数是否不相等。
经典场景
在 & | && || 之间加上 #ifdef #endif

   171     if (   (a & b)                                                                                                                       
   172         && (          (  (c & (d | e | f) ) && (g&h)   )
   173 #ifdef T_true
   174             || (  (i & j) && (k & (l))  )
   175 #endif
   176                )
   177         ) 
   -----
   #ifdef  宏定义
   函数
   #endif
   //注释:如果该宏定义被定义,则执行下方函数。其他同理,即if 、 ifeq 、ifneq 等等

当比较的参数不相等时,条件语句才成立,值为true,否则为false。
参考链接:https://www.zhaixue.cc/makefile/makefile-ifeq.html#:~:text=ifdef

③【include、-include、sinclude】

sinclude:兼容其他的make程序,等价"-include"
-include:忽略由于包含文件不存在或者无法创建时的错误提示
当文件不存在时,make在读取完makefile后会试图使用规则来创建该文件,当不能创建
该文件时,make将提示致命错误并退出,输出以下提示:

Make:*** No rule to make target ‘’. Stop

如果指示符“include”指定的文件不是以斜线开始(绝对路径,如/usr/src/Makefile…),
而且当前目录下也不存在此文件。
make将根据文件名试图在以下几个目录下查找:首先,查找使用命令行选项“-I”或
者“–include-dir”指定的目录,如果找到指定的文件,则使用这
个文件;
否则继续依此搜索以下几个目录(如果其存在):
“/usr/gnu/include”、“/usr/local/include”和“/usr/include”
参考链接:https://www.cnblogs.com/3me-linux/p/8882232.html

④ 【预编译】

#indef MAIN_DEBUG | main函数的DEBUG测试
#error “This is main_debug” | #error:预编译指示字,生成编译错误信息,可默认
#endif

指令 用途
.# 空指令,无任何效果
#include 包含一个源代码文件
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
#define 定义宏,大型工程中,考虑定义某个常量时,必须检查此常量是否已被定义,麻烦
#if defined 如果被定义,在大型工程中避免直接重复定义更改常量的值,消除警告
#if !defined 防止头文件被重复包含
参考链接:https://www.cnblogs.com/herbertchina/p/4306818.html
重点: #define NFC… (1)
()的作用:避免粘包,特别是有的宏在字符串里面替换
参考链接:https://blog.csdn.net/t18438605018/article/details/111575224
#error #warning 的作用:
手搓代码如下:

#include <stdio.h>

#define PCHAR char*     //宏定义只是替换,如果这样会导致 PCHAR p1,p2; 报错,即p2未定义
#define TTT   (2)
#define TT   (3)
#ifndef TTT             //如果没有定义 TTT,则执行下一行代码
#error refdefine TTT    //#error:生成错误信息“#error refdefine TTT”,并停止编译,代表禁止此方向
                       //即 #error 的作用是 确保执行方向正确(你所设想的),若错误则提示并报错
#elif T
#warning this is TTT.   //若定义了宏定义 T,则生成并提示编译警告信息“#warning this is TTT”,但会继续编译
#else 
typedef int* PINT;
int main()
{
   //PCHAR p1,p2;      //或者单个定义,亦或者用 typedef,如 PINT p3,p4;
   PCHAR p1;
   PCHAR p2;

   char **a = &p1;
   char **b = &p2;

   PINT p3,p4;
   int **c = &p3;
   int **d = &p4;

   printf("Hello world\n");
   return 0;
   (void)a;        //部分编译器如果已经定义的变量未使用会报警告,(void)变量; 可解决警告,不建议
   (void)b;
   (void)c;
   (void)d;
}
#endif

#undef 的作用:
手搓代码如下:

#include <stdio.h>

#define TTT (3)

int main()
{
   printf("TTT:%d\n",TTT);
#undef TTT
#warning "use of undeclared identifier 'TTT'"
   printf("TTT:%d\n",TTT);
   printf("Hello world\n");
   return 0;
}

#pragma 的作用:
示例代码:
#include <stdio.h>

#pragma once //文件最开始,避免头文件的重复引用,保证每个头文件只编译一次,
//再加入同名的头文件也没有关系,反正也不编译,且不会报错
//#define T (1)

#ifdef T
#pragma message(“T is defined”) //控制台在编译此处时提示信息,预防定义太多而忘记关键的宏
#endif

#pragma warning (disable:4707) //屏蔽4707警告
#pragma warning (once:4706) //只显示一次4706警告
#pragma warning (error:164) //将164号警告当作一个错误
//等价
#pragma warning (disable:9999;once:0000;error:1314)

#pragma comment(lib,“user1.lib”) //将 user1.lib 库文件导入本工程中

#pragma pack(1) //修改字节对齐为 1

void func(void)
{
;
}
int main()
{
printf(“Hello world\n”);
return 0;
}

⑤ifdef _cplusplus extern C的功能

c++:支持函数重载,会将 函数参数类型 加到编译后的代码中,而不仅仅是函数名
c:不支持函数重载,编译c代码的函数时不会带上函数的参数类型,一般只包括函数名
————————————————————

#ifdef __cplusplus 
extern "C" { 
#endif

//c语法的一段代码(自定义函数)

#ifdef __cplusplus 
} 
#endif

备注:不知道是被c调用还是c++调用时,请添加此段代码
——————————————
手搓代码如下

#include <iostream>
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
void func()
{
    printf("hello world\n");
    
}
#ifdef __cplusplus
}
#endif
int main(void)
{
    func();
    cout<<"Hello world"<<endl;
    return 0;
}
——————————————

重点:
语句:extern int a;
仅仅是一个变量的声明,其并不是在定义变量a,也并未为a分配空间。
变量a在所有模块中作为一种全局变量只能被定义一次,否则会出错。

通常来说,在模块的头文件中对本模块提供给其他模块
引用的 函数和全局变量 以关键字extern声明

extern对应的关键字是static,static表明变量或者函数只能在本模块中使用,
因此,被static修饰的变量或者函数不可能被extern C修饰
手搓代码如下

//文件1:
#include <stdio.h>
#include <pthread.h>

int a = 8;
void funx()
{
    pthread_mutex_t t;
    pthread_mutex_lock(&t);
    int x = a++;
    printf("func:%d\n",x);
    pthread_mutex_unlock(&t);
}
//文件2:
#include <stdio.h>

int main()
{
    extern int a;
    printf("a:%d\n",a);
    return 0;
}
// gcc *.c

—————————————————————plus 等价 “+”

__cplusplus是c++定义的宏
如果是c++调用,extern c声明有效
如果是c 调用,extern c声明无效
原因:c++与c的编译方式不一样,“extern C”{ 表示编译器用C语言的模式编译
参考链接:https://www.cnblogs.com/TurboLemon/p/6364241.html

拓展:extern的作用:
https://www.cnblogs.com/lanhaicode/p/10633125.html
static修饰的函数是否能被外部使用:
https://blog.csdn.net/weixin_42031299/article/details/115942270
C语言中static关键字用法和作用:
https://blog.csdn.net/weixin_42031299/article/details/115587119

⚪⚪⚪pthread_self():获取本线程自身的 id 号

⑥【读写锁】pthread_rwlock_t 结构体

pthread_rwlock_init:初始化读写锁
pthread_relock_rdlock:请求读锁(阻塞、直到获得锁为止)
pthread_rwlock_tryrdlock:读取非阻塞读写锁中的锁
pthread_rwlock_wrlock:请求写锁
pthread_rwlock_trywrlock:写入非阻塞读写锁中的锁
pthread_rwlock_timedrdlock:锁定写入时的读写锁
pthread_rwlock_unlock:解除锁定读写锁
pthread_rwlock_destroy:销毁读写锁
pthread_rwlock_t 读写锁函数参考链接:
https://www.cnblogs.com/renxinyuan/p/3875659.html
读操作可以共享,写操作是排他的,读可以有多个在读,写
只有唯一个在写,同时写的时候不允许读。具有强读者同步和强写者同步两种形式
对于读数据比修改数据频繁的应用,用读写锁代替互斥锁可以提高效率,写上锁,读无影响
实现:http://c.biancheng.net/view/8635.html

手搓代码如下:

#include <stdio.h>
#include <pthread.h>
/*
pthread_rwlock_init:初始化读写锁
pthread_relock_rdlock:读取读写锁中的锁(阻塞、直到获得锁为止)
pthread_rwlock_tryrdlock:读取非阻塞读写锁中的锁
pthread_rwlock_wrlock:写入读写锁中的锁
pthread_rwlock_trywrlock:写入非阻塞读写锁中的锁
pthread_rwlock_unlock:解除锁定读写锁
pthread_rwlock_destroy:销毁读写锁
 */
#include <unistd.h>

static int x = 0;
//创建读写锁变量
pthread_rwlock_t  rwlock;

void* read_thread(void* arg)
{
    printf("1、this is read_thread.\n");
    printf("the read_thread_id is %ld.\n",pthread_self());
    while(1)
    {
        sleep(3);
        //请求读锁
        pthread_rwlock_rdlock(&rwlock);
        printf("x=%d\n",x);
        pthread_rwlock_unlock(&rwlock);
    }
    return NULL;
    (void)arg;
}

void* write_pthread(void* arg)
{
    printf("2、this is write_thread.\n");
    printf("the write_thread_id is %lu.\n",pthread_self());
    while(1)
    {
        sleep(3);
        //请求写锁
        pthread_rwlock_wrlock(&rwlock);
        ++x;
        printf("x:%d\n",x);
        pthread_rwlock_unlock(&rwlock);
    }
    return NULL;
    (void)arg;
}
int main()
{
    int i;
    //初始化读写锁
    pthread_rwlock_init(&rwlock,NULL);
    //创建 3 个读取 x 变量的线程
    pthread_t readThread[3];
    for(i = 0; i < 3; ++i)
    {
        pthread_create(&readThread[i],NULL,read_thread,NULL);
    }
    //创建 1 个修改 x 变量的线程
    pthread_t writeThread;
    pthread_create(&writeThread,NULL,write_pthread,NULL);
    //等待各个线程执行完成
    pthread_detach(writeThread);//非阻塞式,分离线程,线程运行完后系统自动回收
    pthread_join(writeThread,NULL);//阻塞式,告诉系统,回收线程

    for(int i = 0; i < 3; ++i)
    {
        pthread_join(readThread[i],NULL);
    }
    //销毁读写锁
    pthread_rwlock_destroy(&rwlock);
    return 0;
}


⑦【uint32_t家族】

typedef  signed  char 	int8_t;
typedef  short  int 		int16_t;
typedef  int  		int32_t;
typedef  long  int 		int64_t;
typedef  unsigned  char	uint8_t;
......

声明一点*_t是typedef定义的表示标志,是一种规范化的表示
参考链接:https://blog.csdn.net/ai_faker/article/details/118146275

⑧ 【互斥锁】pthread_mutex_t 结构体 保持多个线程同步

pthread_mutex_init:动态创建互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER:静态创建
pthread_mutexattr_setpshared:设置互斥锁
pthread_mutexattr_getshared:获得互斥锁的范围(进程间,线程间)
pthread_mutexattr_settype:设置锁的类型(普通锁、嵌套锁、检错锁、适应锁)
pthread_mutexattr_gettype:得到锁的类型
pthread_mutex_destory:释放锁
pthread_mutex_lock:加锁
pthread_mutex_unlock:解锁
pthread_mutex_trylock:测试加锁
参考链接:https://www.cnblogs.com/eustoma/p/10054783.html
pthread_detach()即主线程与子线程分离,子线程结束后,资源自动回收
pthread_join()即是子线程合入主线程,主线程阻塞等待子线程结束,然后回收子线程资源
一个可结合的线程能够被其他线程收回其资源和杀死
一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放
参考链接:https://www.cnblogs.com/ggzhangxiaochao/p/14415867.html

⑧ 【条件变量】 线程可用的同步机制、与信号类似 【同步】

与互斥量一起使用时,允许线程以无竞争的方式的等待特定的条件发生
使用场景:
条件变量要与互斥量一起使用,条件本身是由互斥量保护的
线程在改变条件之前必须首先锁住互斥量
数据类型:pthread_cond_t
pthread_cond_t cond = PTHREAD_COND_INITIALIZER:静态初始化条件变量
pthread_cond_init:动态初始化
pthread_cond_wait:等待条件变量
pthread_cond_timedwait:控制时间等待条件变量
pthread_cond_signal:至少唤醒一个等待该条件的线程
pthread_cond_broad:唤醒等待该条件的所有线程
pthread_cond_destroy:销毁条件变量
参考链接:
https://blog.csdn.net/www_dong/article/details/120211090
https://www.cnblogs.com/harlanc/p/8596211.html

⚪⚪⚪条件变量和互斥锁一起使用:
https://www.jb51.net/article/102764.htm
http://t.csdn.cn/QU2X7

手搓代码如下:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

static int flag = 0;

pthread_mutex_t s_mutex;
pthread_cond_t cond;
void* thread_1(void* arg)
{
    while(1)
    {
        pthread_mutex_lock(&s_mutex);
        flag++;
        pthread_mutex_unlock(&s_mutex);
        if(1000 <= flag)
        {
            pthread_cond_signal(&cond);
        }
    }
    return NULL;
    (void)arg;
}
void* thread_2(void* arg)
{
    while(1)
    {
        pthread_mutex_lock(&s_mutex);
        flag++;
        pthread_mutex_unlock(&s_mutex);
        if(1000 <= flag)
        {
            pthread_cond_signal(&cond);
        }
    }
    return NULL;
    (void)arg;
}
#if 1
void* thread_3(void* arg)
{
    while(1)
    {
        pthread_mutex_lock(&s_mutex);
        if(1000 <= flag)
        {
            flag--;    
        }
        pthread_mutex_unlock(&s_mutex);
    }
    return NULL;
    (void)arg;
}
#endif
void* thread_4(void* arg)
{
    while(1)
    {
        sleep(1);
        pthread_mutex_lock(&s_mutex);
        while(1000 <= flag)
        {
            pthread_cond_wait(&cond,&s_mutex);
            printf("flag=%d\n",flag);
            flag = 0;
        }
        pthread_mutex_unlock(&s_mutex);
    }
    return NULL;
    (void)arg;
}
int main()
{
#if 0
    //静态初始化互斥锁(在编译阶段完成初始化)
    pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER; 
    //静态初始化条件变量
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
#endif

    pthread_t id_1,id_2,id_3,id_4;
    pthread_create(&id_1,NULL,thread_1,NULL);
    pthread_create(&id_2,NULL,thread_2,NULL);
    pthread_create(&id_3,NULL,thread_3,NULL);
    pthread_create(&id_4,NULL,thread_4,NULL);

    pthread_join(id_1,NULL);
    pthread_join(id_2,NULL);
    pthread_join(id_3,NULL);
    pthread_join(id_4,NULL);
#if 1
    //动态初始化互斥锁(在运行阶段完成初始化)
    pthread_mutex_t *a_mutex = NULL;
    a_mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
    pthread_mutex_init(a_mutex,NULL);
    //动态初始化条件变量
    pthread_cond_init(&cond,NULL);

    //释放互斥锁
    pthread_mutex_destroy(a_mutex);
    free(a_mutex);
    //释放条件变量
    pthread_cond_destroy(&cond);
#endif
    return 0;
    (void)s_mutex;
    (void)cond;
}

⑨ 【回调函数】 重温

分支、回调、根据函数指针调用分支
int callback_1(int x);
int callback_2(int x);
int callback_3(int x);
void Callback(int y, int (*callback)(int) );
类似重载、多态的效果,但是实现方法不一样
参考链接:https://www.runoob.com/w3cnote/c-callback-function.html

手搓代码如下

#include <stdio.h>
int callback_1(int x)
{
    printf("hello,siri,this is callback_1: x = %d \n",x);
    return 0;
}
int callback_2(int x)
{
    printf("hello,siri,this is callback_2: x = %d \n",x);
    return 0;
}
int callback_3(int x)
{
    printf("hello,siri,this is callback_3: x = %d \n",x);
    return 0;
}
void Callback(int x,int (*callback)(int))
{
    puts("");
    printf("hello,siri.Entering Callback Function.\n");
    callback(x);
    printf("hello,siri.Leaving Callback Function.\n");
    puts("");
}
int main()
{
    int a = 2, b = 4, c = 6;
    printf("Hello world\n");
    Callback(a,callback_1);
    Callback(b,callback_2);
    Callback(c,callback_3);
    printf("Hello world\n");

    return 0;
}

⑩ 【getaddrinfo()函数、freeaddrinfo()函数】

gethostbyname():用域名或主机名获取IP地址,仅仅支持IPV4
不允许调用者指定所需地址类型的任何信息,返回的结构只包含了用于存储IPv4地址的空间
getaddrinfo():与协议无关的,既可用于IPv4也可用于IPv6
能够处理名字到地址以及服务到端口这两种转换
返回的是一个addrinfo的结构(列表)指针而不是一个地址清单。
这些addrinfo结构随后可由套接口函数直接使用。
如此以来,getaddrinfo函数把协议相关性安全隐藏在这个库函数内部。
应用程序只要处理由getaddrinfo函数填写的套接口地址结构,该函数在POSIX规范中定义
int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );
备注:
由getaddrinfo返回的所有存储空间都是动态获取的,
这些存储空间必须通过调用freeaddrinfo返回给系统
void freeaddrinfo( struct addrinfo *ai );
参考链接:https://www.cnblogs.com/cxz2009/archive/2010/11/19/1881693.html

⑩① 【断言】 assert() ——如果断言为假,则中止程序 宏函数

#include <assert.h>
void assert( int expression );
这个宏可以帮助程序员发现程序中的bug,或者通过崩溃处理异常情况,会产生有限的调试输出
程序在我的假设条件下,能够正常良好的运作,其实就相当于一个 if 语句
但是这样写的话,就会有无数个 if 语句,甚至会出现,一个 if 语句的括号从文件头到文件尾,
并且大多数情况下,我们要进行验证的假设,只是属于偶然性事件,又或者我们仅仅想测试一下,一些最坏情况是否发生,所以这里有了 assert()
表达式为false(即比较等于零),assert()向stderr打印一个标准错误消息,并通过调用abort()终止程序
错误消息包括包含assert()调用的文件和函数的名称、调用的源代码行号和参数的文本
缺点:频繁的调用会极大的影响程序的性能,增加额外的开销
在调试结束后,可以通过在包含 #include 的语句之前插入 #define NDEBUG 来禁用 assert 调用,
示例代码如下:
#include
#define NDEBUG
#include
参考链接:https://www.cnblogs.com/thisway/p/5558914.html
备注:错误检查后面跟assert(0)的作用:

#define T_CONFIG(config){ \
	if( NULL == config){ \
		error(...); \
		assert(0); \
	} \
}
  1. 捕捉逻辑错误。可以在程序逻辑必须为真的条件上设置断言。除非发生逻辑错误,否则断言对程序无任何影响。即预防性的错误检查,在认为不可能的执行到的情况下加一句ASSERT(0),如果运行到此,代码逻辑或条件就可能有问题。
  2. 程序没写完的标识,放个assert(0)调试运行时执行到此为报错中断,好知道成员函数还没写完。
    参考链接:https://blog.csdn.net/xiaodoubao124/article/details/46804319

⑩② 【地址重叠】memcpy函数拷贝内存时需要分类讨论,防止地址重叠
地址重叠:即将拷贝的空间和即将被拷贝的空间重叠了一部分

例如:
【[][][][1][2] [3][4][5][][] [][][][][] 】
| |
src dest 从左到右开始复制时会发送内容丢失,即源空间占据了拷贝空间的一部分
src<dest<src+size 这是需要从右开始拷贝
参考链接:http://t.csdn.cn/wA56U

手搓代码如下(含断言应用)

#include <stdio.h>
#include <assert.h>
void my_memcpy(void *dest,const void *src,size_t n)
{
    char *pdest = (char *)dest;
    const char *psrc = (char *)src;

    assert(dest);
    assert(src);

    if((dest > src) && (dest < src + n))
    {
        pdest = dest + n - 1;
        psrc = src + n - 1;
        while(n--)
            *pdest-- = *psrc--;
    }else
    {
        while(n--)
            *pdest++ = *psrc++;
    }
}
int main()
{
    char dest[20],src[20] = "hello";
    my_memcpy(dest,src,10);
    printf("dest:%s\n",dest);
    printf("Hello world\n");
    return 0;
}
#if 0
memcpy的这种实现方式只适用于
地址向前重叠(src > dest && dest + count > src)的情况,
如果是地址向后重叠(dest > src && src + count > dest)的情况,
应该是从后向前拷贝。所以memcpy的实现应该分两种情况的
#endif

⑩③ 【setsockopt()函数、getsockopt()函数】重温

int setsockopt(int sockfd,
int level,
int opt_name,
const void *opt_val,
socklen_t opt_len);

int getsockopt(int sockfd,
int level,
int optname,
const void *optval,
socklen_t option);

setsockopt():用于任意类型、任意状态套接口的设置选项值

setsockopt(socket,
被设置的选项的级别[SOL_SOCKET],
准备设置的选项,
有哪些取值[取决于level],
选项值的最大长度[入口] || 选项值的实际长度[出口]);

功能描述:
https://www.cnblogs.com/eeexu123/p/5275783.html
https://www.cnblogs.com/warren-liuquan/p/3557701.html

⑩④ 【RPC】 usb间传输

解决多服务器调用的复杂情况,尽量让远程的调用变得简介透明,让团队能够专注于业务开发
实现RPC关键点:
1)信息传递
识别对方的IP、端口、方法名、参数等都需要经过处理,调用IO时是否阻塞
2)数据序列化
通过网络流传输协议,数据必须进行序列化和反序列化
序列化框架的选择:文本协议、二进制协议、压缩二进制协议
3)网络通信
各种RPC的实现框架都使用TCP协议作为网络通信的基本方法
参考链接:https://www.csdn.net/tags/MtTaMg4sNDU4MTY2LWJsb2cO0O0O.html
RPC的思路流程:
程序中需要远程调用的方法调用客户端句柄,传输所需参数
调用本地系统网络部分,发送信息
消息传输
服务器端接收到信息,将信息传给处理此操作的服务器端句柄
句柄调用相应方法,操作数据(可以采用监视器模式)
得到结果,返回结果给句柄
句柄调用服务器的本地网络,返回信息
信息返回
客户端本地网络将信息发送给客户端句柄
句柄将结果返回给方法

⑩⑤ 【#pragma once 与 #ifndef、#define、#endif 的区别】

#pragma once:常用的C/C++预处理指令
只要在头文件的最开始加入这条预处理指令,就能够保证头文件只被编译一次

#ifndef的方式依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,
也能保证内容完全相同的两个文件不会被不小心同时包含。
当然,缺点就是如果不同头文件的宏名不小心“撞车”,
可能就会导致头文件明明存在,编译器却硬说找不到声明的状况。

#pragma once则由编译器提供保证:
同一个文件不会被编译多次。
注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。
带来的好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。
对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。
当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正。

方式一由语言支持所以移植性好,方式二 可以避免名字冲突
参考链接:https://www.cnblogs.com/qiang-upc/p/11407364.html

⑩⑥ typeof() 关键字 —C语言高级用法

1)不用知道函数返回什么类型,可以使用typeof()定义一个用于接收该函数返回值的变量
2)在宏定义中动态获取相关结构体成员的类型
3)也可直接取得已知类型
4)typeof + (指针或者值) 代替 数据类型
参考链接:https://blog.csdn.net/rosetta/article/details/90741468

手搓代码如下:

// 方法1实例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct apple{
    int weight;
    int color;
}L;

struct apple* get_apple_info()
{
    struct apple *a1;
    a1 = (struct apple *)malloc( sizeof(L) );
    if(a1 == NULL)
    {
        printf("malloc error.\n");
        return NULL;
    }

    a1->weight = 2;
    a1->color = 1;

    return a1;
}

int main()
{
    //不知道函数返回什么类型,用 typeof()定义用于接收函数返回值的变量
    typeof(get_apple_info()) r1 = get_apple_info();
    //定义一个变量r1,用于接收函数get_apple_info()返回的值,
    //由于该函数返回的类型是:struct apple *,
    //所以变量r1也是该类型。
    //注意,函数不会执行。

//    r1 = get_apple_info();

    printf("apple weight:%d\n", r1->weight);
    printf("apple color:%d\n", r1->color);

    return 0;
}
// 方法2实例:
#include <stdio.h>
#if 1
//在宏定义中动态获取相关结构体成员类型
#define max(x,y) ({ \
    typeof(x) _max1 = (x); \
    typeof(y) _max2 = (y); \
    (void) (&_max1 == &_max2); \
    _max1 > _max2 ? _max1 : _max2; })
    //如果调用者传参时,两者类型不一致,在编译时就会发出警告 
#define P  0
#endif

#if 0
#define max(x,y) ( (x) > (y) ? (x) : (y) )
#endif
int main()
{
    int a = 3;
    float b = 4.0;
    int r = max(a,b);

    printf("r:%d\n",r);
    return 0;
}
// 方法3实例:
#include <stdio.h>

int main()
{
    int a = 2;
    //直接取得已知类型
    typeof (int *)p;
    p = &a;
    printf("%d\n",*p);
    return 0;
}
// 方法4实例:
#include <stdio.h>

int main()
{
    //typeof + (指针或者值) 代替 数据类型
    char *p1;
    typeof (*p1) ch = 'a';//ch为char类型,不是char *类型
    printf("%ld, %c\n", sizeof(ch), ch);//1, a

    char *p2;
    typeof (p2) p = "hello world";
    //此时的p才是char *类型,由于在64位机器上,所以指针大小为8字节
    printf("%ld, %s\n", sizeof(p), p);//8, hello world
    return 0;
}

⑩⑦ 【韦根协议】

常用格式:标准的26-bit
其他格式:34-bit、37-bit
适用于涉及门禁控制系统的读卡器和卡片的许多特性
没有定义波特率、数据
基本概念:
韦根数据输出有两根线组成、分别是DATA0和DATA1,代表’0’和’1’
输出’0’时:DATA0线上出现负脉冲
输出’1’时:DATA1线上出现负脉冲
负脉冲宽度TP=100us;周期TW=1600us
注意:
韦根协议只能做到单向传输,读头向控制器传输信息为单向,控制器
无法给读卡器发送信号,控制器做出反馈还需借助其他工具
韦根协议的优缺点
优点
简单、通用、成本低、技术对接容易
可以通过其他方式进行加密,或是通过其他接线进行功能叠加
缺点
1、韦根协议是一种通用的协议,几乎所有的门禁控制系统都接受,因此安全性低,无法加密,相当于明文代码,可随意复制。
2、 传输距离短,100m。
3、单向传输,限制性高

⑩⑧ 【信号集函数】

int sigemptyset(sigset_t *set); 将信号集初始化为空
int sigfillset(sigset_t *set); 把信号集初始化包含所有已定义的信号
int sigaddset(sigset_t *set, int signo); 把信号 signo添加到信号集 set 中,成功0,失败-1
int sigdelset(sigset_t *set, int signo); 把信号 signo从信号集 set 中删除,成功0,失败-1
int sigismember(sigset_t *set,int signo);
判断给定的信号 signo 是否是信号集中一个成员,是 1,不是0,无效 -1
int sigpromask(int how,const sigset_t *set,sigset_t *oset);
根据参数指定的方法修改进程的信号屏蔽字,新的信号屏蔽字有参数 set指定,将原本信号屏
蔽字保存在 oset中。
如果set为空,how 则不一样
how取值:
SIG_BLOCK:添加到信号屏蔽字中
SIG_SETMASK:设置信号屏蔽字为参数 set中的信号
SIG_UNBLOCK:移除
备注:调用该函数才能改变进程屏蔽字,之前函数都是改变一个变量的值,不会真正影响进程屏蔽字
int sigpending(sigset_t *set); 将被阻塞的信号中停留在待处理状态的一组信号写到参数 set指向的信号集中
int sigsuspend(const sigset_t *sigmask);将进程的信号屏蔽字替换成 sigmask给出的信号集,然后挂起进程的执行
参考链接:https://www.cnblogs.com/52php/p/5815125.html
回顾:signal()函数 与 sigaction()函数 、 kill()函数 与alarm()函数
https://www.cnblogs.com/52php/p/5813867.html
备注:注意信号处理函数的原型必须为void func(int)

手搓代码如下
信号集:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handler(int sig)
{
    printf("Handle the signal:%d\n",sig);
}

int main()
{
    sigset_t sigset;    //用于记录屏蔽字的信号集
    sigset_t ign;       //用于记录被屏蔽(阻塞)的信号集
    struct sigaction act;

    //清空信号集
    sigemptyset(&ign);
    sigemptyset(&sigset);

    //向信号集中添加 SIGINT
    sigaddset(&sigset,SIGINT);

    //设置处理函数 和 信号集
    act.sa_handler = handler; 
    //指定要与signum关联的操作
    //SIG_DFL(默认操作)、SIG_IGN(忽略此信号)或指向信号处理函数的指针
    
    sigemptyset(&act.sa_mask);//将被屏蔽的信号集初始化为空
    act.sa_flags = 0;       //sa_flags指定一组修改信号行为的标志
    sigaction(SIGINT,&act,0);//用于更改进程在接收到特定信号时所采取的操作

    printf("wait the signal SIGINT...1\n");
    pause();
    //导致调用进程(或线程)休眠,直到传递一个信号,
    //该信号要么终止进程,要么导致调用信号捕获函数

    //把信号屏蔽字设置为参数 set 中的信号
    sigprocmask(SIG_SETMASK,&sigset,0);
    printf("please press ctrl + C in 10 seconds...2\n");
    sleep(10);

    sigpending(&ign);
    if(sigismember(&ign,SIGINT))
    {
        printf("the SIGINT signal has ignored...3\n");
    }

    //从信号集中删除信号 SIGINT
    sigdelset(&sigset,SIGINT);
    printf("wait the signal SIGINT...4\n");

    //将进程的屏蔽字重新设置,即取消对 SIGINT 的屏蔽
    //挂起进程
    sigsuspend(&sigset);

    printf("the app will int 5 seconds...5\n");
    sleep(5);
    return 0;
}

信号 signal:

#include <stdio.h>
#include <signal.h>
// void ( *signal(int sig, void (*func)(int)) )(int);
// func: void (*)(int) 的函数指针
// 备注:注意信号处理函数的原型必须为void func(int)

#include <unistd.h>

void func(int sig)
{
    printf("this is signal 处理函数.只执行一遍\n");

    //恢复终端中断信号的 SIGINT 的默认行为
    (void)signal(SIGINT,SIG_DFL);
    //忽略中断信号
    //(void)signal(SIGINT,SIG_IGN);

    (void)sig;
}
int main()
{
    (void)signal(SIGINT,func);
    while(1)
    {
        printf("......\n");
        sleep(1);
    }
    printf("Hello world\n");
    return 0;
}

信号改良函数 sigaction:

#include <stdio.h>
#include <signal.h>
//int sigaction(int sig,const struct sigaction *act,struct sigaction *oact);
//设置与信号 sig 关联的动作
//若 oact 不是空指针的话,便用它来保存原先对该信号的动作的位置
//act 用于设置指定信号的动作

//sigaction 结构体至少包含以下成员
/*
 *1、 void (*)(int) sa_handler:处理函数指针,相当于 signal 函数的 func 函数
 *2、 sigset_t sa_mask:制定一个信号集,在调用 sa_handler 所指向的信号处理函
    数之前,该信号集将被加入到进程的信号屏蔽字中。
    信号屏蔽字是指当前被阻塞的一组信号,他们不能被当前进程接收到.
 *3、 int sa_flags:信号处理修改器
 sa_flags 结构体:
 SA_NOCLDSTOP 子进程停止时不产生 SIGCHLD 信号
 SA_RESETHAND 对此信号的处理方式在信号处理函数的入口处重置为 SIG_DFL
 SA_RESTART   重启可中断的函数而不是给出EINTR错误
 SA_NODEFER   捕获到信号时不将它添加到信号屏蔽字中
 备注: sa_mask 可消除这一竞态条件(提前被捕获)
 */

#include <unistd.h>
#define T (20)

void func(int sig)
{
    printf("this is sigaction function.\n");
    
    (void)sig;
}

int main()
{
    struct sigaction act;
    act.sa_handler = func;

    // 创建空的信号屏蔽字,即不屏蔽任何信息
    sigemptyset(&act.sa_mask);

    // 使 sigaction 函数重置为默认行为
    act.sa_flags = SA_RESETHAND;

    sigaction(SIGINT,&act,0);

    int a = T;
    while(a--)
    {
        printf("Hello World!\n");
        sleep(100);
    }
    return 0;
}

kill()函数 和 alarm()函数:

#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
 
static int alarm_fired = 0;
 
void ouch(int sig)
{
    alarm_fired = 1;

    (void)sig;
}
 
int main()
{
    pid_t pid;
    pid = fork();
    switch(pid)
    {
    case -1:
        perror("fork failed\n");
        exit(1);
    case 0:          // 子进程  getpid():获取当前进程的 id 号
        sleep(5);
         
        // 子进程真残忍,睡了5秒,醒来的第一件事是向系统报告,请求杀死父进程
        kill(getppid(), SIGALRM);
        exit(0); 
    default:         // 父进程  getppid():获取当前进程的父进程的 id 号
        ;
    }
     
     // 设置处理函数
    signal(SIGALRM, ouch);
    while(!alarm_fired)
    {
        printf("Hello World!\n");
        sleep(1);
    }
    if(alarm_fired)
        printf("\nI got a signal %d\n", SIGALRM);
 
    exit(0);
}

DEBUG、LINEfunc、##VA_ARGS
参考链接:
https://www.cnblogs.com/lixiaohui-ambition/archive/2012/08/21/2649052.html
手搓代码如下:

#include <stdio.h>
#include <string.h>

#if 0			//选择一
#define __debug_printf(flag, fmt, ...) do { \
	printf("%d | %s() : \n", __LINE__, __func__); \
	printf(fmt,  ##__VA_ARGS__); \
} while (0)
#else		//选择二
#define __debug_printf(flag, fmt, ...) do { \
    printf("File: "__FILE__", Line: %d: \n" fmt "\n", __LINE__, ##__VA_ARGS__);  \
} while (0)
#endif
/* 错误级别日志打印 */
#define __debug_error(level, fmt, ...) do { \
    if (level < 0) \
        __debug_printf("error", fmt, ##__VA_ARGS__); \
} while (0)

/* 警告级别日志打印 */
#define __debug_warn(level, fmt, ...) do { \
    if (level > 0) \
        __debug_printf("warn", fmt, ##__VA_ARGS__); \
} while (0)

/* 调试级别日志打印 */
#define __debug_info(level, fmt, ...) do { \
    if (level == 0) \
        __debug_printf("info", fmt, ##__VA_ARGS__); \
} while (0)


void array_printf(unsigned char*data, int len) {
	printf("{");
	for(int i = 0; i < len; ++i){
		printf("%02x%s", data[i], i<(len)-1 ? ":" : " ");
	}
	printf("}\n");
}

static inline void __debug_hexdump(const char *title, unsigned char *data, int length) {
    printf("\n%s: \n", title);
    array_printf(data, length);
    //printf("\n");
}

#define debug_hexdump(level, title, data, len) do { \
    if (level > 2) \
        __debug_hexdump(title, data, len); \
} while (0)

#if 1
#define __DEBUG__  
#ifdef __DEBUG__  
#define DEBUG(msgmat,...) printf("File: "__FILE__", Line: %d: " msgmat "\n", __LINE__, ##__VA_ARGS__)  
//#define DEBUG(format,...) printf("FILE: "__FILE__", LINE: %d: " format "\n", __LINE__)  
/*
 * ##__VA_ARGS__的作用:
debug.c:51:34: warning: format ‘%s’ expects a matching ‘char *’ argument [-Wformat=]
   51 | #define DEBUG(format,...) printf("FILE: "__FILE__", LINE: %d: " format "\n", __LINE__)
      |                                  ^~~~~~~~
debug.c:59:5: note: in expansion of macro ‘DEBUG’
   59 |     DEBUG("LL: %s",str);
      |     ^~~~~

 */
#else  
#define DEBUG(format,...)  
#endif  
int main() {  
    unsigned char str[]="Hello world";  
    char old[]="old_log";
    unsigned char *data = str;
    DEBUG("LL: %s",str); 
    printf("FINE:%s,LINE:%d,func:%s\n",__FILE__,__LINE__,__func__);
//    __debug_hexdump(old,data,sizeof(str));    //直接调用__debug_hexdump()
//    debug_hexdump(2,old,data,sizeof(str));    //不打印
    debug_hexdump(3,old,data,sizeof(str));      //打印
//    __debug_printf(0,"flag\n");               //验证debug打印函数
    __debug_info(0,"this is normal output.\n");                    
    __debug_warn(1,"this is warn.\n");
    __debug_error(-1,"this is err.\n");
    return 0;  
}  
#endif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值