C语言初学者编程水平上不来?不妨尝试这10个C语言例子

初学者通过下面几个c语言,大家可以提高自己的编程水平,

1. 打印任意一段内存的数据

void print_array(char *title, unsigned char *data, int len) {    printf("%s:\n", title);    for (int i = 0; i < len; i++) {        // 每行开头打印当前字节的地址        if (i % 16 == 0) {            printf("0x%08X: ", (unsigned int)(data + i));        }        // 打印当前字节的十六进制形式        printf("%02X ", data[i]);        // 每行打印16个字节后换行,并打印ASCII字符        if ((i + 1) % 16 == 0 || i == len - 1) {            // 对齐填充            for (int j = 0; j < 16 - (i % 16) - 1; j++) {                printf("   ");            }            printf(" | ");            // 打印ASCII字符            for (int j = i - (i % 16); j <= i; j++) {                if (data[j] >= 32 && data[j] <= 126) {                    printf("%c", data[j]);                } else {                    printf(".");                }            }            printf("\n");        }    }    printf("----------------------------------------\n");}

2. 实现下面信令的封装和解析,只写出结构即可

名称字段个数说明起始字节1只能为0x7e命令1参数2高字节在低位,低字节在高位长度2包括子命令和数据区长度,高字节在低位,低字节在高位子命令1数据区N可变长度,最大长度为20校验字1从命令开始累计到数据区结束符1只能为0x7e

数据帧封装

int msg_send(u8 cmd, u16 param, u8 subcmd, u8 *data, u8 len){ int pos = 0; u8 crc = 0; int i,j; int status = 0; int ret = 0; u8 rawdata[MAX_FRM_LEN] = {0}; //填充7e pos = 0; rawdata[pos] = 0x7e; pos+=1; rawdata[pos] = cmd; pos+=1; setdata16(&rawdata[pos],cmd); pos+=2; setdata16(&rawdata[pos],len+1); pos+=2; rawdata[pos] = subcmd;  pos+=1; for(i=0;i

数据帧解析

用上一个程序执行结果,作为解析函数的测试数据

 u8 buf[]={0x7E,0x01 ,0x00 ,0x01 ,0x00 ,0x05 ,0x01 ,0x01 ,0x02 ,0x03 ,0x04 ,0x12 ,0x7E };
int my_check_crc(char *data){ int i; u8 crc=0; u16 datalen=0; getdata16(&datalen,&data[4]); for(i=1;i<1+6+datalen-1;i++) {  crc+=data[i]; } printf("crc=%x  %x\n",crc&0xff,data[1+6+datalen-1]);//datalen包括子命令1个字节 return (crc&0xff)==(data[1+6+datalen-1]&0xff);}int frm_parse(PENG_FRM_MSG_S *pmsg,u8 data[],int len){ int ret = -1; int pos = 0;  if(len<9) {  printf("invalid frm len %d\n",len);  return -1; } //check crc ret = my_check_crc(data); if(ret != 1) {  printf("crc check error\n");  return -1; } pos = 0; pmsg->startCode = data[pos]; pos++; pmsg->cmd = data[pos]; pos++; getdata16(&pmsg->param,&data[pos]); pos += 2; getdata16(&pmsg->len,&data[pos]); pos += 2; pmsg->subcmd = data[pos]; pos++; if(pmsg->len+8 != len) {  printf("err invalid frm len=%d\n",pmsg->len);   return -1; } memcpy(pmsg->data,&data[pos],pmsg->len-1); return 1;}int main(){ int len; int ret; PENG_FRM_MSG_S msg;  PENG_FRM_MSG_S *pmsg = &msg;  u8 buf[]={0x7E,0x01 ,0x00 ,0x01 ,0x00 ,0x05 ,0x01 ,0x01 ,0x02 ,0x03 ,0x04 ,0x12 ,0x7E }; ret = frm_parse(pmsg,buf,sizeof(buf)); printf("cmd:%x param:%x,len:%x,subcmd:%x\n",  pmsg->cmd,pmsg->param,pmsg->len,pmsg->subcmd);  print_array("[data]<<<",pmsg->data,pmsg->len-1);    return 1;}

思考:如果起始字符和结束符之间的数据如果有0x7e/0x7d需要转义为0x7d5e/0x7d5d,应该如何处理?

参考下面一篇文章 《7E头解析的那些事儿(帧格式分析实例)

3.  编写基于udp C/S架构的服务器客户端程序

  • udp_server.c
#include #include #include #include #include #include #include #include #include #include #define SERVER_PORT 8888 #define MAX_MSG_SIZE 1024 void udps_respon(int sockfd) {  struct sockaddr_in addr;  int addrlen,n;  char msg[MAX_MSG_SIZE];  while(1)  { /* 从网络上读,并写到网络上 */   bzero(msg,sizeof(msg)); // 初始化,清零  addrlen = sizeof(struct sockaddr_in);     //addr存放客户端的port ip信息  n=recvfrom(sockfd,msg,MAX_MSG_SIZE,0,(struct sockaddr*)&addr,&addrlen); // 从客户端接收消息    msg[n]=0;   /* 显示服务端已经收到了信息 */   fprintf(stdout,"Server have received %s  %s\n",msg,inet_ntoa(addr.sin_addr)); // 显示消息      sendto(sockfd,msg,strlen(msg),0,(struct sockaddr*)&addr,addrlen);  } } int main(void) {  int sockfd;  struct sockaddr_in addr;  /* 服务器端开始建立socket描述符 */  sockfd=socket(AF_INET,SOCK_DGRAM,0);  if(sockfd<0)  {   fprintf(stderr,"Socket Error:%s\n",strerror(errno));   exit(1);  }  /* 服务器端填充 sockaddr结构 */  bzero(&addr,sizeof(struct sockaddr_in));  addr.sin_family=AF_INET;  addr.sin_addr.s_addr=htonl(INADDR_ANY);  addr.sin_port=htons(SERVER_PORT);  /* 捆绑sockfd描述符 */  if(bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0)  {   fprintf(stderr,"Bind Error:%s\n",strerror(errno));   exit(1);  }  udps_respon(sockfd); // 进行读写操作 close(sockfd); } 
  • udp_client.c
#include #include #include #include #include #include #include #include #include #include #define SERVER_PORT 8888 #define MAX_BUF_SIZE 1024 void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len) {  char buffer[MAX_BUF_SIZE];  int addrlen,n;  struct sockaddr_in addr_server;   while(1)  {  /* 从键盘读入,写到服务端 */   printf("Please input char:\n");  fgets(buffer,MAX_BUF_SIZE,stdin);     sendto(sockfd,buffer,strlen(buffer),0,addr,len);   bzero(buffer,MAX_BUF_SIZE);     addrlen = sizeof(struct sockaddr);   n=recvfrom(sockfd,buffer,MAX_BUF_SIZE,0,(struct sockaddr*)&addr_server,&addrlen);  buffer[n]=0;  printf("recv:%s\n",buffer); } } int main(int argc,char **argv) {  int sockfd;  struct sockaddr_in addr;  if(argc!=2)  {   fprintf(stderr,"Usage:%s server_ip\n",argv[0]);   exit(1);  } /* 建立 sockfd描述符 */  sockfd=socket(AF_INET,SOCK_DGRAM,0);  if(sockfd<0)  {   fprintf(stderr,"Socket Error:%s\n",strerror(errno));   exit(1);  }  /* 填充服务端的资料 */  bzero(&addr,sizeof(struct sockaddr_in));  addr.sin_family=AF_INET;  addr.sin_port=htons(SERVER_PORT); if(inet_aton(argv[1],&addr.sin_addr)<0)  /*inet_aton函数用于把字符串型的IP地址转化成网络2进制数字*/  {   fprintf(stderr,"Ip error:%s\n",strerror(errno));   exit(1);  }  udpc_requ(sockfd,&addr,sizeof(struct sockaddr_in)); // 进行读写操作 close(sockfd); } 

编译:

gcc udp_server.c -o sgcc udp_client.c -o c

先运行服务器

./s

再打开一个终端,运行客户端

./c

客户端输入任意字符串会直接返回给服务器。

4. 基于步骤2/3通过udp实现信令在client/server之间传输,只写出结构即可

clien.c

#define SERVER_PORT 8888 #define MAX_BUF_SIZE 1024 typedef struct sockaddr SA;void sendfrm_thread(int sockfd,const struct sockaddr_in *addr,int len) {  u8 data[FRM_DATA_MAX_LEN]={0x1,0x2,0x3,0x4}; int addrlen,frmlen;  struct sockaddr_in addr_server;  u16 param = 0x9527; u8 rawdata[MAX_FRM_LEN] = {0};  while(1)  {       frmlen = msg_send(rawdata,MSG_TYPE_QUERY, param, 1, data, 4);    sendto(sockfd,rawdata,frmlen,0,(SA *)addr,len);     sleep(2); } } int main(int argc,char **argv) {  int sockfd;  struct sockaddr_in addr;  if(argc!=2)  {   fprintf(stderr,"Usage:%s server_ip\n",argv[0]);   exit(1);  } sockfd=socket(AF_INET,SOCK_DGRAM,0);  if(sockfd<0)  {   fprintf(stderr,"Socket Error:%s\n",strerror(errno));   exit(1);  }  bzero(&addr,sizeof(struct sockaddr_in));  addr.sin_family=AF_INET;  addr.sin_port=htons(SERVER_PORT); if(inet_aton(argv[1],&addr.sin_addr)<0)   {   fprintf(stderr,"Ip error:%s\n",strerror(errno));   exit(1);  }  sendfrm_thread(sockfd,&addr,sizeof(struct sockaddr_in));  close(sockfd); } 

server.c

void rcvfrm_thread(int sockfd) {  int ret; struct sockaddr_in addr;  int addrlen,len;  PENG_FRM_MSG_S msg;  PENG_FRM_MSG_S *pmsg = &msg;  char buf[MAX_MSG_SIZE];  addrlen = sizeof(struct sockaddr_in);  while(1) {  bzero(buf,sizeof(buf));     len=recvfrom(sockfd,buf,MAX_MSG_SIZE,0,(struct sockaddr*)&addr,&addrlen);  buf[len]=0;   ret = frm_parse(pmsg,buf,len);    printf("\n[msg]<<< cmd:%x param:%x,len:%x,subcmd:%x\n",   pmsg->cmd,pmsg->param,pmsg->len,pmsg->subcmd);    //print_array("[data]<<<",pmsg->data,pmsg->len-1);   } } int main(void) {  int sockfd;  struct sockaddr_in addr;  sockfd=socket(AF_INET,SOCK_DGRAM,0);  if(sockfd<0)  {   fprintf(stderr,"Socket Error:%s\n",strerror(errno));   exit(1);  }  bzero(&addr,sizeof(struct sockaddr_in));  addr.sin_family=AF_INET;  addr.sin_addr.s_addr=htonl(INADDR_ANY);  addr.sin_port=htons(SERVER_PORT);  if(bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0)  {   fprintf(stderr,"Bind Error:%s\n",strerror(errno));   exit(1);  }  rcvfrm_thread(sockfd); close(sockfd); } 

5. 使用数据库sqlite,将步骤4的信令insert到数据库中,

qslite操作可以参考下面文章:

《嵌入式数据库sqlite3【基础篇】-基本命令操作,小白一看就懂》

6. 用c语言执行步骤5的数据库操作指令,并将代码合并到步骤4的架构中

C语言操作数据库,可以参考:《如何用C语言操作sqlite3,一文搞懂》

7. 用任意一款抓包工具抓取一个ip数据包,保存到数组frm[]

《网络/命令行抓包工具tcpdump详解》

《一文包你学会网络数据抓包》

拷贝出数据:

80 8F 1D C7 A6 07 D8 BB C1 C8 51 C3 08 00 45 00 00 34 FC 6E 40 00 80 06 00 00 C0 A8 00 6B 8E FA C4 CA 21 AD 01 BB 25 C1 79 D5 00 00 00 00 80 02 FA F0 14 FF 00 00 02 04 05 B4 01 03 03 08 01 01 04 02

转换成数组:

unsigned char frm[]={0x80,0x8F,0x1D,0xC7,0xA6,0x07,0xD8,0xBB,0xC1,0xC8,0x51,0xC3,0x08,0x00,0x45,0x00,0x00,0x34,0xFC,0x6E,0x40,0x00,0x80,0x06,0x00,0x00,0xC0,0xA8,0x00,0x6B,0x8E,0xFA,0xC4,0xCA,0x21,0xAD,0x01,0xBB,0x25,0xC1,0x79,0xD5,0x00,0x00,0x00,0x00,0x80,0x02,0xFA,0xF0,0x14,0xFF,0x00,0x00,0x02,0x04,0x05,0xB4,0x01,0x03,0x03,0x08,0x01,0x01,0x04,0x02};

8. 提取出ip数据包的mac头,ip数据包的ip头

注意:字节序问题

mact头提取

#define MAC_FMG "%02x:%02x:%02x:%02x:%02x:%02x\n"#define printfmac(x) printf(MAC_FMG,x[0],x[1],x[2],x[3],x[4],x[5])struct mac_h{ u8 dst[6]; u8 src[6]; u16 pro;};int parse_mac_h(u8 *data,struct mac_h *pmac_h,int len){ int pos = 0;  if(len<6+6+2) {  return -1; } pos = 0; memcpy(pmac_h->dst,&data[pos],6); pos+=6; memcpy(pmac_h->src,&data[pos],6); pos+=6; pmac_h->pro = data[pos]<<8 | data[pos+1]; pos+=2;}int main(){ struct mac_h mac_header; ...... parse_mac_h(frm,&mac_header,sizeof(frm)); ...... return 1;}

执行结果如下:

ip头

#define IPADDR_FMG "%d.%d.%d.%d\n"#define printipaddr(x) printf(IPADDR_FMG,((u8*)&(x))[0],((u8*)&(x))[1],((u8*)&(x))[2],((u8*)&(x))[3])void dump_iphdr(struct iphdr *iph){ printf("ihl:%d\nversion:%d\ntos:%d\ntot_len:%x\nid:%x\nfrag_off:%x\nttl:%d\nprotocol:%d\ncheck:%x\n",  iph->ihl,  iph->version,  iph->tos,  iph->tot_len,  iph->id,  iph->frag_off,  iph->ttl,  iph->protocol,  iph->check   ); printipaddr(iph->saddr);   printipaddr(iph->daddr); }int parse_iphdr(u8 *data,struct iphdr *iph,int len){ int pos = 0;  iph->ihl = data[pos]&0xf; iph->version = data[pos]>>4; pos+=1; iph->tos = data[pos]; pos+=1; getdata16(&iph->tot_len,&data[pos]); pos+=2; getdata16(&iph->id,&data[pos]); pos+=2; getdata16(&iph->frag_off,&data[pos]); pos+=2; iph->ttl = data[pos]; pos+=1; iph->protocol = data[pos]; pos+=1; getdata16(&iph->check,&data[pos]); pos+=2; getdata32(&iph->saddr,&data[pos]); pos+=4; getdata32(&iph->daddr,&data[pos]); pos+=4; }

在这里插入图片描述

思考:为什么不可以直接用下面代码提取数据信息:

 memcpy(&mac_header,&frm[0],sizeof(struct mac_h)); memcpy(&ip_header,&frm[14],sizeof(struct iphdr));

9. 将步骤8的结构体变量赋一些初始值,并按照mac头IP头格式将结构体内容填充到一段buf中

void send_pkt(struct mac_h *mach,struct iphdr *iph,char *pdata,int datalen){ int pos = 0; char buf[14+20+1500]={0}; /*add mac头*/ pos =0 ; memcpy(&buf[pos],mach->dst,6); pos+=6; memcpy(&buf[pos],mach->src,6); pos+=6; setdata16(&buf[pos],mach->pro); pos+=2;  /*add ip头*/ buf[pos] = (char)(iph->ihl | iph->version<<4); pos+=1; buf[pos] = iph->tos; pos+=1; setdata16(&buf[pos],iph->tot_len); pos+=2; setdata16(&buf[pos],iph->id); pos+=2; setdata16(&buf[pos],iph->frag_off); pos+=2; buf[pos] = iph->ttl; pos+=1; buf[pos] = iph->protocol; pos+=1; setdata16(&buf[pos],iph->check); pos+=2; setdata32(&buf[pos],iph->saddr); pos+=4; setdata32(&buf[pos],iph->daddr); pos+=4;  memcpy(&buf[pos],pdata,datalen); pos+=datalen;  print_array("\nfrm",buf,pos);}main(){... datalen = ip_header.tot_len; memcpy(data,frm + sizeof(struct mac_h)+sizeof(struct iphdr),datalen-20); send_pkt(&mac_header,&ip_header,data,datalen-20);...}

10. 将3步骤的结构体变量保存到文件,并读取出来,

 void save_head(struct mac_h *mach,struct iphdr *iph,char *pdata,int datalen){ int fd  =-1; int len = 0; struct mac_h mac_head_test; struct iphdr ip_head_test; char buf[1500]; fd  =open(FAILE_NAME,O_RDWR|O_CREAT); if(fd < 0) {  perror("open fail\n");  return; } write(fd,mach,sizeof(struct mac_h)); write(fd,iph,sizeof(struct iphdr)); write(fd,pdata,datalen); lseek(fd,0,SEEK_SET); len = read(fd,&mac_head_test,sizeof(struct mac_h)); len = read(fd,&ip_head_test,sizeof(struct iphdr)); len = read(fd,buf,1500); buf[len] = '\0'; printf("------recover from file--------------\n"); dump_machdr(&mac_head_test); dump_iphdr(&ip_head_test);  print_array("\ndata",buf,len);}save_head(&mac_header,&ip_header,data,datalen-20);

完整代码可从附件中获取。

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
本篇所含全部资料,点击此处留下邮箱我会发给你
资料明细:C语言初学者编程水平上不来?不妨尝试这10个C语言例子.txt
觉得内容不错的朋友,别忘了一键三连哦!
赞 1
收藏 2
关注 180
成为作者 赚取收益
全部留言
0/200
  • zhoucwyh 03-22 22:08
    老师,能不能发我一下资料,谢谢! zh****@****.com
    回复 1条回复