天使羊波波闪耀光芒 软件及互联网爱好者

215月/160

C语言sprintf例子

sprintf使用例子:

1、BCD转换成字符

char src = 0x3A;
char tmp[6];
memset(tmp, 0x00, sizeof(tmp));
sprintf(tmp, "%02X", src);

说明:
1)打印出tmp为3A
2)%02X含义:
02:0表示长度不足用0填充,2表示格式化后总长度是2
X:表示以大写16进制形式输出

2、字符串数值相加

char * p = "000000000100";
char * p2 = "000000000099";
char * p3 = "000000876666";
double d = 0.0;
d = atof(p); //atof字符串转double
d += atof(p2);
d += atof(p3);
char temp[16];
memset(temp, 0x00, sizeof(temp));
sprintf(temp, "%016.0lf", d);

说明:
1)打印出temp为0000000000876865
2)%016.0lf含义:
016:0表示长度不足用0来填充,16表示格式化后的总长度是16
.0lf:.0表示小数点后长度是0,整数部分=总长度-小数点后长度。lf是long float型,与double同义

122月/150

Linux下子进程回收

Linux中有以下几个方法用于子进程回收:

1、父进程使用wait()和waitpid()函数等待子进程结束,父进程会挂起。

2、父进程fork()一个子进程,父进程退出,子进程由init进程接管。

3、父进程使用signal(SIGCHLD, SIG_IGN)通知内核忽略SIGCHLD信号,子进程结束后,由内核回收,并不再给父进程发送SIGCHLD信号。

4、父进程使用signal(SIGCHLD, proc)注册一个函数,子进程结束后,父进程收到SIGCHLD信号,在定义的proc函数中调用wait()处理。

102月/150

signal和sigset函数

signal和sigset都是注册信号函数

signal是ANSI C的信号处理函数
sigset是System V的信号处理函数

ANSI C(标准C)是所有平台C程序都支持的,System V只在Unix/Linux使用。

如果程序运行在Linux平台,两者都可以,但是换到windows平台,信号处理就要用signal()了。

191月/153

Linux time函数

Linux下time函数都在time.h头文件中。

1、头文件
和时间有关的头文件有以下几个:

time.h
sys/time.h
sys/times.h
sys/timeb.h
sys/timex.h

time.h是C标准库的头文件,其余sys开头的都是Linux系统自己的头文件。
/usr/include/time.h定义了常用的time函数。
到/usr/include/sys目录下查看这几个文件:
sys/time.h定义了timezone结构体和Linux系统的时间函数。
sys/times.h定义了进程使用CPU时间的结构体tms。
sys/timeb.h定义了ftime函数的返回值的结构体timeb。
sys/timex.h定义了关于时钟调整算法的结构体timex。

2、常用函数和结构体
time函数原型(time.h中):

time_t time(time_t *calptr);

参数:
time_t类型变量的指针。
返回值:
time_t类型相当于一个long,time用于取Epoch记年以来到现在经过的秒数(系统当前时间),Epoch记年从1970年1月1日开始。把取到的时间存在指针指向的变量中。

51月/150

Linux信号掩码和信号处理函数

1、信号掩码——被阻塞的信号集
每个进程都有一个用来描述哪些信号传送来将被阻塞的信号集,如果某种信号在某个进程的阻塞信号集中,则传送到该进程的此种信号将会被阻塞。当前被进程阻塞的信号集也叫信号掩码,类型为sigset_t。每个进程都有自己的信号掩码,且创建子进程时,子进程会继承父进程的信号掩码。

2、信号阻塞和忽略的区别
阻塞的概念与忽略信号是不同的:操作系统在信号被进程解除阻塞之前不会将信号传递出去,被阻塞的信号也不会影响进程的行为,信号只是暂时被阻止传递;当进程忽略一个信号时,信号会被传递出去,但进程将信号丢弃。

每个进程都有一个信号掩码,信号掩码是一个“位图”,如果位图中某位标识1,表示该位对应的信号被暂时屏蔽。如果标识为0,表示进程可以接收这个信号。

System V signal API信号处理函数除了sigset外,还有以下几个。

sighold()函数
把指定的信号增加到信号掩码中去(将位图中对应的位标识为1),这样就暂时屏蔽了该信号。
返回值:成功返回0,失败返回-1。

sigrelse()函数
从信号掩码中移除指定的信号,这样进程就可以接收该信号了。
返回值:成功返回0,失败返回-1。

sigignore()函数
将信号配置为SIG_IGN,即忽略该信号。
返回值:成功返回0,失败返回-1。
sigignore(SIGALRM)相当于sigset(SIGALRM, SIG_IGN)

3112月/140

Linux中alarm函数和信号处理

alarm函数作用是设定一个计时器,到时间后会向进程发送信号。
它向进程发送SIGALRM信号。要注意的是,一个进程只能有一个闹钟时间,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。
我们还可以调用alarm(0)来取消此闹钟,并返回剩余时间。

函数原型:
unsigned int alarm(unsigned int seconds);
函数参数:
seconds:指定的秒数

2812月/140

Linux下消息队列学习(2)

一、消息队列系统参数设置
在Linux系统下有两个参数可以设置消息队列的大小:

vim /etc/sysctl.conf

加入:

# Controls the default maxmimum size of a mesage queue
kernel.msgmnb = 6553600

# Controls the maximum size of a message, in bytes
kernel.msgmax = 65536

参数说明:
kernel.msgmnb:每个消息队列的大小(字节)
kernel.msgmax:消息队列中一个消息的最大长度(字节)

按照以上设置,一个消息队列最大长度可以为6553600字节,每个消息最大长度65536字节,所以该消息队列可存储100条消息。

二、消息队列的特点
1)消息队列就是一个消息的链表。消息队列允许一个或多个进程向它写入或读取消息,而且消息是按消息类型访问与写入。如果读队列使用的消息类型为0,则读取消息队列中的第一条消息。
2)消息队列是随内核持续的,即当使用该消息队列的进程结束,或者已关闭该消息队列,该队列中的消息不会随之消失,只有在内核重新初始化,即计算机重启之后才会消失,因此成为内核的持续性,这点也是与管道和FIFO的区别。
3)消息队列的另一个特性是,在某一个进程往消息队列写消息之前不需要另外某个进程在该消息队列上等待消息的到达,即不会像管道、FIFO那样,如果往管道和FIFO写数据时,如果没有一个进程已经将读端打开,那么写操作会被阻塞。当然如果从消息队列读取数据时,消息队列为空时可以按设置的flag决定是否阻塞。

1312月/140

Linux下消息队列学习(1)

消息队列是进程间通讯的一种方法,一开始我以为消息队列是类似一个管道,一头连接一个进程、一头连接另一个进程,只能由这两个进程来进行互相的读写。其实这是错的,消息队列是系统层面的,它不属于某两个进程,它是由系统维护的一个链表结构。对消息队列的读写就是一个对链表的操作,默认是在链表的一端写数据,另一端读数据(先进先出),进程也可以取指定某种消息类型的消息。

在一个进程里创建了消息队列,且是可读可写的,那么系统中的所有进程都可以对它进行读写操作。

1、打开或创建一个消息队列
原型:int msgget(key_t key, int msgflg);
参数:
1)key :消息队列的key值。
2)msgflg:
IPC_CREAT:如果key对应的消息队列对象不存在,则创建;否则则进行打开操作,返回0。
IPC_EXCL:如果key对应的消息队列对象不存在,则返回-1;否则则进行打开操作,返回0。
权限控制:0666表示可读可写,和上面的IPC_CREAT做逻辑或操作。
返回值:
成功返回,创建的或打开的消息队列的id。
失败返回-1。

例子程序:test1.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>

int main(void)
{
	int msgid;
	printf("this is test1!\n");
	
	msgid = msgget(1001, 0666|IPC_CREAT);
	printf("msgid = %d\n", msgid);
	
	return 0;
}

138月/140

C++学习

1、C++中引用和指针的区别
指针保存的是一个对象的地址,引用是这个对象的别名。
int a = 1;
int * p = &a;
这里变量a和指针p本身地址是不一样的。

int a = 1;
int &b = a;
这里变量a和引用变量b的地址是一样的,两个变量指向同一个内存地址。

主要区别:
引用变量不能为空,在定义时必须初始化。
引用变量初始化后,引用的对象不能再改变。b引用了a后,不能再引用c变量。

总结:
指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名,引用不改变指向。

指针和引用作为函数参数进行传递时的区别:
传值调用、传址调用、引用调用中传址调用、引用调用都可以改变传入的实参的值。
区别在于指针调用也是将指针变量的值(所指向变量的地址的拷贝)传入形参,在函数内修改所指向变量的值。
引用调用直接把实参传入了函数内,修改了形参就修改了实参。

2、__cdecl和__stdcall区别
两者的区别是清栈方式不同,如果使用__stdcall由函数自己清栈,如果使用__cdecl由调用者自己清栈。

当函数参数个数不定时,只能使用__cdecl来说明函数的调用方式。
如:int sprintf(unsigned char *out, const unsigned char *format, ...)

3、类中的静态变量和静态函数
类中的静态变量是属于类的不属于某个对象,它是所有对象所共用的。
静态数据成员需要单独初始化:
< 数据类型>< 类名>::< 静态数据成员名>=< 值>

静态成员函数可以使用静态数据成员,非静态数据成员要通过具体对象来引用(因为静态成员函数没有this指针)。
静态成员函数主要为了调用方便,不需要生成对象就能调用。

27月/140

将15位一代身份证号转换成18位二代身份证号

原来的一代身份证号是15位的,现在用的二代身份证号是18位的,它们之间有一个转换规则。
一代:340524800101001
二代:34052419800101001X
可以看到它们之间的区别是二代在年份前多了19,最后面多了一位校验位

第十八位数字的计算方法为:
1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
2.将这17位数字和系数相乘的结果相加。
3.用加出来和除以11,看余数是多少?
4余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2。
5.通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。

以下代码VC6编译测试通过:

#include<stdio.h>
#include<string.h>

void id_card_convert(char * id)
{
  char tmp[20];
  int i, j ,sum = 0;
  char verify[11] = {'1','0','X','9','8','7','6','5','4','3','2'};//校验位
  int num[17] = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};//系数

  //转换号码15位到18位
  memset(tmp, 0x00, sizeof(tmp));
  memcpy(tmp, id, 6);//复制前6位
  memcpy(&tmp[6], "19", 2);//前6位后补19
  memcpy(&tmp[8], id+6, 9);//复制后9位
  
  for (i = 0; i < 17; i++)//现在tmp有17位算第18位校验位
  {
    sum = sum + (tmp[i]-0x30)*num[i];
  }

  j = sum % 11;//从verify数组中找到第j位就是校验值

  tmp[17] = verify[j];
  
  memcpy(id, tmp, 20);  
}

void main()
{
  char id_card[20];
  memset(id_card, 0x00, sizeof(id_card));
  memcpy(id_card, "340524800101001", 15);
  printf("原身份证号:%s\n", id_card);
  id_card_convert(id_card);
  printf("转换后:%s\n", id_card); //显示34052419800101001X是正确值
}

参考资料:
http://blog.csdn.net/ylqmf/article/details/4904483

第 1 页,共 2 页12