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
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
#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
#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#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;
}
c++:支持函数重载,会将 函数参数类型 加到编译后的代码中,而不仅仅是函数名
c:不支持函数重载,编译c代码的函数时不会带上函数的参数类型,一般只包括函数名
————————————————————
#ifdef __cplusplus
extern "C" {
#endif
//c语法的一段代码(自定义函数)
#ifdef __cplusplus
}
#endif
备注:不知道是被c调用还是c++调用时,请添加此段代码
——————————————
手搓代码如下:
#include
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
void func()
{
printf("hello world\n");
}
#ifdef __cplusplus
}
#endif
int main(void)
{
func();
cout<<"Hello world"<
重点:
语句:extern int a;
仅仅是一个变量的声明,其并不是在定义变量a,也并未为a分配空间。
变量a在所有模块中作为一种全局变量只能被定义一次,否则会出错。
通常来说,在模块的头文件中对本模块提供给其他模块
引用的 函数和全局变量 以关键字extern声明
extern对应的关键字是static,static表明变量或者函数只能在本模块中使用,
因此,被static修饰的变量或者函数不可能被extern C修饰
手搓代码如下:
//文件1:
#include
#include
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
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_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
#include
/*
pthread_rwlock_init:初始化读写锁
pthread_relock_rdlock:读取读写锁中的锁(阻塞、直到获得锁为止)
pthread_rwlock_tryrdlock:读取非阻塞读写锁中的锁
pthread_rwlock_wrlock:写入读写锁中的锁
pthread_rwlock_trywrlock:写入非阻塞读写锁中的锁
pthread_rwlock_unlock:解除锁定读写锁
pthread_rwlock_destroy:销毁读写锁
*/
#include
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;
}
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_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
#include
#include
#include
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
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;
}
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
#include
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); \
} \
}
- 捕捉逻辑错误。可以在程序逻辑必须为真的条件上设置断言。除非发生逻辑错误,否则断言对程序无任何影响。即预防性的错误检查,在认为不可能的执行到的情况下加一句ASSERT(0),如果运行到此,代码逻辑或条件就可能有问题。
- 程序没写完的标识,放个assert(0)调试运行时执行到此为报错中断,好知道成员函数还没写完。
参考链接:https://blog.csdn.net/xiaodoubao124/article/details/46804319
例如:
【[][][][1][2] [3][4][5][][] [][][][][] 】
| |
src dest 从左到右开始复制时会发送内容丢失,即源空间占据了拷贝空间的一部分
src参考链接:http://t.csdn.cn/wA56U
手搓代码如下(含断言应用):
#include
#include
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
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关键点:
1)信息传递
识别对方的IP、端口、方法名、参数等都需要经过处理,调用IO时是否阻塞
2)数据序列化
通过网络流传输协议,数据必须进行序列化和反序列化
序列化框架的选择:文本协议、二进制协议、压缩二进制协议
3)网络通信
各种RPC的实现框架都使用TCP协议作为网络通信的基本方法
参考链接:https://www.csdn.net/tags/MtTaMg4sNDU4MTY2LWJsb2cO0O0O.html
RPC的思路流程:
程序中需要远程调用的方法调用客户端句柄,传输所需参数
调用本地系统网络部分,发送信息
消息传输
服务器端接收到信息,将信息传给处理此操作的服务器端句柄
句柄调用相应方法,操作数据(可以采用监视器模式)
得到结果,返回结果给句柄
句柄调用服务器的本地网络,返回信息
信息返回
客户端本地网络将信息发送给客户端句柄
句柄将结果返回给方法
#pragma once:常用的C/C++预处理指令
只要在头文件的最开始加入这条预处理指令,就能够保证头文件只被编译一次
#ifndef的方式依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,
也能保证内容完全相同的两个文件不会被不小心同时包含。
当然,缺点就是如果不同头文件的宏名不小心“撞车”,
可能就会导致头文件明明存在,编译器却硬说找不到声明的状况。
#pragma once则由编译器提供保证:
同一个文件不会被编译多次。
注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。
带来的好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。
对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。
当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正。
方式一由语言支持所以移植性好,方式二 可以避免名字冲突
参考链接:https://www.cnblogs.com/qiang-upc/p/11407364.html
1)不用知道函数返回什么类型,可以使用typeof()定义一个用于接收该函数返回值的变量
2)在宏定义中动态获取相关结构体成员的类型
3)也可直接取得已知类型
4)typeof + (指针或者值) 代替 数据类型
参考链接:https://blog.csdn.net/rosetta/article/details/90741468
手搓代码如下:
// 方法1实例:
#include
#include
#include
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
#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
int main()
{
int a = 2;
//直接取得已知类型
typeof (int *)p;
p = &a;
printf("%d\n",*p);
return 0;
}
// 方法4实例:
#include
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
#include
#include
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
#include
// void ( *signal(int sig, void (*func)(int)) )(int);
// func: void (*)(int) 的函数指针
// 备注:注意信号处理函数的原型必须为void func(int)
#include
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
#include
//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
#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
#include
#include
#include
#include
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、LINE、func、##VA_ARGS
参考链接:
https://www.cnblogs.com/lixiaohui-ambition/archive/2012/08/21/2649052.html
手搓代码如下:
#include
#include
#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