分类 "C/C++" 的存档.

结构体赋值如何偷懒

结构体赋值的简单写法
比如结构体

typedef struct _abc {
        char a;
        short b;
        int c;
} abc;

一般人知道可以这样写abc v={0,1,2};
二般人知道还可以这样写abc v={.c=2,.b=1}; 效果同上。
但这都是定义变量时赋初值,如果是二次赋值,大多数人都只会

v.a=0;
v.b=1;
v.c=2;

因为这时如果写成v={.c=2,.b=1}会出现编译错误。
但是,其实只需要做下类型转换,依然可以这样写

v=(abc){.c=2,.b=1};

虽然这里写的代码不是很明显,但是如果结构体成员变量比较多,变量名又比较长的时候,这个懒还是很值得偷的,而且个人感觉这样写也会更清楚一些。

全局常量和局部常量的区别

局部const

[root@IDC-D-2270 czt]# cat clzd.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv) {
    const int a = 10;
    int *p = (int *) &a;
    cout << *p << " " << a << endl;
    cout << p << " " << &p << endl;
    *p = 20;
    cout << *p << " " << a << endl;
    cout << p << " " << &p << endl;
    return 0;
}
[root@IDC-D-2270 czt]# g++ clzd.cpp
[root@IDC-D-2270 czt]# ./a.out
10 10
0x7fff93f7736c 0x7fff93f77360
20 10
0x7fff93f7736c 0x7fff93f77360

神奇的事情发生了,const内容被通过指针改动,而且同一空间的内容竟然不同
首先,局部const虽然看上去与全局常量没有什么区别,但编译器在编译时会在栈区为其分配存储空间,所以局部const称作“只读”变量更为恰当。
其次,常量在编译时默认遇到取地址时会为其分配地址
所以这里修改的只是这个副本而本身并未改变
这是编译器优化的结果。被称作常量折叠const folding

全局const

[root@IDC-D-2270 czt]# cat clzd.cpp
#include <iostream>
using namespace std;
const int a = 10;
int main(int argc, char **argv) {
    int *p = (int *) &a;
    cout << *p << " " << a << endl;
    cout << p << " " << &p << endl;
    *p = 20;
    cout << *p << " " << a << endl;
    cout << p << " " << &p << endl;
    return 0;
}
[root@IDC-D-2270 czt]# g++ clzd.cpp
[root@IDC-D-2270 czt]# ./a.out
10 10
0x400b0c 0x7fffd7e4ae78
Segmentation fault

这里会发现,全局const在同样企图通过指针修改其值时会出现段错误,也就是说const全局常量存储空间在常量存储区段上分配,这才是纯粹的只读的常量,所以当企图通过指针修改常量存储区段上的数据时会出现段错误。当然世事无绝对,通过非常手段还是可以修改常量区段的。
阅读更多…

KMP算法

KMP算法
在介绍KMP算法之前,先介绍一下BF算法。
一.BF算法
BF算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个字符和P的第二个字符;若不相等,则比较S的第二个字符和P的第一个字符,依次比较下去,直到得出最后的匹配结果。
举例说明:
S:  ababcababa
P:  ababa
BF算法匹配的步骤如下
i=0                      i=1                   i=2                 i=3                 i=4
第一趟:ababcababa         第二趟:ababcababa      第三趟:ababcababa    第四趟:ababcababa    第五趟:ababcababa
ababa                    ababa                 ababa               ababa               ababa
j=0                      j=1                   j=2                 j=3                 j=4(i和j回溯)
i=1                      i=2                   i=3                 i=4                 i=3
第六趟:ababcababa         第七趟:ababcababa      第八趟:ababcababa    第九趟:ababcababa   第十趟:ababcababa
ababa                    ababa                 ababa               ababa               ababa
j=0                      j=0                   j=1                 j=2(i和j回溯)        j=0
i=4                      i=5                   i=6                 i=7                 i=8
第十一趟:ababcababa       第十二趟:ababcababa    第十三趟:ababcababa  第十四趟:ababcababa  第十五趟:ababcababa
ababa                    ababa                 ababa               ababa               ababa
j=0                      j=0                   j=1                 j=2                 j=3
i=9
第十六趟:ababcababa
ababa
j=4(匹配成功)
代码实现:
阅读更多…

atoll的陷阱

atoll谁都知道是字符串转long long。
但是日前发现字符串里的数字超过11位竟然就溢出了,好奇怪啊。

#include <stdio.h>
int main(int argc,char**argv) {
        char bigNumStr[] = "12345678901";
        long long llNum;
        llNum = atoll(bigNumStr);
        printf("%lld\n",llNum);
        return 0;
}

看到这应该就有老鸟就笑了。没有#include 《stdlib.h》嘛。
但是为什么呢?
抠嗤了好一会,发现在没有stdlib.h的时候
编译器产生的汇编代码调用atoll的时候是这样的:
阅读更多…

getopt系列函数会重新排序argv

getopt这个东西就不多说了,讲这个有点拉低咱水平了。
直接讲重点,GNU在实现getopt等函数的时候做了个个人觉得有点画蛇添足的功能。就是在循环调用时候最后返回-1后会有可能会重新排列argv选项的顺序,他会把“不包含选项的命令行参数”挪到后面去。也就是./a.out -a ima -b host -ckeke -d haha会变成相当于./a.out -a -b host -ckeke -d ima haha的样子
所以如果选项里有一部分需要自己处理的话就没准会变得很恶心。
而optstring最前面加‘+’虽然使得argv不会被重排序,处理方式类同unix,遇到非选项的参数会停止解析,直接返回-1。
所以只得把argv复制出来一份交给getopt糟蹋了。
另外如果需要自己处理的参数排在前面的话也可以使用optind指定从哪个参数开始处理。
测试过程如下:
阅读更多…

结构体位域

有的数据在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1两种状态, 用一位二进位即可,但gcc没有bool变量,为了节省存储空间,并使处理简便,比较常见的方式是使用宏或函数的方式取变量中某些位的值,比如:

#define FLAG1(st) (st.flag&0x01)
#define FLAG2(st) (st.flag&0x02)
#define FLAG3(st) (st.flag&0x04)
#define FLAG4(st) (st.flag&0x08)
#define PROTOCOL(st) ((st.flag>>4)&0x0f)
typedef struct _stData{
    unsigned char flag;
    ... ...
}stData;

这样取值比较费劲,存起来更费劲,且不够直观。
这种情况下C语言还提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的变量用一个字节的几个二进制位域来表示。
比如我需要大量使用的结构体(如大链表的node)或在移动网络中的socket通信等环境中存储多个标记变量,就可以这样定义结构体:
阅读更多…

为啥么logrotate日志不轮转呢?_新日志size0

某懒人写个程序,要管理log,留作日后的后续功能处理和统计和领导需要。因为懒得写,所以直接用了syslog函数,把log交给rsyslog去写了。然后用logrotate每天做日志轮转。
两种log分别发送到了local6.info和local6.notice。

#define logInfo(...) pthread_mutex_lock(&logLock);\
logLen=snprintf(logBuf,sizeof(logBuf),__VA_ARGS__);\
syslog(LOG_INFO,logBuf,logLen);\
pthread_mutex_unlock(&logLock)

#define logUpload(...) pthread_mutex_lock(&logLock);\
logLen=snprintf(logBuf,sizeof(logBuf),__VA_ARGS__);\
syslog(LOG_NOTICE,logBuf,logLen);\
pthread_mutex_unlock(&logLock)
...
openlog("zhuowang_SS",LOG_PID|LOG_CONS,LOG_LOCAL6);
...

在/etc/rsyslog.conf里添加了
local6.info /var/log/zw_info.log
local6.notice /var/log/zw_notice.log
在/etc/logrotate.d/建新文件zw_log
阅读更多…

make时自动更新-v输出的build版本

其实也没啥神奇的,无非就是脚本和C宏定义。

首先,是脚本部分,Subversion包中有个svnversion命令,有了它就好办了,
在Makefile中将去版本命令写好后作为参数传给gcc/g++/cl等等之类,用-D在编译器参数中定义版本宏。
阅读更多…

c语言的smtp协议和它的mime扩展

一说写代码发邮件,好多人第一反应就是找开源库。其实看下smtp协议还是挺简单的,不简单哪对得起那个s呐。以我个人的理解这里主要有以下几个基本命令:
阅读更多…

怎样对付骚扰电话短信之程序猿篇

今天花花接到一只猥琐大叔的电话,说朋友给的号码,可以交朋友,交你妹啊交,这么直接,骗小孩儿哪?

直脾气的古人孔子曾经曰过:“以德报怨,何以报德?以德报德,以直报怨!”。

来而不往非礼也。看我小程序猿和你这猥琐大叔谁更孔子。

言归正传,用移动的童鞋都知道,移动的10086.cn可以凭短信动态密码登录,就是它!

阅读更多…