字符处理函数:
size_t strlcpy (char *dst, const char *src, size_t dst_sz)
{size_t n;
for (n = 0; n < dst_sz; n++)
{
if ((*dst++ = *src++) == '\0')
break;
}
if (n < dst_sz)
return n;
if (n > 0)
*(dst - 1) = '\0';
return n + strlen (src);
}
size_t strlcat (char *dst, const char *src, size_t dst_sz)
{
size_t len = strlen(dst);
if (dst_sz < len)
/* the total size of dst is less than the string it contains;
this could be considered bad input, but we might as well
handle it */
return len + strlen(src);
return len + strlcpy (dst + len, src, dst_sz - len);
}
用法:
memset(defaultTestUrl,0,255);//注意字符数组的初始化处理!
bzero(buf,sizeof(buf));
bzero(cmdRecv,sizeof(cmdRecv));
strlcat(defaultTestUrl, "rtsp://", sizeof(defaultTestUrl));
strlcpy(defaultServerIp,ips[playnum],sizeof(defaultServerIp));
strlcat(defaultTestUrl, defaultServerIp, sizeof(defaultTestUrl));
strlcat(defaultTestUrl, "/", sizeof(defaultTestUrl));
strlcat(defaultTestUrl,videoNames[playnum], sizeof(defaultTestUrl));
strlcat(defaultTestUrl,"\0", sizeof(defaultTestUrl));
strlcpy(user->testurl,defaultTestUrl,sizeof(user->testurl));
buffer[BUFFMAXSIZE-1]='\0'; //注意在字符串拷贝或链接组合时记得最后要给字符串数组的最后一位赋值'\0' !
while (。。。){
。。。
buffer[i]=ch;
。。。
}
buffer[BUFFMAXSIZE-1]='\0';
write(cfd,buffer,strlen(buffer));
我们经常涉及到数字与字符串之间的转换,例如将32位无符号整数的ip地址转换为点分十进制的ip地址字符串,或者反过来。从给定的字符串中提取相关内容,例如给定一个地址:http://www.bokeyuan.cn:2345,我们要从地址中提出协议,主机地址和端口号。之前对字符串和数字之间的关系不是很熟悉,工作中经常涉及到这个,如是好好总结一下。C语言提供了一些列的格式化输入输出函数,最基本的是面向控制台标准输出和输入的printf和scanf,其实还有面向字符串的sprint和sscanf,面向文件的流的fprintf和fscanf。今天着重总结一下sprintf和sscanf系列函数,这两个函数类似于scanf和printf ,不同点是从字符串*buffer用于输入输出。
2、sprintf函数
sprintf函数原型为 int sprintf(char *str, const char *format, ...)。作用是格式化字符串,具体功能如下所示:
(1)将数字变量转换为字符串。
(2)得到整型变量的16进制和8进制字符串。
(3)连接多个字符串。
举例如下所示:
1 char str[256] = { 0 };
2 int data = 1024;
3 //将data转换为字符串
4 sprintf(str,"%d",data);
5 //获取data的十六进制
6 sprintf(str,"0x%X",data);
7 //获取data的八进制
8 sprintf(str,"0%o",data);
9 const char *s1 = "Hello";
10 const char *s2 = "World";
11 //连接字符串s1和s2
12 sprintf(str,"%s %s",s1,s2);
3、sscanf函数
sscanf函数原型为int sscanf(const char *str, const char *format, ...)。将参数str的字符串根据参数format字符串来转换并格式化数据,转换后的结果存于对应的参数内。具体功能如下:
(1)根据格式从字符串中提取数据。如从字符串中取出整数、浮点数和字符串等。
(2)取指定长度的字符串
(3)取到指定字符为止的字符串
(4)取仅包含指定字符集的字符串
(5)取到指定字符集为止的字符串
sscanf可以支持格式字符%[]:
(1)-: 表示范围,如:%[1-9]表示只读取1-9这几个数字 %[a-z]表示只读取a-z小写字母,类似地 %[A-Z]只读取大写字母
(2)^: 表示不取,如:%[^1]表示读取除'1'以外的所有字符 %[^/]表示除/以外的所有字符
(3),: 范围可以用","相连接 如%[1-9,a-z]表示同时取1-9数字和a-z小写字母
(4)原则:从第一个在指定范围内的数字开始读取,到第一个不在范围内的数字结束%s 可以看成%[] 的一个特例 %[^ ](注意^后面有一个空格!)
解析网址的例子如下所示:
1 const char *s = "http://www.baidu.com:1234";
2 char protocol[32] = { 0 };
3 char host[128] = { 0 };
4 char port[8] = { 0 };
5 sscanf(s,"%[^:]://%[^:]:%[1-9]",protocol,host,port);
6
7 printf("protocol: %s\n",protocol);
8 printf("host: %s\n",host);
9 printf("port: %s\n",port);
10
4、snprintf函数
snprintf函数是sprintf函数的更加安全版本,考虑到字符串的字节数,防止了字符串溢出。函数形式为:int snprintf(char *restrict buf, size_t n, const char * restrict format, ...);。最多从源串中拷贝n-1个字符到目标串中,然后再在后面加一个0。所以如果目标串的大小为n 的话,将不会溢出。
int snprintf(char *restrict buf, size_t n, const char * restrict format, ...);
函数说明:最多从源串中拷贝n-1个字符到目标串中,然后再在后面加一个0。所以如果目标串的大小为n 的话,将不会溢出。
函数返回值:若成功则返回欲写入的字符串长度,若出错则返回负值。
Result1(推荐的用法)
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[10]={0,};
snprintf(str, sizeof(str), "0123456789012345678");
printf("str=%s/n", str);
return 0;
}
sscanf() - 从一个字符串中读进与指定格式相符的数据.
函数原型:
Int sscanf( string str, string fmt, mixed var1, mixed var2 ... );
int scanf( const char *format [,argument]... );
说明:
sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。
其中的format可以是一个或多个 {%[*] [width] [{h | l | I64 | L}]type | ' ' | '\t' | '\n' | 非%符号}
注:
1、 * 亦可用于格式中, (即 %*d 和 %*s) 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中)
2、{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d。
3、width表示读取宽度。
4、{h | l | I64 | L}:参数的size,通常h表示单字节size,I表示2字节 size,L表示4字节size(double例外),l64表示8字节size。
5、type :这就很多了,就是%s,%d之类。
6、特别的:%*[width] [{h | l | I64 | L}]type 表示满足该条件的被过滤掉,不会向目标参数中写入值
支持集合操作:
%[a-z] 表示匹配a到z中任意字符,贪婪性(尽可能多的匹配)
%[aB'] 匹配a、B、'中一员,贪婪性
%[^a] 匹配非a的任意字符,贪婪性
例子:
1. 常见用法。
char buf[512] = {0};
sscanf("123456 ", "%s", buf);
printf("%s\n", buf);
结果为:123456
2. 取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。
sscanf("123456 ", "%4s", buf);
printf("%s\n", buf);
结果为:1234
3. 取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。
sscanf("123456 abcdedf", "%[^ ]", buf);
printf("%s\n", buf);
结果为:123456
4. 取仅包含指定字符集的字符串。如在下例中,取仅包含1到9和小写字母的字符串。
sscanf("123456abcdedfBCDEF", "%[1-9a-z]", buf);
printf("%s\n", buf);
结果为:123456abcdedf
5. 取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。
sscanf("123456abcdedfBCDEF", "%[^A-Z]", buf);
printf("%s\n", buf);
结果为:123456abcdedf
6、给定一个字符串iios/12DDWDFF@122,获取 / 和 @ 之间的字符串,先将 "iios/"过滤掉,再将非'@'的一串内容送到buf中
sscanf("iios/12DDWDFF@122", "%*[^/]/%[^@]", buf);
printf("%s\n", buf);
结果为:12DDWDFF
7、给定一个字符串““hello, world”,仅保留world。(注意:“,”之后有一空格)
sscanf(“hello, world”, "%*s%s", buf);
printf("%s\n", buf);
结果为:world
%*s表示第一个匹配到的%s被过滤掉,即hello被过滤了
如果没有空格则结果为NULL。
Sprintf:
sprintf 是个变参函数,定义如下:
int sprintf( char *buffer, const char *format [, argument] ... );
除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:
格式化字符串上。
fprintf():
按格式输入到流, 其原型是int fprintf(FILE *stream, const char *format[, argument, ...]);其用法和printf()相同,不过不是写到控制台,而是写到流罢了
例:fprintf(fp,"%2d%s",4,"Hahaha");
fscanf():
从流中按格式读取 ,其原型是int fscanf(FILE *stream, const char *format[, address, ...]);其用法和scanf()相同,不过不是从控制台读取,而是从流读取罢了。
例:fscanf(fp,"%d%d" ,&x,&y);
f开头代表 file
可以这么理解.什么都不带的, 在标准 键盘 屏幕(控制台) 出入输出.
- if(1 == value) //注意在作比价时要把常量放在前面,这样效率会好些!
- return 1;
- char localMemory[MAX_SET_STR_LENGTH] = {0}; //注意在定义字符串指针等变量时记得进行初始化处理!
- int* pData = NULL; //注意在定义字符串指针等变量时记得进行初始化处理!
- #include <string.h>
- #include <assert.h>
- FruitShop* create_fruit_shop(int color)
- {
- FruitShop* pFruitShop = (FruitShop*) malloc(sizeof(FruitShop));
- assert(NULL != pFruitShop); //注意时刻用assert函数来判断函数参数及数组指针是否为空,防止后面处理空指针,造成内存泄露!
- if(WHITE == color) //注意在作比价时要把常量放在前面,这样效率会好些!
- {
- pFruitShop->sell_apple = sell_white_apple;
- pFruitShop->sell_grape = sell_white_grape;
- }
- else
- {
- pFruitShop->sell_apple = sell_red_apple;
- pFruitShop->sell_grape = sell_red_grape;
- }
- return pFruitShop;
- }
继承性
- typedef struct _parent
- {
- int data_parent;
- }Parent;
- typedef struct _Child
- {
- struct _parent parent;
- int data_child;
- }Child;
- typedef struct _Action
- {
- int type;
- struct _Action* next; //结构体里面存放其他结构体
- void* pData;
- void (*process)(void* pData); //结构体里面存放函数的方式
- }Action;
-
- typedef struct _Organizer
- {
- int number;
- Action* pActionHead; //结构体里面存放其他结构体
- Action* (*create)(); //结构体里面存放函数的方式
- void (*restore)(struct _Organizer* pOrganizer); //结构体里面存放函数的方式
- }Organizer;
-
- void restore(struct _Organizer* pOrganizer)
- {
- Action* pHead;
- assert(NULL != pOrganizer);
- pHead = pOrganizer->pActionHead;
- pHead->process(pHead->pData);
- pOrganizer->pActionHead = pHead->next;
- pOrganizer->number --;
- free(pHead);
- return;
- }
- typedef struct _AssemblePersonalComputer
- {
- void (*assemble_cpu)();
- void (*assemble_memory)();
- void (*assemble_harddisk)();
- }AssemblePersonalComputer;
- void assemble_intel_cpu()
- {
- printf("intel cpu!\n");
- }
- void assemble_samsung_memory()
- {
- printf("samsung memory!\n");
- }
- void assemble_hitachi_harddisk()
- {
- printf("hitachi harddisk!\n");
- }
- void assemble_amd_cpu()
- {
- printf("amd cpu!\n");
- }
- void assemble_kingston_memory()
- {
- printf("kingston memory!\n");
- }
- void assmeble_western_digital_harddisk()
- {
- printf("western digital harddisk!\n");
- }
假设有两个水果店都在卖水果,都卖苹果和葡萄。其中一个水果店买白苹果和白葡萄,另外一个水果店卖红苹果和红葡萄。所以说,对于水果店而言,尽管都在卖水果,但是两个店卖的品种不一样。
既然水果不一样,那我们先定义水果。
- typedef struct _Apple
- {
- void (*print_apple)();
- }Apple;
- typedef struct _Grape
- {
- void (*print_grape)();
- }Grape;
- void print_white_apple()
- {
- printf("white apple!\n");
- }
- void print_red_apple()
- {
- printf("red apple!\n");
- }
- void print_white_grape()
- {
- printf("white grape!\n");
- }
- void print_red_grape()
- {
- printf("red grape!\n");
- }
- typedef struct _FruitShop
- {
- Apple* (*sell_apple)();
- Apple* (*sell_grape)();
- }FruitShop;
- Apple* sell_white_apple()
- {
- Apple* pApple = (Apple*) malloc(sizeof(Apple));
- assert(NULL != pApple);
- pApple->print_apple = print_white_apple;
- return pApple;
- }
- Grape* sell_white_grape()
- {
- Grape* pGrape = (Grape*) malloc(sizeof(Grape));
- assert(NULL != pGrape);
- pGrape->print_grape = print_white_grape;
- return pGrape;
- }
- FruitShop* create_fruit_shop(int color)
- {
- FruitShop* pFruitShop = (FruitShop*) malloc(sizeof(FruitShop));
- assert(NULL != pFruitShop);
- if(WHITE == color)
- {
- pFruitShop->sell_apple = sell_white_apple;
- pFruitShop->sell_grape = sell_white_grape;
- }
- else
- {
- pFruitShop->sell_apple = sell_red_apple;
- pFruitShop->sell_grape = sell_red_grape;
- }
- return pFruitShop;
- }
下面我们主要从两个方面对头文件进行分析,即头文件是做什么的,头文件编写的过程中要注意些什么?
(1)头文件的作用
其实很多的编程语言是没有头文件的,比如说C#、java语言。为什么呢,因为这些语言数据结构和函数操作是捆绑在一起的。而C语言则不一样,它是把头文件和实现文件分开来的。头文件的内容主要有哪些呢,也就是嵌套头文件、宏定义、数据类型、函数原型定义、static函数等等。
(2)头文件的编写
a)头文件要简洁
很多源文件在编写的时候常常喜欢添加很多的头文件,不管是需要的还是不需要的。可是,我们要知道,头文件的作用主要是定义数据类型和函数类型的。本质上来说,头文件很少会创建实质性的代码,不管是数据段的内容,还是代码段的内容。简洁的头文件不仅有利于快速排除编译故障,还能提高编译的速度。有经验的朋友都知道,源文件的编译错误比较容易解决,而头文件的编译错误常常十分复杂。所以,我们必须在一切可能的条件下保证头文件的简洁。
b)头文件注意互斥性
注意头文件的互斥性,需要我们在开发中养成良好的编程习惯。不管是创建头文件,首先要做的事情就是添加编译宏。看上去这是一个十分不起眼的举动,但是常常可以帮助你减少许多不必要的麻烦。
- #ifndef _DATA_H
- #define _DATA_H
- #endif
c)全局变量不要在头文件里面定义,如果是外部引用,必须添加上extern
- extern int g_Data;
d)不要在头文件里面实现函数,如果要实现,也必须要添加static
- static int add(int a, int b)
- {
- return a + b;
- }
e)头文件当中如果需要嵌入别的头文件,那么只是为了引用另外一个头文件的数据结构
f)头文件中引用的数据类型如果没有说明,那么在被源文件引用的时候,只要保证其他的头文件存在这个数据类型定义即可
g)源文件引用头文件的时候需要注意头文件的顺序,有的时候顺序变了,可能编译就失败了。原因就是之前后面头文件中定义的数据类型找不到出处了
h)某些工程没有把头文件和源文件绑定在一起,修改头文件必须删除工程重新编译
i)头文件的存在只是为了源文件才存在的,如果没有必要不要写头文件。要写,影响范围也要控制在最小的范围内
j)如果头文件定义了数据结构,那么需要嵌入引用头文件,反之如果只是指针,声明一下即可,比如说
- struct _Data;
- typedef struct _Data Data;
k)如果有可能经常整理自己的头文件,全部删除再一个一个添加,这样就知道哪些是我们需要的,哪些不是
l)对于某些宏,如果不确定文件本身是在哪里定义的,可以在源文件中再定义一次,这样编译器就会精确提示我们原来这个宏是在那里定义的
好了,差不多就这么多了。
头文件 *.h文件例子:
- #ifndef _DATA_H
- #define _DATA_H
- #endif
- #include <pthread.h>
- #include <semaphore.h>
- #include <stdio.h>
- #include <stdlib.h>
- // Set either of these to 1 to prevent CPU reordering
- #define USE_CPU_FENCE 0
- #define USE_SINGLE_HW_THREAD 0 // Supported on Linux, but not Cygwin or PS3
- #if USE_SINGLE_HW_THREAD ---------在头文件中使用if ... endif
- #include <sched.h>
- #endif
- //-------------------------------------
- // MersenneTwister
- // A thread-safe random number generator with good randomness
- // in a small number of instructions. We'll use it to introduce
- // random timing delays.
- //-------------------------------------
- #define MT_IA 397
- #define MT_LEN 624
- class MersenneTwister
- {
- unsigned int m_buffer[MT_LEN];
- int m_index;
- public:
- MersenneTwister(unsigned int seed);
- // Declare noinline so that the function call acts as a compiler barrier:
- unsigned int integer() __attribute__((noinline));
- };
- MersenneTwister::MersenneTwister(unsigned int seed)
- {
- // Initialize by filling with the seed, then iterating
- // the algorithm a bunch of times to shuffle things up.
- for (int i = 0; i < MT_LEN; i++)
- m_buffer[i] = seed;
- m_index = 0;