C

[toc]

python输出内容,然后用C将其读出

python脚本: a.py

# coding =utf-8
import os,sys,time,glob,traceback,gc,re,string,stringgrep
import time,socket,random,unittest

reload(sys)
sys.setdefaultencoding("utf-8")
os.system("echo 3900/48412 line\(44.7\) Branch\(43.9%\)>out.txt"

C 代码

#include <stdio.h>
#include <stdlib.h>
int main()
{
    char CoverageOut[128] = {0};
    system("python2.7.exe a.py");
    FILE *pFile = fopen("out.txt", "r");
    if (NULL == pFile)
    {
        printf("failed to open out.txt\n");
        return 0;
    }
    fread(CoverageOut, 1024, 1, pFile);
    printf("Coverage Out is %s\n", CoverageOut);
    if (pFile)
    {
        fclose(pFile);
    }
    return 0;
}

alarm 函数的应用

linux代码段

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
static void sig_alarm(int sigNo)
{
    printf("in sig_alarm, paraIn = %d\n", sigNo);
    system("date");   //注意退出时,打印的时间间隔
    return;
}

int main(void)
{
    signal(SIGALARM, sig_alarm); //注册函数,时间到则调用此函数
    system("data"); //程序启动时,先打印一个基准时间
    printf("alarm(4) = %d\n", alarm(4)); //如果没有下面的sleep(1)和alarm(8)两行代码,则过4秒打印并跳过pause()继续执行
    sleep(1);  //这个时间被统计进alarm()延时中,需要加上sleep值
    alarm(8); //如果同时开启alarm(4) 和alarm(8),则以长的时间为准
    pause();//在C语言中,如果没有alarm()语句,则会一直卡在这里
    printf("print before end ...\n"); // 在9秒之后,继续从pause的地方执行
}
在这个场景中,如果所注册的 sig_alarm函数中,调用了exit(0)函数,则程序提前退出。同样如果同时调用多个alarm()函数,则以时间最长的为准。

最能体现代码执行时序的例子:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void handler() {  printf("hello!\n");
void main()
{
    int i = 0;
    signal(SIGALRM,handler);
    alarm(3);
    for (i = 1; i < 5; i ++)
    { printf("sleep %d ...\n", i); sleep(1);}
}

编译动态链接库,示例项目

a.c:  int a= 1;
b.c:  int b = 1;
c.c : int c = 1;
main.c:
#include <stdio.h>
extern int a;
extern int b;
extern int c;
int main()
{
    printf("a=%d b=%d c=%d\n", a, b, c);
    return 0;
}
第一步:编译动态库
     gcc -shared -fpic a.c b.c c.c
     gcc -shared -fpic -o libprog.so  a.o b.o c.o
   或者将两行合并成一行写
     gcc -shared -fpic a.c b.c c.c -o libprog.so
 第二步,编译可执行文件
    gcc main.c -o main -L./   ./libprog.so  ==>>运行成功
    gcc main.c -o main -L./   libprog.so  ==>>运行失败???
另外,链接关键字 -fpic 换成 -fPIC 同样运行成功,具体原理看过,没完全理解,现在又忘了
其它写法,能编译成功,但是运行失败:
    gcc main.c -o main -L ./ -lprog ==>>多标准的写法,运行不了</stdio.h>

C语言解析CSV的正确打开方式

char *pBuff = NULL;
char fileBuff[4*1024*1024] = {0};
FILE *fp = fopen("./a.csv", "r");
//以字符为单位:1,最多读取4*1024*1024个长度
ret = fread(fileBuff, 1, 4*1024*1024, fp);
pBuff = strtok(fileBuff, "\t");  //将csv copy出来,它是以tab间隔的,所以要以"\t"来分割
pBuff = strtok(fileBuff, ","); //csv原文件是以逗号间隔的,所以要以","来分割
pBuff = strtok(fileBuff, "\r\n"); //csv原文件一般包含多列,最后一列肯定要以换行符来结束
if (pBuff)
{
    strcpy(yourBuff, pBuff);
    printf("yourBuff is : %s\n", yourBuff);
}
else
{  //一般都包含多行,所以需要在一个循环中处理
    break;
}
而且第一个元素需要从总buffer中读取,后续所有的strtok操作,第一个参数都是NULL.

gcc 中的 weak 关键字

1、当没有遇到“强”函数时的效果

aa.c:
#include <stdio.h>
__attribute__((weak)) void fun1(int a)
{
    printf("a=%d\n", a);
}
int main()
{
    fun1(3);
    return 0;
}

此时,执行: gcc aa.c -o a.out ; ./a.out
得到的输出是a=3

2、当遇到“强”函数时,立即失效,被别人代替

bb.c:
#include <stdio.h>
void fun1(int a)
{
    printf("new a=%d\n",a*3);
}

此时执行: gcc aa.c bb.c -o a.out; ./a.out
得到的输出是 new a=9

栈,调试 https://blog.csdn.net/hh012938/article/details/78375458

1、backtrace 
一些内存检测工具如Valgrind,调试工具如GDB,可以查看程序运行时函数调用的堆栈信息,有时候在分析程序时要获得堆栈信息,借助于backtrace是很有帮助的,其原型如下:
#include <execinfo.h>
int backtrace(void **buffer, int size); 
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
头文件“execinfo.h”提供了三个相关的函数,简单的说,backtrace函数用于获取堆栈的地址信息, backtrace_symbols函数把堆栈地址翻译成我们易识别的字符串, backtrace_symbols_fd函数则把字符串堆栈信息输出到文件中


backtrace:该函数用于获取当前线程的函数调用堆栈,获取的信息将存放在buffer中,buffer是一个二级指针,可以当作指针数组来用,数组中的元素类型是void*,即从堆栈中获取的返回地址,每一个堆栈框架stack frame有一个返回地址,参数 size 用来指定buffer中可以保存void* 元素的最大值,函数返回值是buffer中实际获取的void*指针个数,最大不超过参数size的大小。 
backtrace_symbols:该函数把从backtrace函数获取的信息buffer转化为一个字符串数组char**,每个字符串包含了相对于buffer中对应元素的可打印信息,包括函数名、函数的偏移地址和实际的返回地址,size指定了该数组中的元素个数,可以是backtrace函数的返回值,也可以小于这个值。需要注意的是,backtrace_symbols的返回值调用了malloc以分配存储空间,为了防止内存泄露,我们要手动调用free来释放这块内存。 
backtrace_symbols_fd:该函数与backtrace_symbols 函数功能类似,不同的是,这个函数直接把结果输出到文件描述符为fd的文件中,且没有调用malloc。 
在使用以上三个函数时,还需要注意一下几点: 
(1)如果使用的是GCC编译链接的话,建议加上“-rdynamic”参数,这个参数的意思是告诉ELF连接器添加“-export-dynamic”标记,这样所有的符号信息symbols就会添加到动态符号表中,以便查看完整的堆栈信息。 
(2)static函数不会导出符号信息symbols,在backtrace中无效。 
(3)某些编译器的优化选项对获取正确的函数调用堆栈有干扰,内联函数没有堆栈框架,删除框架指针也会导致无法正确解析堆栈内容。
下面是一个简单的例子

//backtrace_ex.cpp
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h> 
void my_backtrace()
{
    void *buffer[100] = { NULL };
    char **trace = NULL;
    int size = backtrace(buffer, 100);
    trace = backtrace_symbols(buffer, size);
    if (NULL == trace) {
        return;
    }
    for (int i = 0; i < size; ++i) {
        printf("%s\n", trace[i]);
    }
    free(trace);
    printf("----------done----------\n");
}
void func2()
{     my_backtrace(); 
} 
void func()
{     func2();
} 
int main()
{     func();     return 0;
}
编译执行上面的文件
g++ backtrace_ex.cpp
./a.out
./a.out() [0x400811]
./a.out() [0x400baf]
./a.out() [0x400bba]
./a.out() [0x400bc5]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f2473cf5ec5]
./a.out() [0x400709]
----------done----------

咦!堆栈信息虽然打出来了,但是函数调用栈并不是很明确,原因是少了“-rdynamic”参数,重新编译执行如下:
g++ -rdynamic backtrace_ex.cpp
./a.out
./a.out(_Z12my_backtracev+0x44) [0x400b11]
./a.out(_Z5func2v+0x9) [0x400eaf]
./a.out(_Z4funcv+0x9) [0x400eba]
./a.out(main+0x9) [0x400ec5]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f006bdfbec5]
./a.out() [0x400a09]
----------done----------
加了“-rdynamic”参数后就很好了,我们可以看到函数名称,由于不同的平台、编译器有不同的编译规则,所以用backtrace解析出来的函数名形式是不同的,以“./a.out(_Z4funcv+0x9) [0x400eba]”为例说明,重点在于圆括号中的内容,“_Z”是个函数名开始标识符,后面的“4”表示函数名长度,接着便是真正的函数名“func”,后面的“v”表示函数参数类型为void,随后的“+0x9”是偏移地址。虽然有一定的编译规则,但可读性还不是很好,我们可以用下面介绍的方法demangle来解析这些符号

2、demangle 
demangle即符号重组,函数原型如下:
#include <cxxabi.h>
char* __cxa_demangle(const char* __mangled_name,
                     char* __output_buffer,
                     size_t* __length,
                     int* __status);

cxxabi.h是一个C++函数运行时库,要用g++编译链接,gcc会有问题。__mangled_name即原符号信息,是个字符串,以空字符结尾,__output_buffer用来保存符号重组后的信息,长度为__length,__status表示demangle结果,为0时表示成功,返回值指向符号重组后的字符串首地址,字符串以空字符结尾。 
我们使用demangle来改进上面的例子:(把my_backtrace替换为my_backtrace2)
void my_backtrace2()
{
    void *buffer[100] = { NULL };
    char **trace = NULL;
    int size = backtrace(buffer, 100);
    trace = backtrace_symbols(buffer, size);
    if (NULL == trace) {
        return;
    }
    size_t name_size = 100;
    char *name = (char*)malloc(name_size);
    for (int i = 0; i < size; ++i) {
        char *begin_name = 0;
        char *begin_offset = 0;
        char *end_offset = 0;
        for (char *p = trace[i]; *p; ++p) { // 利用了符号信息的格式
            if (*p == '(') { // 左括号
                begin_name = p;
            }
            else if (*p == '+' && begin_name) { // 地址偏移符号
                begin_offset = p;
            }
            else if (*p == ')' && begin_offset) { // 右括号
                end_offset = p;
                break;
            }
        }
        if (begin_name && begin_offset && end_offset ) {
            *begin_name++ = '\0';
            *begin_offset++ = '\0';
            *end_offset = '\0';
            int status = -4; // 0 -1 -2 -3
            char *ret = abi::__cxa_demangle(begin_name, name, &name_size, &status);
            if (0 == status) {
                name = ret;
                printf("%s:%s+%s\n", trace[i], name, begin_offset);
            }
            else {
                printf("%s:%s()+%s\n", trace[i], begin_name, begin_offset);
            }
        }
        else {
            printf("%s\n", trace[i]);
        }
    }
    free(name);
    free(trace);
    printf("----------done----------\n");
}
结果如下:
g++ -rdynamic backtrace_ex.cpp
./a.out
./a.out:my_backtrace2()+0x44
./a.out:func2()+0x9
./a.out:func()+0x9
./a.out:main()+0x9
/lib/x86_64-linux-gnu/libc.so.6:__libc_start_main()+0xf5
./a.out() [0x400a09]
----------done----------
可以看出来,demangle后函数名已清晰地显示出来了,没有那些奇奇怪怪的符号了。

C 语言正则表达式

1、先上一段代码

#include <stdio.h>
#include <sys/types.h>
#include <regex.h>
#include <memory.h>
#include <stdlib.h>

int main(){
    char *bematch = "hhhericchd@gmail.com";
    char *pattern = "h{3,10}(.*)@.{5}.(.*)";
    char errbuf[1024];
    char match[100];
    regex_t reg;
    int err,nm = 10;
    regmatch_t pmatch[nm];

    if(regcomp(®,pattern,REG_EXTENDED) < 0){
        regerror(err,®,errbuf,sizeof(errbuf));
        printf("err:%s\n",errbuf);
    }

    err = regexec(®,bematch,nm,pmatch,0);

    if (err == REG_NOMATCH){
        printf("no match\n");
            exit(-1);
    }
    else if(err) {
        regerror(err,®,errbuf,sizeof(errbuf));
        printf("err:%s\n",errbuf);
        exit(-1);
    }

    for(int i=0;i<10 && pmatch[i].rm_so!=-1;i++) {
        int len = pmatch[i].rm_eo-pmatch[i].rm_so;
        if(len) {
            memset(match,'\0',sizeof(match));
            memcpy(match,bematch+pmatch[i].rm_so,len);
            printf("%s\n",match);
        }
    }
    return 0;
}

2、编译运行效果

[Administrator.WINDOWS-LGJ801D] ➤ gcc regExp.c -o ppc
[Administrator.WINDOWS-LGJ801D] ➤ ./ppc
hhhericchd@gmail.com
ericchd
com

3、代码理解及验证

A、重复字符匹配

char *pattern = "h{3,10}(.*)@.{5}.(.*)";
显然是核心,这是一个正则表达式语法.
h{3,10} ==>>表示至少有三个'h'重复,最多10个,以此为开始标记,第11个'h'将被看作普通字符

将 char *bematch = "hhhericchd@gmail.com"; 改为两个'h'开头:"hhericchd@gmail.com"
重新编译运行:
[Administrator.WINDOWS-LGJ801D] ➤ gcc regExp.c -o ppc && ./ppc.exe
no match


再改为5个'h'开头"hhhhhericchd@gmail.com":
[Administrator.WINDOWS-LGJ801D] ➤ gcc regExp.c -o ppc && ./ppc.exe
hhhhhericchd@gmail.com
ericchd
com

再改为10个以上连续'h'开头:"hhhhhhhhhhhhhhhhericchd@gmail.com"
[Administrator.WINDOWS-LGJ801D] ➤ gcc regExp.c -o ppc && ./ppc
hhhhhhhhhhhhhhhhericchd@gmail.com
hhhhhhericchd
com

B、指定个数字符,不作为匹配输出

显然,输出中 ".gmail." 被 ".{5}." 取代

C、输出匹配的内容规则

[0] 表示全匹配字符串
[1] 表示第一个 "(.*)" 匹配成功的内容
[2] 表示第二个 "(.*)" 匹配成功的内容
"尤其注意:
    必须用括号括起来的,才能作为子匹配项。
"

但是有一个神奇的事情,空格的字符串长度竟然为0

D、加戏,增加多场景匹配

一个神奇的事情,空格的字符串长度竟然为0
    char *bematch  = "hhhhhhhhhhhhhhhhericchd@gmail.com";
    char *bematch2 = "hhhhhhhhhhhericchd @gmail.com";
    char *bematch3 = "hhhhhhericchd  @gmail.com";
    char *pattern = "h{3,10}(.*)([ ]*)@.{5}.(.*)";
想要匹配'@'、' @'、'  @'即任意空格和'@'连接的情况,第三个将输出也改一下:
    for(int i=0;i<10 && pmatch[i].rm_so!=-1;i++) {
        int len = pmatch[i].rm_eo-pmatch[i].rm_so;
//        if(len) {
            memset(match,'\0',sizeof(match));
            memcpy(match,bematch+pmatch[i].rm_so,len);
            printf( "match %d: %s\n",i , match);
//        }
    }
则编译运行效果如下:

[Administrator.WINDOWS-LGJ801D] ➤ gcc regExp.c -o ppc && ./ppc
match 0: hhhhhhhhhhhhhhhhericchd@gmail.com
match 1: hhhhhhericchd
match 3: com
match 0: hhhhhhhhhhhhhhhhericchd@gmail
match 1: hhhhhheri
match 3: ail
match 0: hhhhhhhhhhhhhhhhericchd@g
match 1: hhhhhhhhh
match 2:
match 3: d@g

4、在经历了一段时间项目经验后,认识更全面,经验更老道一点

1、排除空格,高风险地方

比如有如下脚本:
  [root]$ ssh 127.0.0.1 -l userName -p 2222
要提取出 `ip / userName / 2222`这三个关键字,可以这样写
char *pattern = "ssh[ ]{1,}(.*)[ ]{1,}-l[ ]{1,}(.*)[ ]{1,}-p[ ]{1,}(.*)[ ]{0,}";
讲解:[ ]{1,} ==>> 表示它后面至少有一个空格,可能有多个
     (.*)    ==>> 是最终
     [ ]{0,} ==>> 最后的一个,是为了清除空格,不被(.*)匹配上
     但是这种写法有缺陷,某些时候,不知道为什么最后的空格没有清除掉,被匹配进最后一个项里面

改进写法:
   char *pattern = "ssh[ ]{1,}([^ ]+)[ ]{1,}-l[ ]{1,}(.*)[ ]{1,}-p[ ]{1,}([^ ]+)";
讲解:用 `([^ ]+)` 代替`(.*)`,可完美避开空格,尤其是最后一项,可以不用考虑末尾的空格影响

带上小括号的,就是将被匹配上的内容,唯一例外就是全字符,就是如果匹配
成功,全字符将被告放入匹配数组 match[0],其它依次放入
match[1]/match[2]

2、开关匹配情况

如:
  [root]$ status on
  [root]$ status off
两种情况都应该被匹配上,正确写法是
char *pattern = "status[ ]{1,}(on|off)[ ]{0,}";
这样 match[0]为全字符,match[1]为 on 或者 off

5、其它人的总结,如下内容不全正确,可以批判接受

字 符 意 义 示 例
* 任意长度的字符串。 a* 表示: 空字符串、aaaa、a…
? 长度为0或者1的字符串。 a? 表示: 空字符串和a。
+ 长度为一个或者多个的字符串。 a+表示:a、aa、aaaaaa…
. 任意字符。 a. 表示:a后跟任意字符。
{} 代表上一规则重复数目
{1,1,s}包含一组匹配花括号,里面有两个数字和一个字符,表示在指定次数范围内找到字符。 
a{3}表示:三个a、
a{1,3}表示:一个到三个a、
a{3,} 表示:大于等于三个a、
{3,7,a}表示在3到7次重复范围内匹配字符a。
[] 集合,代表方括号中任意一个字符。 
[ab] 表示:a或者b都可以
[a-z] 表示:从a到z的字符。
() 组,代表一组字符。 (ab){2}表示:abab。
a/b 同时满足。 a/b表示:字符串a后跟字符串b才能满足要求。
a|b 并列,代表符合a或者符合b都可以,this|that表示: 字符串this或者字符串that都满足要求。
^ 如果放在开头表示代表该规则必须在字符串的开头,其他位置代表字符本身。
  如果放在[]中的开头表示对该集合取反,其他位置代表字符本身。
^a表示:a必须在字符串的开头
[^a]表示:除了a以外的其他字符。
$ 如果放在最后表示该规则必须放在最后,其他位置代表字符本身。 a$表示:a必须在字符串最后。
/:s 正则表达式用 /:s 表示空格。 a/:sb 匹配 a b。
  ==>>根据本人实践,/:s代表空格失败,直接采用[ ]表示空格是成功的
/:a 正则表达式用 /:a 表示字符与数字。 a/:a 匹配 ab、a6 等。
/:c 正则表达式用 /:c 仅表示字符。 a/:c 匹配 ac等,不匹配a1等。
/:p 正则表达式用 /:p 表示可打印字符。
/:D 正则表达式用 /:d 仅表示数字。 a/:c 匹配 a1等,不匹配ac等。
/:x00 正则表达式用 /:x00 表示ASCII字符。
/:r 正则表达式用 /:r 表示回车。
/:N 正则表达式用 /:d 表示换行。

C/C++ 中的单子节对齐问题

有两种方式:
1、pragma
2、__attribute__((packed))
一般为了阅读方便,代码美观,会这样处理:
#define MPACK __attribute__((packed))
typedef struct
{
    char c;
    int i;
}MPACK myStruct;
  这样就实现了单子节对齐

编译静态库和动态库

编译静态库

#正常情况下,就直接将 object 文件链接一起编译生成可执行程序就行了
gcc -c funTest.c
gcc -o testMain testMain.c funTest.o //库文件形式下
gcc -c funTest.c //编译生成funa.o
ar -rsv libfunTest.a funTest.o //ar指令, 编译生成静态库文件
gcc -o testMain testMain.c -L./ -lfunTest //链接静态库文件,生成可执行文件

动态链接库的编译与使用

// 动态库编译生成
gcc -o libtest.so -fPIC -shared funa.c
gcc -o testMain testMain.c ./libtest.so //直接指定动态库位置

C 语言彩色打印

彩色定义

#define NONE "\033[m"
#define RED "\033[0;32;31m"
#define LIGHT_RED "\033[1;31m"
#define GREEN "\033[0;32;32m"
#define LIGHT_GREEN "\033[1;32m"
#define BLUE "\033[0;32;34m"
#define LIGHT_BLUE "\033[1;34m"
#define DARY_GRAY "\033[1;30m"
#define CYAN "\033[0;36m"
#define LIGHT_CYAN "\033[1;36m"
#define PURPLE "\033[0;35m"
#define LIGHT_PURPLE "\033[1;35m"
#define BROWN "\033[0;33m"
#define YELLOW "\033[1;33m"
#define LIGHT_GRAY "\033[0;37m"
#define WHITE "\033[1;37m"

简单应用示例

#include <stdio.h>
#define RED    "\033[0;32;31m"
#define NONE   "\033[m"
#define YELLOW "\033[1;33m"
int main()
{
    printf(RED" the red!\n"NONE);
    printf(YELLOW" the yello!\n"NONE);
    return 0;
}

对 printf 参数进行封装

printf(YELLOW" he is %d years old!\n"NONE, 99);

用法用法:
char buff[512]={0};
sprintf(buff,"this is a print content,num=%d", 99);
colorPrint(YELLOW, buff);

最后封装

typedef enum {
    eRED,
    eLIGHT_RED,
    ...
    eLIGHT_GRAY,
    eWHITE,
}eColor;
int colorPrint(eColor color, const char *format, ...)
{
    char content[512 + 1] = {0};
    va_list argptr;
    memset((char *)&argptr, 0, sizeof(va_list));
    va_start(argptr, format);
    vsnprintf(content, 512, format, argptr);
    va_end(argptr);
    if (eRED == color)
    {
        printf(RED "%s" END, content);
    }
    else if(eLIGHT_RED == color)
    {
        printf(LIGHT_RED "%s" END, content);
    }
    ...
    else if (eWHITE == color)
    {
        printf(WHITE "%s" END, content);
    }
    else
    {
        printf("%s", content);
    }
    return 0;
}
用法:
colorPrint(eRED, "myname:%s,age:%d",name, 99);

怎样输出百分号:‘%’

int main()
{
    printf("%%%s%%\n","xx");
    return 0;
}

C 语言中调用shell语句(linux C)

1、亲自编写的代码

#define ERROR_CODE (1<<8)
void test()
{
    char cmdBuff[512] = {0};
    sprintf(cmdBuff, "grep -rni %d ${PWD%%KEY*}/KEY/file.c || exit 1");
    if (ERROR_CODE ==system(cmdBuf))
    {
        return 1;
    }
    return 0;
}

int ret =  system("exit 1");
则 ret=(1<<8),0x100,256

int ret =  system("exit 2");
则 ret=(2<<8),0x200,512

int ret =  system("exit 3");
则 ret=(1<<8),0x300,768

但是126、127有特殊用途,一般不返回这两个值

2、转链接

https://blog.csdn.net/linluan33/article/details/8097916

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注