菜鸟的日常总结

.LOG
SQL是指结构化的查询语句:
-是一种面向数据库的通用数据处理语言规范。
-能够完成:
  提取查询数据
  插入修改删除数据
  生成修改和删除数据库对象
  数据库安全控制
  数据库完整性及数据保护控制。
结构化,语言组织形式是结构化的。即,语言的使用是有固定套路的,所以叫结构化。
SQL是一种ANSI的标准计算机语言
-这样就使我们在学习基本sql语句的基础上,轻易的访问所有数据
-当然各类型数据库还是有差异的。SQL语句是各类型数据的最小集合。
SQL可以创建新的数据库
增删改插
sqlite3


.show databases ;


启动mysql:
进入/usr/bin/mysql
service mysqld start
查看数据库中的表
-show tables
创建表
-create table 表名(字段参数);
-create table if not exists表名(字段参数)
删除一个久表
-drop table 表名;
-drop table if exits;
查看表的结构:
desc 表名称


查看手册toolbutton


显示图片的语句:
QString filename = list.at(this->number);
QImage image(filename);
imageLabel->stePixmap(QPixmap::fromImage(iamge));


SQLite数据库
.tables显示当前表格
sqlite3 text.tb
select * from std




增删改查




20:38 2017/3/7
/*****************************************************************************************************/
int *p[3];与int (*p)[3];的区别:
先判断其优先级,[]和()的优先级都比*的优先级大,不加()表明是一个
数组,即int* p[3],三个元素中存放的是int类型的地址;加上()时,表
明p是指向了一个有三个元素的数组的地址。
int (*p)[3];//定义了一个指针,这个指针指向int[3]这种数据类型;


10:24 2017/3/8


指针可以作为函数传递的参数,C语言都是值传递,既然是值传递
,就表明传递得到的是实参的数值,其数值赋给了形参,所以不能改变
实参的值,因为实参和形参是两个不同的变量。
例:
void test(int *n)    //传进去的是一个地址
{
   (*n)++;         //++和*的优先级一样,结合方式是自右向左


}
int main(int argc,char *argv[])
{


int a = 100;
test(&a);
printf("%d\n",a);


return 0;
}
所以需要在函数内部修改实参的值都需要指针作为参数,传递其地址,
再修改地址里面的值来改变实参的值。




11:23 2017/3/8
/*****************************************************************************************************/
数组名代表的是数组的首地址,所以可以传递数组名来改变实参的值
二维数组作为参数传递时,其定义方式如下


  4 void test(int (*p)[3])
  5 {
  6     int i;
  7     int j;
  8     for(i = 0;i<2;i++)
  9         for(j = 0;j<3;j++)
 10           printf("a[%d][%d] = %d\n",i,j,*(*(p+i)+j));
 11 }
 12 
 13 int main(int argc,char *argv[])
 14 {
 15    int a[2][3] = {{1,2,3},{4,5,6}};
 16    test(a);
 17 
 18     return 0;
 19 }
/*****************************************************************************************************/
char *p与 char p[]非等效:(指针类型不能修改值,但可以修改指向的地址,但数组类型的可以修改内容.)
     是有区别的,char *p="abcdef";声明一个指针,p在栈,“abcdef”在静态数据存储区,不可写,
p存储的是“abcdef”的首地址,p指向“abcdef”;char p[]="abcdef";声明一个字符串数组,p在栈,“abcdef”在栈区,
数据可以修改重写;
数组本身有存储空间,数据就放在数组里面可以修改。而上面的指针由于本身没有存储空间,数据时放在只读存储区的而指
针取得的只是他的首地址而已。所以要通过指针对只读存储区的数据进行修改是不行的。
/*****************************************************************************************************/
20:40 2017/3/8


19:13 2017/3/9
用const关键字保护数组


c语言指针最高级的应用,指向函数的指针。
指向函数的指针:
所有的函数也是有地址的
定义方法:int(*p)(int,int);定义了一个指向函数的指针
例如:
int add(int a,int b)
{
 return a+b;
}
int max(int a,int b)
{
   return a>b?a:b;   
}
int main()
{
    int (*p)(int,int);//定义一个指向函数的指针
    int status = 0;
    scanf("%d",&status);
    if(status == 1)
    p = add;          //函数名就是函数的地址
    else
    p = max;
    int i = 0;
    i = p(5,7);       //通过之指针变量间接的调用指针指向的函数
    printf("i = %d\n",i);
    return 0;
}
意义:使用指针指向函数指针可以动态的调用函数,这个p在编译的时候
不知道要调用什么函数。
对二级指针和多级指针的理解:
一级指针指向一个变量的地址,二级指针就指向了一级指针,注意这里
用的是指向来描述可以方便理解


#pragma warning(disable:4996)//屏蔽错误




定义一个指向函数的指针时一定要与函数的类型(参数和返回值的类型)相同。


void *p(int,char*):
 声明了一个函数,函数的返回值是void*,函数名是p,函数的参数是int和char*
void (*p)(int,char *):
定义了一个指针,这个指针指向的是一个参数为int和char*,返回值是void类型的函数
//带括号的是指针,不带括号的是函数
int*(*p)(int*):定义一个参数为int * 返回值为int *的指向函数的指针
void (*p)():
定义一个指向返回类型为void,没有参数函数的指针;
多态:底层函数是通过指向函数的指针来实现的。
在回调函数和运行期动态绑定的时候大量的使用了指向函数的指针




21:11 2017/3/9
用指向函数的指针做参数:
int add(int a,int b)
{
return a+b;
}
int funcl( int(*p)(int,int),int a,int b)//第一个参数是指向函数的指针
{
  return p(a,b);//通过指向函数的指针调用一个函数
}
int main()
{
   int i = funcl(add,6,9);//add函数在这里就叫做回调函数
   printf("i = %d\n",i);
   return 0;
}
什么是回调函数:就是在程序的运行过中,通过某个信号,例如鼠标
点击,来调用之前并不知道要调用的函数,叫做回调。




/*****************************************************************************************************/
内存操作函数:


memset(buf,0,sizeof(buf));
第一个参数是要设置的内存地址,第二个参数是要设置的大小,第三个参数是buf
的字节大小(内存大小),单位是字节
//将一块内存初始化为0最常用的方法。
memcpy(buf2,buf1,sizeof(buf1));
将buf1的内存内容全部拷贝的buf2,拷贝大小为第个参数
memmove(buf2,buf1,sizeof(buf1));
把buf1的内容移到buf2。(buf1和buf2的内容是一样的)
内存拷贝和内存移到的区别:内存移动并没有改变原地址的值。
使用内存拷贝memcpy一定要确保没用内存重叠区域。


/*****************************************************************************************************/
指针总结:
int i;          //定义一个int类型的变量
int *p;         //定义一个指向int类型的指针,,注意指针的大小是在同一计算机内是相同的,32位是4个字节,64位是8个字节
int a[10];      //定义一个数组,a数组名代表的是这这个数组的起始地址
int *p[10];     //结合方式是自右向左,定义了一个数组,这个数组有10个元素,存放的是int了类型的地址(存放地址的数组)
int (*p)[10];   //定义了一个指针,这个指针指向了含有10个元素的数组,移动一个移动40个字节
int func();     //定义了一个函数,函数名是func,返回类型是int,没有参数
int *func();    //定义了一个函数指针,返回类型是int*,即返回一个地址,没有参数,函数名是func
int (*p)();     //定义了一个指针p,这个指针指向一个返回类型是int,没有参数的函数
int **p;        //定义了一个二级指针










12:54 2017/3/10
/*****************************************************************************************************/
18:59 2017/3/11
通过指针数组成员来访问指针的值。
例如:
 5 int main(int argc,char *argv[])
  6 {
  7 int *p[3];
  8 int a = 0;
  9 int b = 0;
 10 int c = 0;
 11 p[0] = &a;
 12 p[1] = &b;
 13 p[2] = &c;
 14 
 15 *p[0] = 10;
 16 printf("%d\n",a);
 17     return 0;
 18 }






要注意指针数组和数组指针,指针数组是数组,其每个元素中存放的是
地址,数组指针是指针,该指针指向了一个数组。
指针数组的定义方式:
int *p[3];
数组指针的定义方式:
int (*p)[3];
数组指针大多数情况下用来指向一个二维数组。指向的二维数组的第一维
要与数组指针向的数组的元素数相同。




%p打印出来地址。


交换的方法:
第一种:
void swap(int *a,int *b)
{
   int *temp = *a;
   *a = *b;
   *b = *temp;
}
第二种:
void swap(int *a,int *b)
{
   *a = *a + *b;
   *b = *a - *b;
   *a = *a - *b;
}


字符串是明确的以'\0'为结尾的,用以与别的字符做区分。




主函数是操作系停调用的,
int main(int argc,char *argv[])
其中,调用的时候有几个参数,argc的值就是几。argc代表程序执行的
时候有几个参数。程序名称本身就是一个参数,所以argc最小值是1.
第二个参数是一个指针数组,其中每个成员的类型是char*。
argc这个参数的作用就是告诉main函数argv这个数组有多少成员的。


atoi是一个把字符串转化为整形的函数。




通过main函数命令写计算器的方法:
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 
  4 int main(int argc,char * argv[])
  5 {
  6    if(argc != 4)
  7    {
  8          printf("参数错误,程序退出!");
  9          return 0;
 10    }
 11    int a =atoi( argv[1]);
 12    int b =atoi( argv[3]);
 13    char *s = argv[2];//注意argv里面的元素放的是字符串,因为argc数组里面放到都是地址,>    所以也要定义一个char类型的指针变量来指向argv[2]中存放的地址
 14    char c = s[0]; //含义是把argc这个指针数组的第二个元素字符串的第一个字符赋值给c
 15    switch(c)      //这里需要传入一个int类型的变量,而charc本身就是传递的时候会以ascii    码的值传递进去    
 16    {
 17        case '+':c = a + b;break;
 18        case '-':c = a - b;break;
 19        case '*':c = a * b;break;
 20        case '/':c = a / b;break;
 21        default:break;
 22    }
 23    printf("c = %d\n",c);
 24     return 0;
 25 }






21:36 2017/3/11
c++:




  1 #include<iostream>       //包含c++头文件 
  2 
  3 using namespace std;     //使用命名空间std标准的命名空间(在这个命名空间中定义了很多标准定义)
  4 
  5 int main()
  6 {
       //cout 标准输出黑屏
       //<<左移操作符 在c++里面 功能的改造(增强) ===>c++语言操作符重载
       //endl \n
  7    cout << "hello....." <<endl;
  8     return 0;
  9 }   






9:04 2017/3/12




/*****************************************************************************************************/
10:02 2017/3/12
/*****************************************************************************************************/
QT:udp
tcdap
/*****************************************************************************************************/
20 :06 2017/3/15
/*****************************************************************************************************/
public表明该数据成员、成员函数是对所有用户开放的,所有用户都可
以直接进行调用
private表示私有,私有的意思就是class内的可以直接用,别的都不可
以直接使用,相当于私有财产,只能自己使用,除了自己,都不可以使
用。
protected对于子女、朋友来说,就是public的,可以自由使用,没有
任何限制,而对于其他的外部class,protected就变成private。






19:09 2017/3/16


考虑c++中类和对象之间的关系:
 #include<iostream>
  2 
  3 using namespace std;
  4 
  5 class MyCircle
  6 {
  7 
  8    public:             //定义成员变量
  9       double m_r;
 10       double m_s;
 11    public:             //定义成员函数 
 12       void setR(double r)
 13       {
 14            m_r = r;
 15       }
 16       double getR(double m_r)
 17       {
 18            return m_r;
 19       }
 20       double getS(double m_r)
 21       {
 22           m_s = 3.14 * 3.14 *m_r;
 23           return m_s;
 24       }
 25 };
 26 //用面向对象的方法
 27 //1 类的抽象,成员变量和成员函数
 28 //2 实例化 类的对象
 29 //3 求面积
 30     //面向过程加工的是 一个一个的函数
 32 
 33 int main(int argc,char argv[])
 34 {
 35    MyCircle c1,c2,c3;        //用类 定义 变量 对象,这个类型可以定义很多对象,
 36    double r;
 37    cout<<"输入圆的半径r:";
 38    cin>>r;
 39 
 40    c1.setR(r);
 41   // c1.getR(r);
 42    cout<<"圆的面积是:"<<c1.getS(r)<<endl;
 43 
 44    return 0;
 45 }
 46 //类的代码分析:类的程序不是一步一步执行的,
 47 //类就好比是数据类型,数据类型的本质就是固定大小内存块的别名。c++编译器不会分配内存,用数据类型定义变量才会分配内存。
 48 //类和对象的关系:是一对多的关系。                                                                




/*****************************************************************************************************/
成员函数的意义
  1 #include<iostream>
  2 using namespace std;
  3 class circle
  4 {
  5     public:
  6         double r;
  7         double pi = 3.1415926;
  8         double area = pi*r*r;
  9 };
 10 int main()
 11 {
 12     circle c1;
 13     cout<<"please input your r"<<endl;
 14     cin >> c1.r;
 15     cout << c1.area<<endl;
 16     return 0;c
 17 }
这样写会报错,
1.初始化的时候已经执行了,当时r是一个随机值;
2.结果:造成area变量是一个乱码
/*****************************************************************************************************/
register在c++中得到加强
//register关键字 请求编译器让变量a直接放在寄存器里面,速度快
//在c语言里面中register修饰的变量不能取地址,但是在c++里面做了
内容
c++编译器发现程序中需要register变量的地址时,register对变量的声明
变得无效
c语言中struct的用法:
struct Teacher
{
   char name[32];
   int age;
};
void main()
{
    struct Teacher t1;     //c++中没有struct是可用的,struct和class的功能是一样的。
    t1.age = 10;  
}


/*****************************************************************************************************/
c语言中表达式的返回值是一个值,放在寄存器
c++中表达式返回的是变量的自身。        


20:24 2017/3/17
学习重在反馈。




引用:
引用是c++里面的范畴,c语言里面没有。
提出引用问题的实质是:一块连续的内存空间是否可有多个不同的别名?
可以,在定义引用的变量的同时,必须要有初始化的变量。
 1 #include<iostream>
  2 using namespace std;
  3 
  4 int main1(int argc,char *argv[])
  5 {
  6   int a = 10;
  7   int &b = a;      //定义引用的方法是:类型 &变量命=初始化; 
  8   printf("a = %d,b = %d\n",a,b);
  9   b = 20;
 10   printf("a = %d,b = %d\n",a,b);
 11   a = 100;
 12   printf("a = %d,b = %d\n",a,b);
 13   return 0; 
 14 }       
 15 //使用引用的意义
 16 void swap1(int a,int b)
 17 { 
 18     int c = a;
 19     a = b;
 20     b = c;
 21 } 
 22 void swap2(int *a,int *b) 
 23 {
 24 //    int *c;
 25 //  c = (int*)malloc(4);
 26 //  *c = *a;
 27 //  *a = *b;
 28 //  *b = *c;
 29     int c = 0;
 30     c = *a;
 31     *a = *b;
 32     *b = c;
 33 }
 34 void swap3(int &a,int &b)
 35 {
 36     int c = a;
 37     a = b;
 38     b = c;
 39 }
 40 int main(int argc,char *argv[])
 41 {
 42     int x = 10;
 43     int y = 20;
 44     swap1(x,y);
 45     printf("%d,%d\n",x,y);
 46     swap2(&x,&y);
 47     printf("%d,%d\n",x,y);
 48     swap3(x,y);
 49     printf("%d,%d\n",x,y);
 50     return 0;
 51 }






9:04 2017/3/18
结构体就好比一个类型,就比如int和char等数据类型,在使用你定义
好的结构体之前,需要先用结构体定义个变量。




11:50 2017/3/18
/*****************************************************************************************************/
mysql打开命令:service mysqld start


16:48 2017/3/20


16:56 2017/3/20
/*****************************************************************************************************/
软考中级学习总结:








13:45 2017/3/23
操作系统虚拟了多个进程空间,目的是是这些进程可以使用同一个物理地址。
1.操作系统为每一个进程虚拟了内存空间:为什么要虚拟内存
空间(是为了让n个进程有效的访问物理内存)
2.每一个进程空间的地址,&a 逻辑地址。。。。
3.==========内存的物理地址
     段式管理和页式管理机制
     linux内核模型的开发。




 find ./ -name  "*.cpp" | xargs grep "main" -n
查找当前目录下main的位置,-n是加行号
  


/*****************************************************************************************************/
字节序:


  1 #include<stdio.h>
  2 
  3 int main(int argc ,char *argv[])
  4 {
  5   unsigned int num = 0x12345678;     //int类型是4个字节,每两个数字表示一个字节   
  6   char *p =(char*) &num;  //78所在的位置是最低字节,通过看低字节是在高位还是地位来判断是大端字节序还是小端字节序。
  7   printf("%x,%x,%x,%x\n",p[0],p[1],p[2],p[3]);
  8   if(p[0] == 0x78)
  9   {
 10       printf("是小端字节序\n");
 11   }
 12   if(p[3] == 0x78)
 13   {
 14       printf("是大段字节序\n");
 15   }
 16 
 17   return 0;
 18 }
/*****************************************************************************************************/
int inet_aton(const char *cp,struct in_addr *inp);
int_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
/*****************************************************************************************************/
  1 #include<stdio.h>
  2 
  3 int main(int argc ,char *argv[])
  4 {
  5   unsigned int num = 0x12345678;     //int类型是4个字节,每两个数字表示一个字节   
  6   char *p =(char*) &num;  //78所在的位置是最低字节,通过看低字节是在高位还是地位来判断是大端字节序还是小端字节序。
  7   printf("%x,%x,%x,%x\n",p[0],p[1],p[2],p[3]);
  8   if(p[0] == 0x78)
  9   {
 10       printf("是小端字节序\n");
 11   }
 12   if(p[3] == 0x78)
 13   {
 14       printf("是大段字节序\n");
 15   }
 16   printf("把本地字节转换成网络字节\n");
 17    int mynetdat = htonl(num);
 18   p = (char *)&mynetdat;
 19   printf("%x,%x,%x,%x\n",p[0],p[1],p[2],p[3]);
 20 if(p[0] == 0x78)
 21   {
 22       printf("是小端字节序\n");
 23   }
 24   if(p[3] == 0x78)
 25   {
 26       printf("是大段字节序\n");
 27   }
 28   return 0;
 29 }






21:18 2017/3/23
/*****************************************************************************************************/
追求卓越,解决问题。
15:55 2017/3/24
t是文本模式,适合写字符串,比如写一个 255占3个字节。
b是二进制模式,适合写二进制数据,比如直接写一个整型255,占一个字节




10:41 2017/3/25
/*****************************************************************************************************/
gcc myseverpv.c -lpthread      //链库


20:43 2017/4/2
CPU操作内存的过程:
CPU通过总线操控内存,通过控制线控制寄存器,通过数据线传送数据。


/*****************************************************************************************************/
理解linux内核要从进程切换的角度上思考。
假如有进程A和进程B,进程A在执行的时候,通过调用DMA,使cpu进入空闲
状态,从而开始调用进程B。




SCI  嵌入内核,嵌入内核有两种方式,一种是硬中断,一种是软中断。




9:30 2017/4/3
mysql启动的命令:
service mysqld start


15:16 2017/4/3
在windows,启动mysql的命令是:net start mysql。
mysql登录:mysql
-u:用户名。
-p:密码。
-p:端口号


9:59 2017/4/15
照着我这个粘贴进去运行吧
create database test_database;                                          #创建数据库test_database
set names gbk;                                                          #设置字符编码cmd窗口默认为gbk
use test_database;                                                      #使用创建好的数据库
create table test1 (tid  int unsigned  primary key auto_increment,      #开始创建表 
                              name  char(20)  not null.


);
16:19 2017/4/16
跨网路通信是通过路由器。在同一个网络内通信使用交换机就可以啦。
1.255.255.255代表当前网络的广播地址,不能分配。
1.0.0.0代表网络本事,也不能分配。
私有ip是免费的,但不可以访问公网,需要转化成公网ip才可以访问公
网ip是需要付费的,现在公网ip的数量是2的32次方,42亿多,为了竟
可能节约公网ip,使用私有ip。
 
16:53 2017/4/16
常见端口号:
FTP(文件传输协议):端口号是 20 21
SSH(安全shell协议):端口号是 22
telnet(远程登录协议):端口号 23
DNS(域名系统):端口号53
http(超文本传输协议):端口号是80
SMTP(简单邮件传输协议):端口号是25
POP3(邮局协议3代):端口号是110


查看端口号命令:
netstat -an
-a:查看所有连接和监听端口
-n:显示ip地址和端口号,而不是显示域名和服务名。


不配置DNS是不能访问互联网的。
DNS的作用是把域名解析为ip,或者把ip解析为域名。
DNS服务的作用:
将域名解析为ip地址
(1)、客户机向DNS服务器发送域名查询请求
(2)、DNS服务器告知客户机Web服务器的IP地址
(3)、客户机与Web服务器通信


防范钓鱼网站:
主要看域名,不管后面有多长,例如:
www.taobao.com
www.taobae.com


DNS查找过程中,从DNS客户机到本地域名服务器使用了递归查询,从本地
域名服务器到根DNS服务器中是迭代查询。
递归查询和迭代查询的区别是,递归查询要得到准确的答案,即要么有,
并且是准确信息,要么报错。迭代查询是可以给个相似解的,一步步迭代,
最终得到正确答案。
从查询内容上分:
正向查询由域名查找ip地址,
反向查询由ip地址查找域名。


网关的作用是:
1、网关在所有内网计算机访问的不是本网段的数据报时使用。
2、网关负责将内网IP转换为公网IP,公网IP转换为内网IP. 


21:08 2017/4/16
网络基础:
(1)、Linux网络基础ISO/OSI七层模型:
应用层
表示层
会话层
传输层
网络层
数据链层
物理层


发送是从上到下,接收是从下到上
(2)、TCP/IP 4层物理模型
应用层
传输层
网际互联层
网络接口层
(3)、IP地址
IP地址的格式是32位。
其中分为A、B、C、D、E五大类。
D和E国家使用,A、B、C是民用。
A类IP地址范围是:1.0.0.0 -- 126.255.255.255
B类IP地址范围是:128.0.0.0 -- 191.255.255.255
C类IP地址范围是: 192.0.0.0 -- 223.255.255.255


局域网内是通过交换机根据物理地址来通信。网外是根据IP地址来通信。
(4).子网掩码
子网掩码的格式有:
255.0.0.0
255.255.0.0
255.255.255.0
IP地址与子网掩码对应,对应子网掩码255的区段,IP地址保持不变,剩下的
代表主机地址,广播地址是把代表主机地址的位置为255
例如:
IP地址:172.16.2.121
子网掩码:255.255.255.0
网络地址:172.16.2.0
主机地址:172.16.2.1-172.16.2.254
广播地址:172.162.2.255
网络地址就是IP地址与子网掩码做与处理。
(5).端口
常见端口号:
FTP(文件传输协议):端口号是 20 21
SSH(安全shell协议):端口号是 22
telnet(远程登录协议):端口号 23
DNS(域名系统):端口号53
http(超文本传输协议):端口号是80
SMTP(简单邮件传输协议):端口号是25
POP3(邮局协议3代):端口号是110
以寄邮件为例,IP地址是门牌号,端口就好比收件人。
(6).DNS的作用
DNS是一台服务器,当我们输入域名是,服务器把客户输入的域名传给
DNS服务器,DNS再从根域,顶级域,二级域逐级找到IP地址,通过迭代的
方式传给DNS服务器,DNS服务器再以递归的方式传给客户。
(7).网关
网关的作用是:
1、网关在所有内网计算机访问的不是本网段的数据报时使用。
2、网关负责将内网IP转换为公网IP,公网IP转换为内网IP.


总之,在同一网段内通讯时,需要知道端口号,在不同网段内通讯,需要
配置网关,端口,DNS,还有子网掩码。


如果有人在服务器内搭建了DHCP服务器,则可以自动获取IP地址。没有的
话就需要手动配置IP。




20:57 2017/4/18
(int)10.0*rand()/(RAND_MAX+1.0);与rand()%10的区别是:
前者更加平均。rand()的范围是1~RAND_MAX,分母加1是为了取小于1的
小数,在乘以10是为了取1~10的整数。
rand()返回值介于1-RAND_MAX之间,所有10*rand()/RAND_MAX就会介于
0-10之间,为了保证1-10的需要,所以要加一,后边这个除法要保证在
0-9的范围之内,而10*rand()/(RAND_MAX+1)永远不可能大与10.


/* 产生介于1 到10 间的随机数值,此范例与执行结果可与rand()参照*/ 
#include<time.h> 
#include<stdlib.h> 
main() 

int i,j; 
srand((int)time(0)); 
for(i=0;i<10;i++) 

j=1+(int)(10.0*rand()/(RAND_MAX+1.0)); 
printf(" %d ",j); 

}
/*****************************************************************************************************/
16:11 2017/4/21
/*****************************************************************************************************/
putchar函数的作用:向终端输出一个字符。
(1)putchar函数只能用于单个字符的输出,且一次只能输出一个字符

(2)在程序中使用putchar函数,务必牢记:在程序(或文件)的开头
加上编译预处理命令


getchar函数的作用:从系统隐含的输入设备(如键盘)输入一个字符。
getchar函数只能用于单个字符的输入,一次输入一个字符。程序的功
能是输入一个字符,显示一个字符,回车换行,再输入并显示一个字符。
而运行时字符是连续输入的,运行结果却是正确的,这是因为输入字符
后,它们暂存于键盘的缓冲区中,然后由getchar函数从键盘缓冲区中
一个一个的取出来。使用getchar函数时,在程序(或文件)的开头也
须加上编译预处理命令:#include "stdio.h"
getchar输入字符,一直到按下回车才结束,然后执行代码
getch不用按回车键


/*****************************************************************************************************/
21:23 2017/4/21
/*****************************************************************************************************/
解决编译含<mysql/mysql.h>头文件的编译问题:
gcc -I/usr/include/mysql *.c -L/usr/lib/mysql -lmysqlclient -o *
要链接上库。
当编译提示没有mysql/mysql.h文件时,考虑是否安装了mysql-devel-*-*
包,用命令
用rpm –qa |grep mysql查看安装的rpm包,这句话的意思是:
rpm –qa是列出所有rpm包
后面接管道 |grep xinetd
就是查含有xinetd的包名
/*****************************************************************************************************/
21:31 2017/4/21
/*****************************************************************************************************/
*mysql_real_connect(MYSQL *mysql, const char *host,
const char *user, const char *passwd, const char *db,
unsigned int port, const char *unix_socket,
unsigned long client_flag)
//上面描述了五个参数的主要取值,MYSQL *为mysql_init函数返回的指针,host为null或              
// localhost时链接的是本地的计算机,当mysql默认安装在unix(或类unix)系统中,root账户是没
// 有密码的,因此用户名使用root,密码为null,当db为空的时候,函数链接到默认数据库,在进行  
// mysql安装时会存在默认的test数据库,因此此处可以使用test数据库名称,port端口为0,使用    
// unix连接方式,unix_socket为null时,表明不使用socket或管道机制,最后一个参数经常设置为0


/*****************************************************************************************************/
16:39 2017/4/22
保护好你的鸡蛋,你的鸡蛋最为重要,要放在很多篮子当中。
/*****************************************************************************************************/
sprintf(缓存区地址,“格式化字符串”,输入字符);


/*****************************************************************************************************/
17:36 2017/4/22
/*****************************************************************************************************/
DESC table_name;
是查看表的属性。


/*****************************************************************************************************/
9:45 2017/4/23
/*****************************************************************************************************/
ssh root@192.168.0.125
密码:servers




/*****************************************************************************************************/
10:15 2017/4/23
/*****************************************************************************************************/
都是把格式好的字符串输出,只是输出的目标不一样:
1 printf,是把格式字符串输出到标准输出(一般是屏幕,可以重定向)。
2 sprintf,是把格式字符串输出到指定字符串中,所以参数比printf多一个char*。那就是目标字符串地址。
3 fprintf, 是把格式字符串输出到指定文件设备中,所以参数笔printf多一个文件指针FILE*。


/*****************************************************************************************************/
11:19 2017/4/23
/*****************************************************************************************************/
vim nohl取消高亮。




/*****************************************************************************************************/
11:27 2017/4/24
/*****************************************************************************************************/
哈夫曼编码(Huffman Coding)是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。
uffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长
 度最短的码字,有时称之为最佳编码,一般就叫作Huffman编码。 以哈夫曼树─即最优二
叉树,带权路径长度最小的二叉树,经常应用于数据压缩。 在计算机信息处理中,“哈夫
曼编码”是一种一致性编码法(又称"熵编码法"),用于数据的无损耗压缩。这一术语是
指使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。这张编码表的
特殊之处在于,它是根据每一个源字符出现的估算概率而建立起来的(出现概率高的字符
使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均
期望长度降低,从而达到无损压缩数据的目的)。这种方法是由David.A.Huffman发展起
来的。 例如,在英文中,e的出现概率很高,而z的出现概率则最低。当利用哈夫曼编码
对一篇英文进行压缩时,e极有可能用一个位(bit)来表示,而z则可能花去25个位(不是26)
。用普通的表示方法时,每个英文字母均占用一个字节(byte),即8个位。二者相比,
e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出
现概率的较准确的估算,就可以大幅度提高无损压缩的比例。 


/*****************************************************************************************************/
20:00 2017/4/24
/*****************************************************************************************************/
svn地址 : svn://122.114.174.115/opt/svn/yuanjian
用户名是每个人的首字母拼音,密码123456


/*****************************************************************************************************/
20:20 2017/4/24
/*****************************************************************************************************/
perror(); 
是错误输出函数。 
用来输出当前的错误信息,如果没有错误就显示ERROR 0。 
例如: 
perror("ddddddddddddd"); 
在没有错误的情况下就输出: 
ddddddddddddd : error 0 .
perror()用来将上一个函数发生错误的原因输出到标准设备(stderr)。
/*****************************************************************************************************/
20:29 2017/4/24
/*****************************************************************************************************/


union 维护足够的空间来置放多个数据成员中的“一种”,而不是为每
一个数据成员配置空间,在union 中所有的数据成员共用一个空间,同
一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地
址。例子如下:
union StateMachine
{
   char character;
   int number;
   char *str;
   double exp;
};


一个union 只配置一个足够大的空间以来容纳最大长度的数据成员,以
上例而言,最大长度是double 型态,所以StateMachine 的空间大小就
是double 数据类型的大小。


在C++里,union 的成员默认属性页为public。union 主要用来压缩空
间。如果一些数据不可能在同一时间同时被用到,则可以使用union。




/*****************************************************************************************************/
20:39 2017/4/24
/*****************************************************************************************************/


  联合变量的说明有三种形式:先定义再说明、定义同时说明和直接说明。
   以test类型为例,说明如下:
    1) union test
       {
         int office;
         char teacher[5];
       }; 
       union test a,b;    /*说明a,b为test类型*/
    2) union test
       {
         int office;
         char teacher[5];
       } a,b;             //同时定义且说明了a,b是test类型。
    3) union 
       {
         int office;
         char teacher[5];
       } a,b; 
       经说明后的a,b变量均为test类型。a,b变量的长度应等于test的成
员中最长的长度,即等于teacher数组的长度,共5个字节。a,b变量如赋予
整型值时,只使用了4个字节,而赋予字符数组时,可用5个字节。




/*****************************************************************************************************/
20:18 2017/4/25
/*****************************************************************************************************/


SET NAMES ‘charset_name’
    SET NAMES显示客户端发送的SQL语句中使用什么字符集。


/*****************************************************************************************************/
20:34 2017/4/25
/*****************************************************************************************************/
mysql_store_result和mysql_use_result的区别
在使用mysql_query()进行一个查询后,一般要用这两个函数之一来把结果
存到一个MYSQL_RES *变量中。
两者的主要区别是,mysql_use_result()的结果必须“一次性用完”,也
就是说用它得到一个result后,必须反复用mysql_fetch_row()读取其结果
直至该函数返回null为止,否则如果你再次进行mysql查询,会得到
“Commands out of sync; you can't run this command now”的错误。
而mysql_store_result()得到result是存下来的,你无需把全部行结果读
完,就可以进行另外的查询。比如你进行一个查询,得到一系列记录,再根
据这些结果,用一个循环再进行数据库查询,就只能用mysql_store_result()。


用mysql_query语句对mysql数据库进行查询,用mysql_store_result或mysql_
use_result进行,得到查询后的结果。


15:32 2017/4/27
/*****************************************************************************************************/
/*
 * The ifaddr structure contains information about one address  
 * of an interface.  They are maintained by the different address families,
 * are allocated and attached when an address is set, and are linked
 * together so all addresses for an interface can be located.
 */
   ifaddr结构包含有关接口的一个地址的信息。它们由不同的地址族维护,在设置地址
时分配和附加,并且链接在一起,因此可以找到接口的所有地址。
struct ifaddr {
struct sockaddr *ifa_addr; /* address of interface */  接口地址
struct sockaddr *ifa_dstaddr; /* other end of p-to-p link */p-p链接的另一端
#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */广播地址接口
struct sockaddr *ifa_netmask; /* used to determine subnet */用于确定子网
struct ifnet *ifa_ifp; /* back-pointer to interface */指向接口的指针
struct ifaddr *ifa_next; /* next address for interface */接口的下一个地址
void (*ifa_rtrequest)(); /* check or clean routes (+ or -)'d */检查或清理路线(+或-)‘d
u_short ifa_flags; /* mostly rt_flags for cloning */主要时rt_flag用于克隆
short ifa_refcnt; /* extra to malloc for link info */额外的malloc的链接信息
int ifa_metric; /* cost of going out this interface */走出这个界面的成本
#ifdef notdef
struct rtentry *ifa_rt; /* XXXX for ROUTETOIF ????? */
#endif
};


struct sockaddr {
unsigned short sa_family;     /* address family, AF_xxx */
char sa_data[14];                 /* 14 bytes of protocol address */
};
sa_family是地址家族,一般都是“AF_xxx”的形式。好像通常大多用的是都是AF_INET。
sa_data是14字节协议地址。
此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。


The AF_INET address family is the address family for IPv4.
The AF_INET6 address family is the address family for IPv6.


地址家族,如果用的是ipv4,则是AF_INET,如果用的是ipv6,则是AF_INET6
/*****************************************************************************************************/
int getnameinfo(const struct sockaddr *sockaddr, socklen_t addrlen, char *host,
 size_t hostlen, char *serv, size_t servlen, int flags);  
它以一个套接口地址为参数,返回一个描述主机的字符串和一个描述服务的字符串。
成功返回0,出错返回-1
sockaddr指向包含协议地址的套接口地址结构,它将会被转换成可读的字符串,
addrlen是结构的长度,这个结构及其长度通常由accept、recvfrom、getsockname或getpeername返回。
调用 者给两个直观可读的字符串分配空间:host和hostlen指定主机字符串,serv和servlen指定服务
字符串。如果调用 者不想返回主机字符串,将hostlen设为0即可。同样servlen设为0就指定不返回服
务的信息。为了给这两个字符串分配空间,
<netdb.h>中定义了两个常值
NI_MAXHOST:返回的主机字符串的最大长度
NI_MAXSERV:返回的服务字符串的最大长度
/*****************************************************************************************************/
网络套接字:
源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务。
它是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连接使用的协议,本地主机的IP地址,
本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
/*****************************************************************************************************/
文件描述符的本质:
     无论是文件句柄(Windows中概念),还是文件描述符(linux中概念),其最终目的都是用来定位打开的文
件在内存中的位置,或者进行操作。只是它们映射的方式不一样。(用来定位打开文件在内存中的位置)
在文件描述符表中,寻找1个尚未使用的元素,在该元素中填入一个指针值,让其指向刚建立的文件表元素。
最重要的是:将该元素的下标作为open的返回值返回。
/*****************************************************************************************************/
9:24 2017/4/28
/*****************************************************************************************************/
Linux网络编程中
主要介绍:socket、bind、listen、connect、accept、send、sendto、recv、recvfrom、close、shutdown
1.socket。
其在linux和windows环境下的头文件主要是:#include<sys/socket.h>和#include<WinSock2.h>


int socket(int demain,int type, int protocol);
    -成功的话返回一个小的非负整数,出错的话返回-1.
    -family指明协议族/域,通常为通常AF_INET、AF_INET6、AF_LOCAL等,AF_INET是ipv4,AF_INET6是ipv6
    -type是套接口类型,主要SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;
    -protocol一般取为0。成功时,返回一个小的非负整数值,与文件描述符类似。
其目的是返回一个文件描述符,(每个进程有一个独一的文件描述符表,文件描述表就是一个操作物理文件
的标识,通过文件描述符读取所有进程共享的文件表,在通过文件读取v节点表,最终来操作物理文件),
也就是说socket后建立一个标识,以后的操作都要通过这个标识。
2.bind
int bind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen);
    -成功后返回的是0,错误的话返回的是-1
    -sockfd参数表示的是要绑定的文件描述符
    -myaddr表示的是想要绑定的IP和端口号,均要使用网络字节序-即大端模式
    -addrlen表示的是前struct sockaddr的长度。
  其作用是,在通过socket函数获得文件描述符后,将一个具体的ip协议地址与其绑定。
当socket函数返回一个描述符时,只是存在于其协议族的空间中,并没有分配一个具体的协议地址
(这里指IPv4/IPv6和端口号的组合),bind函数可以将一组固定的地址绑定到sockfd上。
   通常服务端的程序需要,客户端的程序不需要,是自动匹配。
   通常服务器在启动的时候都会绑定一个众所周知的协议地址,用于提供服务,客户就可以通过它来接连服务器;
而客户端可以指定IP或端口也可以都不指定,未分配则系统自动分配。这就是为什么通常服务器端在listen之前会
调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
3.listen
int listen(int sockfd,int backlog)
    作用:1.当函数socket创建一个套接口时,它被假设为一个主动套接口,也就是说,它是一个将调用connect
发起连接的客户套接口,函数listen将未连接的套接口转化成被动套接口,指示内核应该接受指向此套接口的连接请求。
    2.函数的第二个参数规定内核为此套接口排队的最大连接数
4.connect
int connect(int sockfd,conststruct sockaddr *addr, socklen_t addrlen) 
    -成功的话返回值是0,失败的话返回值是-1.
    -通过此函数建立于TCP服务器的连接,实际是发起三次握手过程,仅在连接成功或失败后返回。
    -参数sockfd是本地描述符,
    -addr为服务器地址
    -addrlen是socket地址长度。
/*****************************************************************************************************/
sockaddr_in结构体:
struct sockaddr_in { 
   short int sin_family;
   unsigned short int sin_port; 
     struct in_addr sin_addr;
     struct in_addr { 
            unsigned long s_addr;
           }      
     unsigned char sin_zero[8];
}  
sin_family是通信类型,ipv4是AF_INET。把目标地址和端口信息分开进行保存。
/*****************************************************************************************************/
ssize_t read(int fd, void * buf, size_t count);
     -函数说明:
   read()会把参数fd 所指的文件传送count 个字节到buf 指针所指的内存中. 若参数count 为0,
   则read()不会有作用并返回0. 返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取 
   的数据,此外文件读写位置会随读取到的字节移动. 
     -附加说明:
   如果顺利 read()会返回实际读到的字节数, 最好能将返回值与参数count 作比较, 若返回的字节数比要求读取的字节数少, 则有可能读到了文件尾、从管道(pipe)或终端机读? ?蛘呤莚ead()被信号中断了读取动作. 
   当有错误发生时则返回-1, 错误代码存入errno 中, 而文件读写位置则无法预期.
/*****************************************************************************************************/
int listen(int s, int backlog);
函数说明:
listen()用来等待参数s 的socket 连线. 
     -参数backlog 指定同时能处理的最大连接要求, 如果连接数目达此上限则client 端将收到ECONNREFUSED
 的错误. Listen()并未开始接收连线, 只是设置socket 为listen 模式, 真正接收client 端连线的是accept().
 通常listen()会在socket(), bind()之后调用, 接着才调用accept().
     -返回值:成功则返回0, 失败返回-1, 错误原因存于errno
     -附加说明:listen()只适用SOCK_STREAM 或SOCK_SEQPACKET 的socket 类型. 
如果socket 为AF_INET 则参数backlog 最大值可设至128.
/*****************************************************************************************************/
int accept(int sockfd, void *addr, int *addrlen);   
     -第一个参数用来标识服务端套接字(也就是listen函数中设置为监听状态的套接字)
     -第二个参数是用来保存客户端套接字对应的“地方”(包括客户端IP和端口信息等)
     -第三个参数是“地方”的占地大小。返回值对应客户端套接字标识。
     -函数说明:accept()用来接受参数s 的socket 连线.
     参数s 的socket 必需先经bind()、listen()函数处理过, 当有连线进来时accept()会返回一个新的socket
     处理代码, 往后的数据传送与读取就是经由新的socket处理, 而原来参数s 的socket 能继续使用accept()
     来接受新的连线要求. 连线成功时, 参数addr 所指的结构会被系统填入远程主机的地址数据, 
     参数addrlen 为scokaddr 的结构长度. 关于机构sockaddr 的定义请参考bind().
     -返回值:成功则返回新的socket 处理代码, 失败返回-1, 错误原因存于errno 中.
即服务端程序
/*****************************************************************************************************/
atoi() 函数用来将字符串转换成整数(int),其原型为:
int atoi (const char * str)
【返回值】返回转换后的整型数;如果 str 不能转换成 int 或者 str 为空字符串,那么将返回 0
/*****************************************************************************************************/
strcmp() 用来比较字符串(区分大小写),其原型为:
    int strcmp(const char *s1, const char *s2);
/*****************************************************************************************************/
头文件:#include <string.h>
strlen()函数用来计算字符串的长度,其原型为:
    unsigned int strlen (char *s);
/*****************************************************************************************************/
strcpy(char *dest,char *src); 
 原型:extern char *strcpy(char *dest,char *src);
用法:#include <string.h>  
功能:把src所指由NULL结束的字符串复制到dest所指的数组中。
/*****************************************************************************************************/
strcat() 函数用来连接字符串,其原型为:
    char *strcat(char *dest, const char *src);
【参数】dest 为目的字符串指针,src 为源字符串指针。
/*****************************************************************************************************/
MYSQL_RES *mysql_store_result(MYSQL *mysql);  
     对于成功检索了数据的每个查询(SELECT、SHOW、DESCRIBE、EXPLAIN、CHECK TABLE等),
必须调用mysql_store_result()或mysql_use_result() 。对于其他查询,不需要调用mysql_store_result()
或mysql_use_result(),但是如果在任何情况下均调用了 mysql_store_result(),它也不会导致任何伤害或
性能降低。通过检查mysql_store_result()是否返回0,可检测查询 是否没有结果集(以后会更多)。
      注意其返回值的类型是MYSQL_RES*
/*****************************************************************************************************/
int mysql_num_fields(MYSQL *mysql);
   该函数的作用是获取结果集中列的个数。
int mysql_num_rows(MYSQL *mysql);
   该函数的作用是获取结果集中行的个数。




/*****************************************************************************************************/
typedef struct st_mysql_field {
  
 char *name;                     /* Name of column */
 列的名字
 char *table;                    /* Table of column if column was a field */
 如果列是一个字段表的列
 char *org_table;                /* Org table name if table was an alias */
Org表名如果表是一个别名
 char *db;                       /* Database for table */
 表的数据
 char *def;                      /* Default value (set by mysql_list_fields) */


 unsigned long length;           /* Width of column */


 unsigned long max_length;       /* Max width of selected set */
 
 unsigned int flags;             /* Div flags */
 层标记
 unsigned int decimals;          /* Number of decimals in field */
小数位数
 enum enum_field_types type;     /* Type of field. Se mysql_com.h for types */


} MYSQL_FIELD;


/*****************************************************************************************************/
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);返回类型是mysql_field*,是一个地址。
/*****************************************************************************************************/
typedef struct st_mysql_res {
  my_ulonglong row_count;                              // 结果集的行数
  unsigned int field_count, current_field;           // 结果集的列数,当前列
  MYSQL_FIELD *fields;                                   // 结果集的列信息
  MYSQL_DATA *data;                                     // 结果集的数据
  MYSQL_ROWS *data_cursor;                       // 结果集的光标
  MEM_ROOT field_alloc;                                   // 内存结构
  MYSQL_ROW row;                                        // 非缓冲的时候用到
  MYSQL_ROW current_row;                           //mysql_store_result时会用到。当前行
  unsigned long *lengths;                               //每列的长度
  MYSQL *handle;                                          // mysql_use_result会用。
  my_bool eof;                                                //是否为行尾
} MYSQL_RES;
/*****************************************************************************************************/
typedef char **MYSQL_ROW; /* 返回的每一行的值,全部用字符串来表示*/
typedef struct st_mysql_rows {
  struct st_mysql_rows *next; /* list of rows */
  MYSQL_ROW data;
} MYSQL_ROWS;        //mysql的数据的链表节点。可见mysql的结果集是链表结构
/*****************************************************************************************************/
4. 取得列的信息。
mysql_fetch_field()
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result)
描述
返回采用MYSQL_FIELD结构的结果集的列。重复调用该函数,以检索关于结果集中所有列的信息。未剩余字段时,
mysql_fetch_field()返回NULL。每次执行新的SELECT查询时,将复位mysql_fetch_field(),以返回关于第1个
字段的信息。调用mysql_field_seek()也会影响mysql_fetch_field()返回的字段。
如果调用了mysql_query()以在表上执行SELECT,但未调用mysql_store_result(),如果调用了
mysql_fetch_field()以请求BLOB字段的长度,MySQL将返回默认的Blob长度(8KB)。之所以选
择8KB是因为MySQL不知道BLOB的最大长度。应在日后使其成为可配置的。一旦检索了结果集,
field->max_length将包含特定查询中该列的最大值的长度。
返回值当前列的MYSQL_FIELD结构。如果未剩余任何列,返回NULL。错误无
示例:
MYSQL_FIELD *field; 
while((field = mysql_fetch_field(result)))
{
    printf("field name %s/n", field->name);
}
也就是说mysql_fetch_field函数在获取了一个字段的列信息后,在下一次执行中会自动提取下一字段的信息。
/*****************************************************************************************************/


既然嫉恨别人比你好,就对自己要求高点。
20:24 2017/5/1
/*****************************************************************************************************/
c语言程序所在内存分类:
(1).栈:由编译器自动分配释放,存放函数的参数值、局部变量的值、返回地址等,其他操作
方式类似于数据结构中的栈。
(2).堆:一般由程序员动态分配(调用函数malloc())和释放函数(free()),若程序员不释放,程序
结束时可能由操作系统回收。
(3).数据段:存放的是全局变量、静态变量、常数。根据存放的数据,数据段又可以分成普通数据段
(包括可读可写/只读数据段,存放静态初始化的全局变量或常量)、BSS数据段(存放未初始化的全局变量)。
(4).代码段(code):用于存放程序代码。
/*****************************************************************************************************/
堆和栈的区别:
1.申请方式的区别:
   栈的申请:系统会自动为申请的栈提供空间,即栈的申请是系统自动分配的。
   堆的申请:堆需要程序员自己申请,需要调用函数(malloc()),使用完后,再用函数free()释放。
2.申请完后结果的区别。
  堆在程序空间中有一个记录空闲内存地址的链表。当系统得到申请后,系统就会开始遍历该链表,
寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空
间分配给程序。在程序员调用函数malloc()申请堆空间后,堆链表会从头指针开始,沿着空链表寻
找大于或等于申请大小的空指针域,然后给分配空间,得到的链表的头指针数据域存放的是申请的字节大小,
free()要以此来释放空间。
   栈的申请,只要栈的剩余空间大于申请空间,就可以分配,否自会报栈溢出的错误。
3.申请大小的限制
   堆是向高地址申请,由于是链表存储结构,所以不是连续的。
   栈是由高向低地址申请,是连续的。 
4.申请速度
   堆是由malloc()申请的,申请速度较慢,空间地址不连续,较为分散。
   栈的申请空间快,空间连续。
5.存储内容。
   堆的头部用一个字节存放堆的大小。
/*****************************************************************************************************/
1.能够由机器一次完成处理的数据叫做字。
2.数据对齐指的是它的长度是它内存地址的整数倍。例如:一个32位的数据(长度是4字节,一个字节是8位),如果它的地址
能够被4整除,则称为自然对齐。
3.对字节序的理解,计算机中,字符是一个字节,写入和读取都是一个一个的字节,而int类型是4个字节,这时,这4个字节
读取的顺序就关系到该int类型数据的值。所以会有字节序。
  字节序有大端模式和小端模式,大端模式即高字节存储在低地址中,低字节存储在高地址中。小端模式即低字节存储在地址
中,高字节存储在高地址中。


16:55 2017/5/2
/*****************************************************************************************************/
对文件描述符的认识:
   基于linux系统中一切皆文件的概念,所有对设备和文件的操作都是使用文件描述符来进行的。文件描述符是一个非负整数,
是一个索引值。并指向在内核中每个进程打开的文件记录表里。当打开一个现存文件或创建一个新文件时,内核就像进程返回一个
文件描述符。当需要读写文件时,也需要把文件描述符传递给对应的函数做参数。
   一个进程打开时,都会打开三个文件:标准输入,标准输出,标准错误。这3个文件分别对应的文件描述符是0,1,2。
/*****************************************************************************************************/
介绍文件I/O操作的系统调用:
   文件I/O操作的系统调用,主要用到5个函数:open()、read()、write()、lseek()、close().
   open():用于打开和创建文件,在打开或创建文件时,可以指定文件的属性以及用户的权限。
   close():函数用于关闭一个被打开的文件。当一个进程终止时,所有被它打开的文件都由内核自动关闭,很多程序
都使用这一功能而不显示地关闭一个文件。
   read():函数用于将从指定的文件描述符中读出的数据放到缓存区中,并返回实际杜读入的字节数。若返回为0,则
表示没有数据可读,即以大文件尾。读操作从文件的当前指针位置开始。当从终端设备文件中读出数据时,通常一次最多
读一行。
   write():函数用于向打开的文件写数据,写操作从文件的当前指针位置开始。对磁盘文件进行写操作,若磁盘已满或
超出该文件的长度,则write()函数返回失败。
   lseek():函数是用于在指定的文件描述符中将文件指针定位到相应的位置。它只能用于在可定位文件操作中。管道、套接字和
大部分字符设备文件是不可定位的,所以在这些文件的操作中无法使用lseek()调用。
   使用方法:(1)、open()函数语法要点: 
                 -所需要的头文件:#include<sys/types.h>   //提供类型pid_t的定义
                                 #include<sys/stat.h>
                                 #include<fcntl.h>
                 -函数原型:int open(const char * pathname,int flags,int perms)
                 -函数传入值: -pathname:被打开的文件名(可包括路径名)。。用双引号引起来。
                              -flag:文件打开的方式:O_RDONLY:以只读方式打开文件
                                                   O_WRONLY:以只写方式打开文件
                                                   O_RDWR:以读写方式打开文件                  
                                                   O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三个参数为
                                                   其设置权限
                                                   O_EXCL:如果使用O_CREAT时,则可返回错误信息。这一参数可测试文件是否
                                                   存在。此时open是原子操作,防止多个进程同时创建同一个文件。
                                                   O_NOCTTY:使用本参数时,若文件为终端,那么该终端不会成为调用open()的
                                                  那个进程的终端 
                                                   O_TRUNC:若文件已经存在,那么会删除文件中的所有数据,并且设置文件的大小
                                                   为0.
                                                   O_APPEND:以添加的方式打开文件,即打开文件后,文件指针会指向末尾,即
                                                   将写入的数据添加到文件末尾。
                             -perms:被打开文件的存储权限。
                                     可以用一组宏定义:S_I(R/W/X)(USR/GRP/OTH).
                                     其中R/W/X分别表示文件的度/写/操作权限
                                     USR/GRP/OTH分别表示文件所有者/文件所属组/其他用户。
                                     例如:S_IRUSR|S_IWUSR表示设置文件所有者的可读可写属性。八进制表示法中0600也表示
                                     同样的权限。
                 -函数的返回值:成功:返回文件描述符
                               失败:-1
                 在open()函数中,打开方式可以用|来组合构成,但是O_RDONLY.O_WRONLY.O_RDWR三种方式不可以相互组合。
                 被打开文件的存储权限也可用宏定义或者是数字来表示。
             (2).close()函数的语法要点。
                -所需的头文件:#include<unistd.h>
                -函数原型:int close(int fd)
                -函数的输入值:fd 文件描述符
                -函数的返回值:成功:0,出错:-1.
             (3).read()函数的语法要点。
                -所需要的头文件:#include<unistd.h>
                -函数原型:ssize_t read(int fd,void *buf,size_t count)
                -函数传入值:fd:文件描述符。
                            buf:指定存储器读出数据的缓冲区
                            count:指定读出的字节数
                -函数返回值:成功:读到的字节数
                            已到达文件尾:0
                            出错:-1
                在读普通文件时,若读到要求的字节数之前已到达文件的尾部,则返回的字节数会小于希望读出的字节数。
             (4).write()函数的语法格式:
                -所需头文件:#include<unistd.h>
                -函数原型:ssize_t write(int fd,void *buf,size_t count)
                -函数传入值:fd:文件描述符
                            buf:指定存储器写入数据的缓冲区
                            count:指定读出的字节数
                -函数的返回值:成功:已写的字节数
                              出错:-1
                在写普通文件时,写操作从文件的当前指针位置开始
             (5).lseek()函数的语法格式
                -所需的头文件:#include<unistd.h>
                              #include<sys/types.h>
                -函数原型:off_t lseek(int fd,off_t offset,int whence)
                -函数传入值:fd:文件描述符
                           offset:偏移量,每一读写所需要移动的距离,单位是字节,可正可负(向前移,向后移)
                           whence:当前位置的基点。SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小
                                                  SEEK_CUR:当前位置为文件的位置,新位置为当前位置加上偏移量。
                                                  SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小。
                           函数返回值:成功:为文件的当前位移。
                                      出错:返回值-1。
/*****************************************************************************************************/                           、
文件锁的概述:
   上述的五个函数:open(),close(),write(),read(),lseek().这五个函数是对文件的基本操作,在文件已经共享的情况下如何操作,
也就是当多个用户共同使用,操作一个文件的情况下,这时,Linux通过给文件上锁来防止共享资源的竞争。
   文件锁包括建议性锁和强制性锁。
   实现上锁的函数有lockf()和fcntl(),lockf()是施加建议性锁,fcntl()建议性锁和强制性锁都可以施加。一般内核施加的是强制性锁。
   fcntl()还可以对某一文件的记录上锁,也就是记录锁,记录所包括读取锁和写入锁,读取锁又是共享锁,能使多个进程在文件的同一部分
建立读取锁。写入锁又叫排斥锁,在任何时刻只能有一个进程在文件的某个部分上建立写入锁。当然,文件的同一部分不能同时写读取锁和
写入锁。
   fcntl()除了可以给文件上锁外,还可以获得文件描述符和文件描述符标志、文件描述符的复制等很多功能。
   fcntl()函数格式:
          - 所需要的头文件:#include<sys/types.h>
                           #include<unistd.h>
                           #include<fcntl.h>
          - int fcntl(int fd,int cmd,struct flock *lock)
          - 函数传入值:fd:文件描述符
                       cmd:F_DUPFD:复制文件描述符
                           F_GETFD:获得fd的close-on-exec标志,若标志未设置,则文件经过exec()函数之后仍保持打开状态。
                           F_SETFD:设置close-on-exec标志,该标志有参数arg的FD_CLOEXEC位决定
                           F_GETFL:得到open设置的标志
                           F_SETFL:改变open设置的标志
                           F_GETLK:根据lock描述,决定是否上文件锁
                           F_SETLK:设置lock描述的文件锁
                           F_SETLKW:这是F_SETLK的阻塞版本(命令名中的W表示等待(wait)).在无法获取锁时,会进入睡眠状态;
                                    如果可以获取锁或者捕捉到信号,则会返回。
                       lock:结构为flock,设置记录锁的具体状态
           - 返回值:成功的话返回0
                    出错的话返回-1
第三个参数的类型flock结构的定义如下:
         struct flock
         {
             short l_type;
             off_t l_start;
             short l_whence;
             off_t l_len;
             pid_t l_pid;             
         }
         flock结构中每个成员的取值含义:
         l_type:F_RDLCK:读取锁(共享锁)
                 F_WRLCK:写入锁(排斥锁)
                 F_UNLCK:解锁
         l_start:加锁区域在文件中的相对位移量(字节),与l_whence值一起决定加锁区域的起始位置
         l_whence(相对位移量的起点(同lseek的whence)):SEEK_SET:当前位置文件的开头,新位置为便移量的大小
                                                     SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量的大小
                                                     SEEK_END:当前位置为文件的结尾,新的位置为文件大小加上偏移量的大小。
         l_len:加锁区域的长度
         l_pid:具有阻塞当前进程的锁,其持有进程的进程号存放在l_pid中,仅由F_GETLK返回
         为加锁整个文件,通常的方法是将l_start设置为0,l_whence设置为SEEK_SET,l_len设置为0.
fcntl()应用:多个进程对同一个文件,不同部分可以并行执行,有交叉的部分可以互斥进行。  
     其实这个在APUE里面已经讲的很清楚了,在系统仅提供建议性锁的情况下,文件锁只有在使用“一致的方法处理记录锁(也即文件锁)”
的合作进程间才有效,建议性锁并不能阻止对文件有写权限的任何其他进程对文件进行的写操作。只有在系统提供强制性锁的情况下,
内核才会对每一个open、read、和write系统调用进行检查。    
      这里所谓的“一致的方法”就是说其他进程在对文件操作前也要进行相应的fcntl操作,这样建议性锁才有效。
      所以,对于记录锁,要采用一致的方法才有作用,例如都进行相应的fcntl函数操作。
至于应用,一个应用程序的多个进程在都需要修改一个数据文件时,这种锁就显的重要了。
多个进程修改的文件区不同时就可以并行执行,有交差时就可以互斥执行,多好啊!
此函数就是要解决文件的共享问题。
实验代码:实验目的:对输入的文件进行上锁。
  1#include<sys/types.h>
  2 #include<unistd.h>
  3 #include<fcntl.h>
  4 #include<stdio.h>
  5 #include<sys/stat.h>
  6 int lock_set(int fd,int type)
  7 {
  8   struct flock old_lock,lock;
  9   lock.l_whence = SEEK_SET;  //加锁的起始位置,SEEK_SET表示的是从文件头开始
 10   lock.l_type = type;        //加锁的类型,是读取锁还是写入锁,或者解锁
 11   lock.l_start = 0;          //加锁区域在文件的相对位移量,它和l_whence一起决定了加锁的起始位置
 12   lock.l_len = 0;            //加锁区域的长度
 13   lock.l_pid = -1;           //具有阻塞当前进程的锁
 14   //初始化完成后,先判断能否加锁,使用宏定义F_GETLK
 15   fcntl(fd,F_GETLK,&lock);
 16   if(lock.l_type != F_UNLCK) //F_UNLCK表示的是解锁,当type等F_UNLCK时,表明已经解了锁,所以此时是空的,就可以上锁,若不是,则表明>    已经上了读取锁了或者写入锁。
 17      {
 18          if(lock.l_type == F_RDLCK)
 19          {
 20             printf("已经上了读取锁了\n");
 21          }
 22          if(lock.l_type == F_WRLCK)
 23          {
 24              printf("已经上了写入锁了\n");
 25          }
 26      }
 27   lock.l_type = type;//防止type被改,上锁,用参数F_SETLKW
 28   if((fcntl(fd,F_SETLKW,&lock)) < 0)
 29   {
 30       printf("lock failed = %d\n",lock.l_type);
 31       return 1;
 32   }
 33   switch(lock.l_type)
 34   {
 35       case F_RDLCK:
 36           {
 37               printf("READ lock set by %d\n",getpid());
 38           }
 39           break;
 40       case F_WRLCK:
 41           {
 42               printf("WRITE lock set by %d\n",getpid());
 43           }
 44           break;
 45       case F_UNLCK:
 46           {
 47               printf("REALEASE lock by %d\n",getpid());
 48           }
 49           break;
 50       defalult:break;
 51   }
 52   return 0;
 53 }
 54 int main(int argc,char *argv[])
 55 {
 56     int fd;
 57     fd = open("read.c",O_RDONLY,S_IRUSR);
 58     lock_set(fd,F_RDLCK);
 59     printf("%d\n",fd);
 60     return 0;
 61 }




/*****************************************************************************************************/                           
字符串的处理:
/*****************************************************************************************************/         
                     
/*****************************************************************************************************/                              
10:56 2017/5/3
/*****************************************************************************************************/       
多路复用:既然是多路复用,所以就是文件描述符集,就是对文件描述集的操作。
I/O处理的模型有以下五种:
   (1).阻塞I/O模型
   (2).非阻塞I/O模型
   (3).I/O多路转接模型:在这种模型下,如果请求的I/O操作阻塞,且不是真正的阻塞I/O,而是让其中一个函数等待,
在这期间,I/O还能进行其他操作。如select()和poll()
   (4).信号驱动I/O模型:通过安装一个信号处理程序,系统可以自动捕获特定信号,从而启动I/O
   (5).异步I/O模型 :不常用
   用select()和poll()函数是处理I/O复用的一个高效的方法。它可以具体设置程序中每一个所关心的文件描述符的条件、希望等待的
时间,从select()和poll()函数返回时,内核会通知用户已准备好的文件描述符的数量、已准备好的条件(或事件等)。通过使用select()
函数或poll()函数的返回结果,就可以调用对应的I/O函数了。
    1、select()函数的语法要点:
       -所需要的头文件:#include<sys/types.h>
                       #include<sys/times.h>
                       #include<unistd.h>
       -函数原型:int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exeptfds,struct timeval *timeout)
       -函数传入值:numfds:由参数值为需要监视的文件描述符的最大值加1
                   readfds:由select()监视的读文件的描述符集合
                   writefds:由select()监视的写文件的描述符集合
                   execptfds:由select()监视的异常处理文件描述符集合
                   timeout:NULL:永远等待,直到捕捉到信号或文件描述符已准备好为止
                           具体值:struct timeval类型的指针,若等待了timeout时间还没有检测到任何文件描述符准备好,就立即返回。
                            0:从不等待,测试所有指定的描述符并立即返回。
       -函数的返回值:成功:准备好的文件描述符
                      超时:0
                     出错:-1                       
        可以看到,select()函数根据希望进行的文件操作对文件描述符进行了分类处理,这里,对文件描述的处理主要涉及4个宏函数。
        select()文件描述符处理函数
        FD_ZERO(fd_set *set)      :清除一个文件描述符集
        FD_SET(int fd,fd_set *set):将一个文件描述符加入文件描述符集中
        FD_CLR(int fd,fd_set *set):将一个文件描述符从文件描述符集中删除
        FD_ISSET(int fd,fd_set *set):如果文件描述符fd为fd_set集中的一个元素,则返回非零值,可以用于调用select()之后    
        测试文件描述符集中的某个文件描述符是否有变化。
        使用方法:在使用select()函数之前,先用FD_ZERO()函数来清除一个文件描述符集,然后用FD_SET()函数将一个文件描述符
加入到文件描述符集中,然后再用FD_ISSET()函数来逐个检验每个文件描述符集,然候再用函数FD_CLR将文件描述符集删除。
        select()函数中的timeout()是一个struct timeval类型的指针,改指针结构体如下所示:
        struct timeval
        { 
          long tv_sec;      //秒
          long tv_unsec;    //微秒
        }
        这个结构体可以精确到微妙级,对于绝大多数程序时够用了。
    2、poll()函数的语法格式:
       -所需头文件:#include<sys/types.h>
                   #include<poll.h> 
       -函数原型: int poll(struct pollfd *fds,int numfds,int timeout)
       -函数的传入值:fds:struct pollfd结构的指针,用于描述需要对那些文件的哪种类型的操作进行监控。
                      struct pollfd
                      {
                          int fd;            //需要监听的文件描述符
                          short events;      //需要监听的事件
                          short revents;     //已发生的事件
                      }
                      events成员描述需要监听那些类型的事件,可以用以下几种标志来描述;
                      POLLIN:文件中有数据可读,下面实例中使用到了这个标志;
                      POLLPRI:文件中有紧急数据可读;
                      POLLOUT:可以向文件写入数据;
                      POLLERR:文件中出现错误,只限于输出;
                      POLLHUP:与文件到的连接被断开,只限于输出;
                      POLLNVAL:与文件描述符是不合法的,即它并没有指向一个成功打开的文件;
                      numfds:需要监听的文件个数,即第一个参数所指向的数组中的元素数目;
                      timeout:表示poll阻塞的超时时间(毫秒)。如果该值小于等于0,则表示无线等待。
        -函数的返回值:成功:返回大于0的值,表示事件发生的pollfd结构的个数。
                      超时:0
                      出错:-1    
  
      其实用select()函数会有比较多的局限,比如说:内核必须检查多余的文件描述符,每次调用select()之后必须重置被监听的
文件描述符集,而且监听的个数受到限制。poll()机制比select()机制使用范围更广,效率更高。
实验目的:多路复用。通过poll()函数,来监听3个终端的输入。建立一个poll()函数监视的读文件描述符集,其中包含3个文件描述符,
分别为标准输入文件描述符和两个管道文件描述符。 
例:
  1 #include<fcntl.h>
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 #include<string.h>
  6 #include<time.h>
  7 #include<poll.h>
  8 #include<errno.h>
  9 #define MAX_BUFFER_SIZE 1024
 10 #define IN_FILES   3
 11 #define TIME_DELAY 60000
 12 #define MAX(a,b)  ((a>b)?(a):(b))
 13 //目的:用poll()函数对三个文件,分别是标准输入,两个管道文件进行监控和操作。OB
 14 //struct pollfd结构的指针,用于描述需要对哪些文件的哪些操作进行监控
 15 //select()和poll()函数用于多复路监控
 16 //memset(void *s,int c,int g)用个给某个内存空间赋值给定值,c是某字符的ascii码
 17 
 18 //程序流程:先获取两个管道文件的文件描述符,然后设置需要监听文件事件的类型,然后对于执行操作
 19 int main(void)
 20 {
 21    struct pollfd fds[IN_FILES];   //文件描述服集定义
 22    char buf[MAX_BUFFER_SIZE];
 23    int i , real_read;
 24    fds[0].fd = 0;
 25    if((fds[1].fd = open("in1",O_RDONLY|O_NONBLOCK)) < 0)
 26    {
 27        printf("Open tub1 failed!\n ");
 28        return 1;
 29    }
 30    if((fds[2].fd = open("in2",O_RDONLY|O_NONBLOCK)) < 0)
 31    {
 32        printf("Open tub2 failed!\n");
 33        return 1;
 34    }
 35    for(i = 0;i < IN_FILES;i++)                  //设置文件监听的事件的类型
 36    {
 37           fds[i].events = POLLIN;
 38    }
 39    while(fds[0].events || fds[1].events || fds[2].events)//events表示正在监听的事件,循环检测是否有监听的文件
 40    {
 41        if(poll(fds,IN_FILES,0) < 0)    //调用poll函数对文件描述符集进行监听,第二个参数表示的是需要监听的文件个数,0表示永远阻塞
 42        {
 43            printf("poll failed!\n");
 44            return 1;
 45        }
 46        for(i = 0;i < IN_FILES;i++ )
 47        {
 48           if(fds[i].revents)      //判断哪个文件上发生了事件,revents表示以发生的事件
 49             {
 50                   memset(buf,0,MAX_BUFFER_SIZE);    //初始化buf数组
 51                   real_read = read(fds[i].fd,buf,MAX_BUFFER_SIZE);
 52                   if(real_read < 0)
 53                   {
 54                      if(errno != EAGAIN)
  55                      {
 56                         // printf("real_read = %d\n",real_read);
 57                          return 1;
 58                      }
 59                   }
 60                   else if(!real_read)
 61                   {
 62                       close(fds[i].fd);
 63                       fds[i].events = 0;   //取消对该文件的监听
 64                   }
 65                   else
 66                   {
 67                       if(i == 0)
 68                       {
 69                         if(buf[0] == 'q' || buf[0] == 'Q')  //输入'q'或'Q'则会退出
 70                              {
 71                               return 1;
 72                              }
 73                       }
 74                       else
 75                       {
 76                           buf[real_read] = '\0';
 77                           printf("%s\n",buf);       //将读取到的数据显示到终端上
 78                       }
 79                   }
 80             }
 81        }
 82    }
 83     return 0;
 84 }
 先用mknod指令创建两个管道in1和in2,。mknod in1 p   
                                    mknod in2 p   
 接下来在两个虚拟中端上分别运行cat>in1和cat>in2.同时在第三个虚拟中端上运行程序。(注意不要忘记>)                                                              
16:57 2017/5/3
/*****************************************************************************************************/       
18:04 2017/5/3
/*****************************************************************************************************/       
memset()函数作用:用来给给定字符串的指定范围赋值.
void *memset(void *s,int c,size_t n)
  总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c。
/*****************************************************************************************************/       
17:35 2017/5/4
/*****************************************************************************************************/       
mknod 指令可以用来创建管道:例如:mknod in1 p
       cat指令可以查看文件内容,cat>是用来创建已存在的文件。
/*****************************************************************************************************/       
常见的数据通信方式分为:并行通信和串行通信。
    1.并行通信方式指的是利用多条数据线,把数据的各比特位同时传送到达。并行传送数据速度快,传输距离短。
    2.串行通讯方式指定是利用一条数据线,把数据的每个比特位按顺序传送到达。串行传送数据速度慢,传输距离短。
/*****************************************************************************************************/       
一、对串口的认识:
   在Linux中,所有的设备文件一般都位于“/dev”下,其中串口1和串口2对应的设备名依次为"/dev/ttyS0"和"/dev/ttyS1",
而且USB转串口的设备名通常为"/dev/ttyUSB0"和"/dev/ttyUSB1",可以在"/dev"目录下查看。在Linux下对设备的操作方法和对文件的
操作方法是一样的,因此,对串口的读写就可以使用简单的read()、write()函数来完成,所不同的只是对串口的其他参数另做配置。
二、串口设置详解。
    串口的设置主要是设置struct termios结构体的各成员值。
    #include<termios.h>
    struct termios  
    {
       unsigned short c_iflag;        //输入模式标志
       unsigned short c_oflag;        //输出模式标志
       unsigned short c_cflag;        //控制模式标志
       unsigned short c_lflag;        //本地模式标志
       unsigned short c_line;         //线路规程
       unsigned char c_cc[NCC];       //控制特性
       speed_t  c_ispeed;             //输入速度
       speed_t  c_ospeed;             //输出速度
     };
   termios是在POSIX规范中定义的标准接口,表示终端设备(包括虚拟终端、串口等)。因为串口是一种终端设备,所以通过终端编程
接口对其进行配置和控制。
   终端指的是用于与计算机的接口,例如键盘,显示器等。
   终端有三种工作方式:分别是规范模式,非规范模式,原始模式。通过在termios结构的c_cflag中设置ICANNON标志来定义终端
是以规范模式(设置ICANNON标志),还是以非规范模式(清除ICANNON标志),默认情况是规范模式。
   1.规范模式:在规范模式中,所有的输入都是基于行处理的。在用于输入一个行结束符("Enter"、EOF等)之前,系统调用read()函数
读取不到任何的字符。除了EOF的行结束符,与普通的字符一样会被read()函数读入到缓冲区中。在规范模式中,行编辑是可行的,而且一次
调用read()函数最多只能读取一行数据。如果read()被请求读取的字节数小于当前可读入的字节数,则read()函数只会读取被请求的字节
数,剩下的字节下次再被读取。
   2.在非规范模式下,所有的输入都是即时有效的,也就是说不需要输入换行结束符。
     在此模式下,对参数MIN(c_cc[VMIN])和TIME(c_cc[VTIME])的设置决定read()函数的调用方式。设置可以有4种不同的情况。
      MIN = 0 和 TIME = 0 :read()函数立即返回。若有数据可以读取,则读取数据并返回数据的字节数。
      MIN > 0 和 TIME = 0 :read()函数会被阻塞知道MIN个字节数据可被读取。
      MIN = 0 和 TIME > 0 :只要有数据可读或者经过TIME个0.1s的时间,read()函数则立即返回,返回值为读取到的字节数。
如果超时并未读取到数据,则read()函数返回0.
      MIN > 0 和 TIME> 0 : 当有MIN个字节刻度或者两个输入字符之间的时间间隔超过TIME个0.1s,read()函数才返回。因为在输入
第一个字符之后系统才会启动定时器,所以在这种情况下,read()函数至少读取一个字节之后才返回。
   3.原始模式:是一种特殊的非规范模式。在原始模式下,所有的输入数据以字节位单位处理。终端是不回显的。调用cfmakeraw()
函数可以将终端设置为原始模式。
三、设置串口的基本方法。
    对串口的设置最重要的是对波特率,校验位,停止位的设置。这个结构中最稳重要的是c_cflag,通过对它设置,用户可以设置波特率、
字符大小、数据位、停止位、奇偶校验位和硬软流控。另外c_iflag和c_cc也是比较常用的标志.
四、设置串口属性的基本流程。
    1.保存原串口配置。
       为了安全和方便起见,可以先保存原串口的配置。使用函数tcgetattr(fd,&old_cfg).该函数得到由fd指向的终端的配置参数,
    并将它们保存于termios结构变量old_cfg中。该函数还可以测试配置的是否正确、该串口是否可用等。若调用成功,函数返回0,若
    调用失败,函数返回值为-1,
        例: if(tcgetattr(fd,&old_cfg) != 0)
            {
               perror("tcgetattr");
               return -1;
            }
    2.激活选项。
         CLOCAL和CREAD分别用于本地连接和接收使能,因此,首先要是通过位掩码的方式激活这两个选项。
         newtio.c_cflag |= CLOCAL|CREAD;
         调用cfmakeraw()函数可以将终端设置为原始模式。
         cfmakeraw(); 
    3.设置波特率。
         设置波特率有专门的函数,用户不能直接通过位掩码来操作。设置波特率的主要函数有:cfsetispeed()和cfsetospeed().
         这两个函数的使用如下:
         cfsetispeed(&new_cfg,B115200);
         cfsetospeed(&new_cfg,B115200);
         一般的cfsetispeed()函数设置数据输入波特率,cfsetospeed()函数设置数据输出波特率。用户需要将输入和输出波特率设置成
一样的。这几个函数,在成功的时候返回为0,失败的时候返回为-1.
    4.设置字符大小。
        需要用位掩码,首先去除数据位中的位掩码,再重新按要求设置。
        new_cfg.c_cflag & = ~CSIZE;
        new_cfg.c_cflag | = CS8;
    5.设置奇偶校验位。
        设置奇偶校验位需要用到termios中的两个成员:c_cflag和c_iflag。首先需要激活c_cflag中的校验位使能标志位PARENB,这样会对
输出数据产生校验位,而输入数据进行校验检查。同时还要激活c_iflag中的对于输入数据的奇偶校验位使能(INPCK).
        如使能奇校验时,代码如下:
        new_cfg.c_cflag |= (PARODD | PARENB);
        new_cfg.c_iflag |= INPCK; 
        使能偶校验时:
        new_cfg.c_cflag |= PARENB;
        new_cfg.c_cflag &=~PARODD;
        new_cfg.c_iflag |= INPCK;
    6.设置停止位。
        设置停止位是通过激活c_cflag中的CSTOPB而实现的。若停止位为一个,则清除CSTOPB,若停止位为两个,则激活CSTOPB。
        停止位一个比特的:new_cfg.c_cflag &= ~CSTOPB;
        停止位两个比特的:new_cfg.c_cflag |= CSTOPB;  
    7.设置最少字符和等待时间。
        在对接收字符和等待时间没有特别要求的情况下,可以将其设置为0,则在任何情况下read()函数立即返回,此时串口操作
        会设置为非阻塞方式。
        new_cfg.c_cc[VTIME] = 0;
        new_cfg.c_cc[VMIN] = 0;
    8.清除串口缓冲。
        int tcdrain(int fd);                     //使程序阻塞,直到输出缓冲区的数据全部发送完毕
        int tcflow(int fd,int action);           //用于暂停或重新开始输出
        int tcflush(int fd,int queue_selector);  //用于情况输入/输出缓冲区
        对于缓冲区中的尚未传输的数据,或者收到的但是尚未读取的数据,其处理方法取决于queue_selector的值,它可能的取值
      有以下几种方式:
        (1)TCIFLUSH :对接收到而未被处理的数据进行清空处理。
        (2)TCOFLUSH:对尚未传送成功的数据进行清空处理。
        (3)TCIOFLUSH:包括前两种功能,即对尚未处理的输入输出数据进行清空处理。
    9.激活配置。
        在配置完成后,需要激活才能使配置生效。这里用到的函数是tcsetattr(),
        函数原型是:tcsetattr(int fd,int optional_actions,const struct termios *termios_p);
        其中参数termios_p是termios类型的新配置变量。
        参数optional_actions可能的取值有以下3种:
        (1).TCSANOW:配置的修改立即生效。
        (2).TCSADRAIN:配置的修改在所有写入fd的输出都传输完毕之后生效。
        (3).TCSAFLUSH:所有已接收但未读入的输入都将在修改生效之前被丢弃。
        该函数若调用成功则返回0,失败则返回-1
        例:if((tcsetattr(fd,TCSANOW,&new_cfg)) != 0) 
        {
            perror("tcsetattr");
            return -1;
        } 
例:
  1 #include<termios.h>
  2 
  3 int set_com_config(int fd,int baud_rate,int data_bits,char parity,int stop_bits)
  4 {
  5    struct termios new_cfg,old_cfg;
  6    int speed;
  7    //先保存原串口配置。
  8     if(tcgetattr(fd,&old_cfg) != 0)
  9     {
 10         perror("tcgetattr");
 11         return -1;
 12     }
 13     //激活选项,此处将终端模式设置成原始模式
 14     cfmakeraw(&new_cfg);
 15     //设置波特率
 16     swicth(baud_rate);
 17     {
 18         case 2400:{
 19                       speed = B2400;
 20                   }break;
 21         case 4800:{
 22                       speed = B4800;
 23                   }break;
 24         case 9600:{
 25                       speed = B9600;
 26                   }break;
 27         case 19200:{
 28                       speed = B19200;
 29                   }break;
 30         case 38400:{
 31                       speed = B38400;
 32                   }break;
 33         default:
 34         case 115200:{
 35                       speed = B115200;
 36                   }break;
 37     }
 38     cfsetispeed(&new_cfg,speed);
 39     cfsetospeed(&new_cfg,speed);
 40    //设置数据位
 41     new_cfg.c_cflag &=~CISZE;     //先进行取反
 42     swicth(data_bits)
 43     {
 44         case 7:
 45             {
 46                 new_cfg.c_cflag |= CS7;
 47             }
 48             break;
 49         default:
 50         case 8:
 51             {
 52                 new_cfg.c_cflag |= CS8;
 53             }
 54             break;
 55     }
 56     //设置奇偶校验位
 57     swicth(parity)
 58     {
 59         case 'n':     //无校验位,清空始能位
 60         case 'N':
 61             {
 62                 new_cfg.c_cflag &=~PARENB;    //c_cflag:PARENB校验位使能,PARODD使用奇校验位,而不使用偶校验位
 63                 new_cfg.c_iflag &=~INPCK;     //c_iflag:INPCK奇偶校验使能
 64             }
 65             break;
 66         case 'o':
 67         case 'O':      //偶校验位
 68             {
 69                 new_cfg.c_cflag |= (PARODD | PARENB);
 70                 new_cfg.c_iflag |= INPCK;
 71             }
 72             break;
 73 
 74         case 'e':
 75         case 'E':      //奇校验位
 76             {
 77                 new_cfg.c_cflag |= PARENB;
 78                 new_cfg.c_cflag &= ~PARODD;
 79                 new_cfg.c_iflag |= INPCK;
 80             }
 81             break;
 82 
 83         case 's':
 84         case 'S':
 85             {
 86                 new_cfg.c_cflag &= ~PARENB;
 87                 new_cfg.c_cflag &= ~CSTOPB;
 88             }
 89             break;
 90         default:
 91     }
 92     switch(stop_bits)   //设置停止位
 93     {
 94         default:
 95         case 1:
 96             {
 97                 new_cfg.c_cflag &= ~CSTOPB;
 98             }break;
 99         case 2:
100             {
101                 new_cfg.c_cflag |= CSTOPB;
101                 new_cfg.c_cflag |= CSTOPB;
102             }
103     }
104     //设置等待时间和最小接收字符
105     new_cfg.c_cc[VTIME] = 0;
106     new_cfg.c_cc[VMIN] = 1;
107     tcflush(fd,TCIFLUSH);
108     if((tcsetattr(fd,TCSANOW,&new_cfg)) != 0 )
109     {
110         perror("tcsetattr");
111         return -1;
112     }
113     return 0;
114 }
/*****************************************************************************************************/       
16:16 2017/5/5
/*****************************************************************************************************/       
串口使用步骤:
   1.打开串口。
   打开串口和打开普通文件是一样的,都是使用open()函数。
   fd = open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
   可以看到,这里除了普通的参数外,还有两个参数O_NOCTTY和O_NDELAY
   (1).O_NOCTTY标志用于通知Linux系统,请参数不会使打开的文件成为这个进程的控制终端。如果没有指定这个标志,那么任何一个
输入(诸如键盘中止信号等)都将会影响用户的进程。
   (2).O_NDELAY标志用于设置非阻塞方式。通知Linux系统,这个程序不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。
如果用户没有指定在这个标志,则进程将会一直处在睡眠状态,直到DCD信号线被激活。
    接下来可恢复串口的状态为阻塞状态,用于等待串口数据的读入,可用fcntl()函数实现,如下所示:
    fcntl(fd,F_SETFL,0);
    接下来可以测试打开的文件描述符是否连接到一个终端设备,以进一步确认串口是否正确打开,如下所示:
    isatty(fd);
    该函数调用成功后则返回0,若失败则返回-1;
    这时,一个串口就已经成功打开了。接下来就可以对这个串口进行读和写的操作。
17:17 2017/5/8
/*****************************************************************************************************/       
17:49 2017/5/8
比较字符串大小的函数:
1.头文件:#include <string.h>
   strncmp() 用来比较两个字符串的前n个字符,区分大小写,其原型为:
   int strncmp ( const char * str1, const char * str2, size_t n );
   说明:
        当s1<s2时,返回值<0
        当s1=s2时,返回值=0
        当s1>s2时,返回值>0
2、strcmp() 用来比较字符串(区分大小写),其原型为:
    int strcmp(const char *s1, const char *s2);
【参数】s1, s2 为需要比较的两个字符串。
【返回值】若参数s1 和s2 字符串相同则返回0。s1 若大于s2 则返回大于0 的值。s1 若小于s2 则返回小于0 的值。
/*****************************************************************************************************/       
对标准I/O编程的认识:
   open(),read(),write(),close(),lseek(),fcntl()等函数使基于文件描述符操作的。是不带缓存的。
   而标准I/O操作是基于缓冲流的。
   前面的系统调用是系统直接提供的函数接口。因为运行系统调用时,Linux必须从用户态切换到内核态,执行相应的请求,然后
再返回到用户状态,所以应该尽量减少系统的调用次数,从而提高程序的效率。
   标准I/O提供缓冲流的目的是尽可能减少read()和write()函数的调用的的数量。标准的I/O提供了3种类型的缓冲存储。
   (1).全缓冲:在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。对于存放在磁盘上的文件通常是由标准I/O库实施全
缓冲的。标准I/O尽量多读写文件到缓冲区,当缓冲区已满或手动调用flush时才会进行磁盘操作。
  (2).行缓冲:在这种情况下,当在输入和输出种遇到行结束符时,标准I/O库执行I/O操作。这允许一次输出一个字符(如fputc()函数)
,但只有写了一行之后才进行实际I/O操作。标准输入和标准输出就是使用行缓冲的典型例子。
  (3).不带缓冲:标准I/O库不对字符进行缓冲。如果用标准I/O函数写若干字符到不带缓冲的流中,则相当于用系统调用write()函数将
这些字符全写到被打开的文件上。标准出错stderr通常是不带缓存的,这就使得存错信息尽可能快的显示出来,而不管他们是否含有一个行
转化符。
一、基本操作。
   1.打开文件。
   打开文件有3个标准函数,分别为:fopen(),fdopen()和freopen().它们可以以不同模式打开,但都返回一个指向FILE的指针,改指针
都指向对应的I/O流。此后,对文件的读写都是通过这个FILE指针来进行。
   -fopen()可以指定打开文件的路径和模式。
   -fdopen()可以指定打开的文件描述符和模式。
   -freopen()除可指定打开的文件、模式外,还可指定特定的I/O流。
   语法要点:
    (1).fopen():
        -头文件:#include<stdio.h>
        -FILE * fopen(const char * path,const char * mode)
        -函数传入值:path:包含要打开的文件路径及文件名。
                    mode:文件打开状态
        -函数返回值:成功:指向FILE的指针。
                     失败:NULL.
        --对于mode的取值:r或rb:打开只读文件,该文件必须存在。
                         r+或r+b:打开可读写的文件,该文件必须存在。
                         w或wb:打开只写文件,若文件存在则文件长度清为0,即会擦写文件以前的内容。
                                若文件不存在则建立该文件。
                         w+或w+b:打开可读写文件,若文件存在则文件长度清0,即会擦写文件以前的内容。若文件不存在则
                                建立该文件。
                         a或ab:以附加的方式打开只写文件。若文件不存在,则会建立该文件;若文件存在,写入的数据会被加
                                到文件尾,即文件原些的内容会被保留。
                         a+或a+b:以附加的方式打开可读写文件,若文件不存在则会建立该文件;若文件存在,写入的数据会被加
                                到文件尾,即文件原些的内容会被保留。                      
   (2).fdopen():
       -头文件:#include<stdio.h>
       -函数原型:FILE *fdopen(int fd,const char *mode);
       -传入的函数值:fd:要打开的文件描述符。
                     mode:文件打开的状态。
       -函数返回值:成功:指向FILE的指针。
                   失败:NULL.         
   (3).freopen()
       -头文件:#include<stdio.h>
       -函数原型:FILE *freopen(const char *path,const char *mode,FILE *stream)
       -函数传入值:path:包含要打开的文件路径及文件名。
                   mode:文件打开状态。
                   stream:已打开的文件指针。
       -函数返回值:成功:指向FILE的指针。
                    失败:NULL.
   2.关闭文件:
     关闭标准文件流的函数是fclose(),该函数将缓冲区内的数据全部写入到文件中,并释放系统所提供的文件资源。
     fclose()函数要点:     
       -所需要的头文件:#include<stdio.h>
       -函数原型:int fclose(FILE *stream) 
       -函数传入值:stream:以打开的文件指针。
       -函数返回值:成功:0
                    失败:EOF.
   3.读文件
     在文件流被打开以后,可以对文件流进行读写操作,其中读操作函数是fread().
     fread():
        -所需要头文件:#include<stdio.h>
        -size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream)
        -函数的传入值:ptr:存放读入记录的缓冲区。
                      size:读取的记录大小。
                      nmemb:读取的记录数。
                      stream:要读取的文件流。
        -函数返回值:成功:返回实际读取到的nmemb数目
                                  失败:EOF.  
    4.写文件
      fwrite()函数是用于对指定的文件流进行写操作。
      fwrite():
         -所需要的头文件:#include<stdio.h>
         -函数原型:size_t fwrite(const void *ptr,size_t size,size_t nmmemb,FILE *stream)
         -函数传入值:ptr:存放写入记录的缓冲区。
                     size:写入的记录大小。
                     nmemb:写入的记录数。
                     stream:要写入的文件流。
         -函数返回值:成功:返回实际写入到的nmemb数目。
                      失败:EOF.
    5.其他操作:
       文件打开之后,根据一次读写文件中字符的数目可分为字符输入输出、行输入输出和格式化输入输出。
       1、字符输入输出
       字符输入输出函数一次仅读写一个字符。
       字符输出函数要点:
            -所需要的头文件:#include<stdio.h>
            -函数原型:int getc(FILE *stream)
                       int fgetc(FILE *stream)
                       int getchar(void)
            -函数传入值:stream:要输入的文件流。
            -函数返回值:成功:下一个字符。
                        失败:EOF.
       字符输入函数语法要点:
            -所需要的头文件:#include<stdio.h>
            -函数原型:int putc(int c,FILE *stream)
                       int fputc(int c,FILE *stream)
                       int putchar(int c)
            -函数值:成功:字符c
                     失败:EOF.
       例:#include<stdio.h>
           main()
           {
              int c;
              fputc(fgetc(stdin),stdout);
           }
       2.行输入输出
       行输入输出函数一次操作一行。
       行输出函数语法要点:
           -所需要的头文件:#include<stdio.h>
           -函数原型:char *gets(char *s);
                     char *fgets(char *s,int size,FILE *stream);


           -函数传入值:s:要输入的字符串。
                       size:输入的字符串长度。
                       stream:对应的文件流。
           -函数返回值:成功:s
                        失败:NULL                        
       行输入语法要点:
           -所需要的头文件:#include<stdio.h>
           -函数原型:int puts(const char *s)
                      int fputs(const char *s,FILE *stream)
           -函数传入值:s:要输出的字符串
                       stream:对应的文件流
           -函数返回值:成功:s
                        失败:NULL
         例:#include<stdio.h>
             main()
            {
                char s[80]; 
                fputs(fgets(s,80,stdin),stdout);
            }
        3.格式化输入输出
           格式化输入输出函可以指定输入输出的具体格式。
           格式化输出函数1:
                -所需要的头文件:#include<stdio.h>
                -函数原型:int printf(const char *format,...)
                           int fprintf(FILE *fp,const char *format,...)
                           int sprintf(char *buf,const char *format,...)
                -函数传入值:format:记录输出格式
                             fp:文件描述符。
                             buf:记录输出缓冲区。
                -函数返回值:成功:输出字符数(sprintf返回存入数组中的字符数)
                             失败:NULL
           格式化输出函数2:
               -所需要的头文件:#include<stdarg.h>
                              #include<stdio.h>
               -函数原型:int vprintf(const char *format,va_list arg)
                         int vfprintf(FILE *fp,const char *format,va_list arg)
                         int vsprintf(char *buf,const char *format,va_list arg)      
               -函数传入值:format:记录输出格式。
                            fp:文件描述符
                           arg:相关命令参数。
               -函数返回值:成功:存入数组的字符数。
                            失败:NULL.
           格式化输入函数:
               -所需要的头文件:#include<stdio.h>
               -函数原型:int scanf(const char *format,...)
                          int fscanf(FILE *fp,const char *fomat,...)
                          int sscanf(char *buf,cosnt char *format,...)
               -函数传入值:format:记录输出格式
                            fp:文件描述符。
                            buf:记录输入缓冲区。
               -函数返回值:成功:输出字符数(sprintf返回存入数组中的字符数)
                            失败:NULL
7:38 2017/5/9
/*****************************************************************************************************/       
16:15 2017/5/9
/*****************************************************************************************************/       
当对文件进行操作时,要保证文件尽量是原子操作。
    原子操作保证当内核调用系统操作时,保证其操作单一独立完成,期间不被其他进程或线程打断。
对文件上锁可以保证进程对文件的访问是原子操作。
17:21 2017/5/10
注意给给字符型数组赋值时不能直接用'=',而应该用strcpy()函数。
/*****************************************************************************************************/       
feof():
头文件:#include <stdio.h>
feof()函数用来检测当前文件流上的文件结束标识,判断是否读到了文件结尾,其原型为:
    int feof(FILE * stream);
【参数】stream为文件流指针。
【返回值】检测到文件结束标识返回1,否则返回0。
/*****************************************************************************************************/       


18:46 2017/5/10
/*****************************************************************************************************/       
关于fread和fwrite函数,第二个参数读什么类型的就写什么类型的。比如:
typedef struct{
     char name[BUFFER_MAX];   //账户名
     char ps[BUFFER_MAX];     //密码
     int power;               //权限
}user;
user tmp,show;
fread(&show,sizeof( user),1,fp);//sizeof里面不是char
fwrite(&tmp,sizeof(user),1,fp);
这样的写入的数据类型就是user类型的了。
/*****************************************************************************************************/       
19:58 2017/5/10
/*****************************************************************************************************/       
goto 函数只能在函数内跳转,函数间跳转用setjmp和longjmp函数。
但是goto语句也只限于一个函数内,不能进行函数间的跳转。在c语言中,进行函数间的跳转使用setjmp和longjmp函数。
使用方法:
   头文件:#include <setjmp.h>
   定义一个jmp_buf的全局变量。
   int setjmp(jmp_buf env);   //直接调用则返回0,如从longjmp调用则返回非0,用来设置跳转返回结点
   int longjmp(jmp_buf env, int val);设置跳转结点,val用来判断是哪个结点返回。
   
19:49 2017/5/12
/*****************************************************************************************************/       
16:39 2017/5/13
/*****************************************************************************************************/       
char类型的数组其数组名就是字符的首地址,其每个元素的地址都是挨着的,所以其首地址就是字符串的地址。
例如:
   char a[10] = {'a','c','w','k','q'};
则char *p;
  p = a;
  *p = "acwkq";
/*****************************************************************************************************/       
17:42 2017/5/13
/*****************************************************************************************************/       
指向结构体类型变量的使用
首先让我们定义结构体:
struct stu
{
char name[20];
long number;
float score[4];
} ;
再定义指向结构体类型变量的指针变量:
struct stu *p1, *p2 ;
定义指针变量p 1、p 2,分别指向结构体类型变量。引用形式为:指针变量→成员;
/*****************************************************************************************************/       
19:09 2017/5/13
/*****************************************************************************************************/
对于一组字符串,要想单个操作其中的一个字符,可以使用这样的方法:
  1.已知一个字符串:char *p = "asdasfafzx";
    先定义一个char类型的数组:char str[20];
  2.利用strcpy()函数给str[20]数组赋值:
    strcpy(str,p);       
  3.在创建一个char类型指针char *q;
    q = str;
    则q[i]就可以对单个字符进型操作。
例: char *p ;
     char str[20] = "asdascxzc";
     p = str;
     for(;i<6;i++)
     printf("%c",p[i]);
/*****************************************************************************************************/
9:08 2017/5/14  心智模式,成功是速度不是高度,成功就是越走越近
/*****************************************************************************************************/
硬件学习:
  布板子,用AD。
  电路设计工具以及电路常识。
  夸学科。
  有两种工具: AD(低频),Cadence(高频)。
  电阻:103->10*10^3(10%)
        1002->100*10^2(1%)
        470->47*10^0
        471->47*10^0
        4R7->4.7
  电容:104->10*10^4PF
            10^5 PF
            0.1*10^6PF
            0.1uF
       106->10*10^6PF  
  精密低温漂:精度要求1%,多温度要求高。
  电感:inductor
  二极管:D*
  发光二极管:LED
  晶振:XTAL
  三极管:NPN.PNP
  按键:SW
  继电器:relay,弱点跟强电进行转换.
  电池: battery
  蜂鸣器:bell
  单排针:header
  天线:antenna
快捷键:鼠标: 左键+TAB,调出属性,或左键双击。
              左键+X,左右反转。
              左键+Y,上下反转。
             左键+空格,90度反转。
              左键+shift,复制。
              滚轮:放大缩小。
                    pageup 和pageon。
              右键:移动。
              点击对象,按ctrl键,先不断移动对象
   TTL:    逻辑1是3.3v      逻辑0是0v
               5v            0v
   rs232:     -9v - -15v   +9v - +15v
   差分(485):con、usb、网线也是差分信号。
   逻辑上对应的标准不同。   
/*****************************************************************************************************/
10:47 2017/5/14
/*****************************************************************************************************/
最小系统:
      能够工作起来的最小工作单元。
      1.CPU 2.时基电路(晶振) 3.复位 4.调试接口 5.I/O接口


在名称上面加横线是字\
/*****************************************************************************************************/
16:32 2017/5/14
/*****************************************************************************************************/
1.改变字符数组的内容时不能直接赋值,要使用strcpy函数进行拷贝内容。
2.使用库函数返回一个字符串时,也就是返回一个字符串的首地址,要申请
动态内存,用malloc函数,动态内存是安全的地方申请动态内存,要想返回字符传地址,要把它放在安全的地方,
即动态内存中,库函数都是在静态内存中,使用完后会全部撤回。
例如:char * fun(){
                 char s[100];
                 //...代码
                 //return s;//原来你自己写的返回语句,改为下面的语句
                 char* str = malloc(strlen(s)+1);
                 strcpy(str, s);
                 return str;
                }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值