1. 0X0
0x0地址是NULL
2. Gdb调试子进程
1.set follow-fork-mode <parent/child>
这条命令可以用于在调试父进程或是子进程的模式之间进行切换。例如在fork函数执行之前执行set follow-fork-mode child, 当fork执行后,设定在子进程上的断点将会有效,而父进程上的断点此时无效;反之依然。缺省gdb是调试主进程的。
2.attach<pid>
GDB有附着(attach)到正在运行的进程的功能,即attach <pid>命令。因此我们可以利用该命令attach到子进程然后进行调试。
3. Gdb调试nginx
多进程调试
#sudo gdb –q
(gdb)shell ./nginx
如果只是gdb ./nginx
那么启动nginx还是单进程
4. Linux内核模块编写和makeFile编写
hello.c内容:
#include <linux/module.h>
#include <linux/init.h>
static int hello_init(void)
{
printk( KERN_ALERT " hello world enter \n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT " hello world exit \n");
}
module_init(hello_init);
module_exit(hello_exit);
MakeFile编写:
KERNEL_VERSION=/lib/modules/$(shell uname -r)/build
PWD=$(shell pwd)
default:
make -C $(KERNEL_VERSION) M=$(PWD) modules
注意上面的make –C需要时半角,而且前面需要有一个TAB,否则会执行失败
一、模块加载函数: (必须)
module_init(initialization_funciton);
二、模块卸载函数: (必须)
module_exit(cleanup_function);
三、模块参数:
module_param(参数名,参数类型,参数读/写权限)
在模块插入时: insmod 模块名 参数名=参数值
eg:
static char *book_name = " Linux 设备驱动 ";
static int num = 4000;
module_param(num, int , S_IRUGO);
module_param(book_neme ,charp, S_IRUGO);
参数类型: byte, short, ushort, int uint, long , ulong, charp(字符指针), bool,
四、导出符号 (建议有)
EXPORT_SYMBOL( 符号名/函数名)
EXPORT_SYMBOL_GPL(符号名 /函数命)
五、模块声明
MODULE_LICENSE(" Dual BSD/GPL ");
MODULE_AUTHOR (" XXXXX");
MODULE_DESCRIPTION("XXXXX DRIVER");
MODULE_VERSION(" XXXX VERSION");
MODULE_DEVICE_TABLE(TABLE_INFO);
MODULE_ALISA(XXXXXXX);
5. 内核编程中的EXPORT_SYMBOL
EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用 。 您还可以手工修改内核源代码来导出另外的函数,用于重新编译并加载新内核后的测试。
使用时注意事项:
在使用EXPORT_SYMBOL 的.c文件中 需要 #include <linux/module.h> 文件。
// 先写函数
func_a ()
{
}
//再使用EXPORT_SYMBOL
EXPORT_SYMBOL(func_a);
注:export出的函数必须为非static函数,且要在相应的.h文件中进行声明。
在内核中新加入文件时,需要对相应目录下的Makefile进行更改,并且新加入的文件使用的config设置与调用函数文件的config设置同。\\
6. Sprintf和printf的区别
Sprintf是将一个格式化后的字符串输出到指定的字符串中;
Printf是将格式化后的字符串输出到屏幕中
7. 宏和函数在同名在一个头或者一个文件的作用
当宏和函数同名在一个文件中时,那么函数将被宏替换掉,执行宏的内容,因为宏在预编译时已经将内容替换。
一般这种写法都是用作注释函数的作用。
例子:
#include "stdio.h"
void p(int a,int b,int c){
printf("%s","test");
}
#define p(a,b,c)({(void)b,NULL;printf("%s","aaaa");})
int main(){
p(1,2,3);
printf("%s","hello world`");
}
上面代码执行后,输出的是aaaahello world
8. 结构体赋值并初始化
//结构体定义:
struct testa{
int a;
};
//结构体赋值
struct testa sa={
.a=1
};
9. Gdb 加参数调试
首先进入GDB,然后设置断点
然后用set args 参数1 参数2 ….
然后执行r则可以进入到断点中了
10. Strdup 复制字符串
原型:extern char *strdup(char *s);
头文件:#include <string.h>
用法:char *strdup(char *s);
功能:复制字符串s
说明:strdup()在内部调用了malloc()为变量分配内存,当程序结束后,必须用free()释放相应的内存空间,否则会造成内存泄漏
#include <stdio.h>
#include <string.h>
int main()
{
char *s="Golden Global View";
char *d;
clrscr();
d=strdup(s);
printf("%s",d);
free(d);
getchar();
return 0;
}
执行后输出Golden Global View,就是把s 的内容复制给了d
11. atoi 字符串转换为整数函数
功 能: 把字符串转换成整型数.
名字来源:array to integer 的缩写.
原型: int atoi(const char *nptr);
函数说明: 参数nptr字符串,如果第一个非空格字符不存在或者不是数字也不是正负号则返回零,否则开始做类型转换,之后检测到非数字(包括结束符 \0) 字符时停止转换,返回整型数。
12. Gdb 显示函数调用栈
打好断点后,执行后,执行bt命令则显示出函数的调用栈信息:
13. gdb "value optimized out"原因
是因为在编译时加入了编译优化参数,比如gcc -O2,导致变量在调试中不能读出。将编译参数-O去掉即可。.configure
14. strchr
原型:extern char *strchr(const char *s,char c);
const char *strchr(const char* _Str,int _Val)
char *strchr(char* _Str,int _Ch)
头文件:#include <string.h>
功能:查找字符串s中首次出现字符c的位置
说明:返回首次出现c的位置的指针,如果s中不存在c则返回NULL。
返回值:Returns the address of the first occurrence of the character in the string if successful, or NULL otherwise
15. strtod
strtod(将字符串转换成浮点数)
相关函数
atoi,atol,strtod,strtol,strtoul
表头文件
#include<stdlib.h>
定义函数
double strtod(const char *nptr,char **endptr);
函数说明
strtod()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,到出现非数字或字符串结束时('\0')才结束转换,并将结果返回。若endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr传回。参数nptr字符串可包含正负号、小数点或E(e)来表示指数部分。如123.456或123e-2。
16. Floor
函数名: floor
功 能: 返回小于或者等于指定表达式的最大整数
用 法: double floor(double x);
头文件:math.h
17. Fprintf
#include <stdio.h>
int fprintf( FILE *stream, const char *format, ... );
fprintf()函数根据指定的format(格式)(格式)发送信息(参数)到由stream(流)指定的文件. fprintf()只能和printf()一样工作. fprintf()的返回值是输出的字符数,发生错误时返回一个负值.
返回值:
若成功则返回输出字符数,若输出出错则返回负值。
例子:
#include <stdio.h>
int main(void)
{
FILE *stream;
if ((stream = fopen("testwrite.txt", "a")) == NULL) /* open file TEST.$$$ */
{
fprintf(stderr, "Cannot open output file.\n");
return 1;
}
s.i = 0;
s.ch = 'A';
//fwrite(&s, sizeof(s), 1, stream); /* ..struct..*/
int f=fprintf(stream,"%s %d\n","test aaa",123);
int fe=ferror(stream);
printf("%d %d",fe,f);
//printf("%s",f);
fclose(stream); /*....*/
return 0;
}
18. Ferror
函数名: ferror
功 能: 在调用各种输入输出函数(如 putc.getc.fread.fwrite等)时,如果出现错误,除了函数返回值有所反映外,还可以用ferror函数检查。 它的一般调用形式为 ferror(fp);如果ferror返回值为0(假),表示未出错。如果返回一个非零值,表示出错。应该注意,对同一个文件 每一次调用输入输出函数,均产生一个新的ferror函 数值,因此,应当在调用一个输入输出函数后立即检 查ferror函数的值,否则信息会丢失。在执行fopen函数时,ferror函数的初始值自动置为0。
例子见17
在保障健壮性的情况下需要对文件的输出进行状态判断。
19. C中字符指针的赋值
赋值可以通过strcpy和sprintf来进行赋值
数值型的,需要先声明一个数值类型的变量,然后把变量赋值给指针
例:
char *a=(char*)malloc(sizeof(char)*100);
int *b=(int*)malloc(sizeof(int)*8);
strcpy(a,"\naaaa\n");
printf("%s",a);
sprintf(a,"%s%d%f\n","aaabbbbccc",123,1.23);
printf("%s",a);
*b=10;
int c=100;
*b=c;
printf("%d\n",c);
printf("%p\n",b);
printf("%p",c);
free(a);
free(b);
20. malloc
malloc是stdlib.h库下的
21. strcat
原型
extern char *strcat(char *dest,char *src);
#include <string.h>
把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。
src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。
22. 未声明(在此函数内第一次使用)
如果报了该错误,你声明的是外部全局变量,那么可能声明位置不对,你可以把你的变量放在文件的首部,这样就可以了。
23. 查看core_dump,然后调用堆栈
ulimit –c 是查看coredump的大小
ulimit –c 1024设置coredump大小,也可以通过unlimited设置(不限制)
ulimit –c 后面跟随的是core file size 是文件的大小,单位是kb,1024就是1m,512m就是1024*512
echo "1" > /proc/sys/kernel/core_uses_pid使core文件名加上pid号,还可以用
mkdir -p /root/corefile
echo "/root/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern控制core文件保存位置和文件名格式。
以下是参数列表:
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加当前uid
%g - insert current gid into filename 添加当前gid
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加命令名
当你执行二进制文件错误时,会生成一个coredump的文件在/root/corefile/目录下,然后通过gdb [exec file] [core file] 命令查看
如:
gdb ./test /root/corefile/core-xxx-xxx
进入gdb后通过bt查看调用堆栈
普通用户,首先需要设置
ulimit –c unlimited
然后设置的
echo "/export/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
这个/export/目录,用户需要有写权限,如:chmod –R 775 /export
这样用户和组都有对该目录的写权限
如果想永久的保存该信息,那么则需要保存在环境变量配置中,如:
/etc/profile
~/.bashrc
24. G++编译
g++ test_mongodb.cpp -I /export/dev/usr/include/ /export/dev/usr/lib/libmongoclient.a /usr/lib64/libboost_thread-mt.so /usr/lib64/libboost_filesystem.so /usr/lib64/libboost_program_options.so -o test_mongodb
g++ test_mongodb.cpp -I /export/dev/usr/include/ -lmongoclient -L/export/dev/usr/lib/ -lboost_thread-mt -lboost_filesystem -lboost_program_options -L/usr/lib64/ -o test_mongodb
引入依赖库的库和动态链接库即可,注意路径,如/export/dev/usr/include/mongo
下面有个mongo目录,在cpp中引用的是mongo/….,那么这里就直接写include即可,如果cpp中没有写mongo 直接写的../../,那么外面编译时则需要加上/export/dev/usr/include/mongo
25. error: default argument given for parameter 1 of错误解决方法
/export/dev/hiphop-php/src/runtime/ext/ext_mongo.cpp:32: error: default argument given for parameter 1 of 'HPHP::c_Mongo::c_Mongo(const HPHP::ObjectStaticCallbacks*)'
当报如上错误时,是由于函数的参数默认值在声明时已经存在,而在实现时又重新定义,所以出现这个问题,所以在头文件中声明默认值后,在cpp文件中,实现时,参数就无需要在写默认值,如:
string Money::asString(bool shortVersion=true){
}
在cpp中
string Money::asString(bool shortVersion){}
就不需要重新声明默认值了
26. 编译c++错误
/export/dev/hiphop-php/src/runtime/ext/ext_mongo.cpp:31: error: expected unqualified-id before ':' token
编译时出现这个错误,是在31行:
c_Mongo:c_Mongo(const ObjectStaticCallbacks *cb): ExtObjectDataFlags<ObjectData::UseGet>(cb){
}
注意c_Mongo:c_Mongo这里,正常的c++类函数实现应该是:
c_Mongo::c_Mongo
所以31行处少了一个引号,改为2个就可以了
/export/dev/hiphop-php/src/runtime/ext/ext_mongo.h:36: error: 'DBClientConnection' does not name a type
27. 获取环境变量
函数名: getenv
功 能: 从环境中取字符串,获取环境变量的值
头文件: stdlib.h
用 法:char *getenv(char *envvar);
28. 引用定义需要初始化
error: uninitialized reference member 'HPHP::c_Redis::c'
29. 报指针转换错误解决方法
/export/dev/hiphop-php/src/runtime/ext/ext_redis.cpp:36: error: cannot convert 'redis::base_client<redis::default_hasher>' to 'redis::client*' in assignment
当报如上错误时,可能是由于编译环境不同造成的,如:
redis::client *c;
boost::shared_ptr<redis::client> shared_c=boost::shared_ptr<redis::client>( new redis::client(host.data(),port));
c=*shared_c;
如上赋值把shared_c赋值给c会报上面的错误,所以改为如下方式:
c=&(*shared_c);
改成这样就没有问题了。
30. Strace 查看调用函数
Strace –p 端口
31. C拼接字符串乱码
char* varKey=(char*)malloc(varLen,1);
char* temp_varKey="mGet";
printf("%s\n",varKey);
当输出时,varKey是乱码
将分配空间语句改为:
char* varKey=(char*)calloc(varLen,1);即可
32. 报类型转换不匹配解决方法
error: invalid conversion from 'void*' to 'redisReply*'
当报如上错误时,是有如下语句造成的
reply = redisCommand(c,"SELECT %d",dbindex);
类型void不允许转换为'redisReply,这可能是由于编译器不同造成的,因为无法识别类别,如果我们知道类别,那么强转一下即可,如下:
reply = (redisReply*)redisCommand(c,"SELECT %d",dbindex);
这样就没有问题了,指定上'redisReply这个类型
33. 空指针判断和输出
通过printf输出指针
Char *a=NULL;
printf(“%p”,a);
输出如下值:
(nil)
就是空指针
判断空指针:
If(a==NULL){
}
34. make: Nothing to be done for `all'解决方式
make clean
make
35. gcc编译添加动态库
gcc test_redis_libevent.c -I hiredis/ -I /export/dev/usr/include/ -levent -lhiredis -L/export/dev/usr/lib/ -o test_redis_libevent
-I 是添加文件夹(是ai)
-levent 这里后面连接的是名字,找libevent这个库(小L)
-L 是包的路径
36. Mongo c++ limit
auto_ptr< DBClientCursor > mongo::DBClientBase::query ( const string & ns,
Query query,
int nToReturn = 0,
int nToSkip = 0,
const BSONObj * fieldsToReturn = 0,
int queryOptions = 0,
int batchSize = 0
)
nToReturn 就是limit
37. gdb list错误
(gdb) list analysis_result.cpp
Can't find member of namespace, class, struct, or union named "analysis_result.c pp"
这种写法有问题改为如下写法即可:
(gdb) list analysis_result.cpp:1
冒号后面是行号或者方法名称
38. C++ std::set swap
Swap方法是交换2个集合内容
39. 头文件中写实现函数默认是内联
40. C++的set用法
使用set需要
#include <iostream>
#include <set>
using namespace std;
后2个是必要的
声明一个set对象:
std::set<string> done;
<>间是他的类型
插入内容
done.insert(“test”);
根据类型不同可以插入不同对象;
Set的find使用
std::set <string> ::iterator test=done.find("test");
if(test==done.end()){
cout<<"end"<<endl;
}else{
cout<<"find"<<endl;
}
如果find中的内容在set内,那么则find的返回值不是set的end,否则find内查找的内容set中不存在,那么返回值与set的end是相等的。
41. Gdb watch(观察点)使用
1 #include <iostream>
2
3 using namespace std;
4
5 int main(){
6 cout<<"test gdb"<<endl;
7 int a=0;
8 for(int i=0;i<10;i++){
9 a++;
10 }
11 cout<<a<<endl;
12 return 0;
13 }
在编译时:
g++ -g test_gdb.cpp –o tet_gdb
然后执行:
Gdb ./test_gdb
然后在6行打上断点
b 6
断点需要打在可以捕捉到观察点变量的位置,我们要观察的是a
然后运行程序,执行r
执行r后,进入断点
然后我们执行
Watch a
(gdb) watch a
Hardware watchpoint 2: a
这样便成功的加入了观察点,然后执行c
当观察点发生变化时:
Hardware watchpoint 2: a
Old value = 0
New value = 1
main () at test_gdb.cpp:8
8 for(int i=0;i<10;i++){
这里就进入了变化的位置
42. __thread
线程局部变量修饰关键字,如:
__thread int f;
43. dynamic_pointer_cast 动态指针转换
boost:: dynamic_pointer_cast
这个是将指针动态向上转换,如:
Class A{
}
Class B:public A{
}
Int main(){
boost::shared_ptr<A> ptrBase = boost::shared_ptr<DeriveClass>(new B());
boost::shared_ptr<B> ptrDerive = dynamic_cast<boost::shared_ptr<B> >(ptrBase);
return 0;
}
这样就转换成功了。
44. C++ 的转换操作符
Class SmallInt{
Public:
SmallInt(int i=0):value(i){….}
Operate int() constr{ return val;}
Private:
Std:size_t val;
}
C++的转换操作符的主要功能是做类型转换用途
如:
(1)只要存在转换,编译器将在可以使用内置转换的地方自动调用它:
SmallInt si;
double dval;
si >=dval;
(2)在条件中:
If(si)
(3)将实参传给函数或从函数返回值:
Int calc(int);
SmallInt si;
Int i=calc(si);
(4)作为重载操作符的操作数:
Cout<si<endl;
(5)显式转换:
Int ival;
SmallInt si=3.511;
Ival = static_cast<int>(si)+3;//转换si为int
但是,这个不可以做2次类型强转,如:
Class Intergral{
Public :
Intergral(int i=0):val(i){}
Operator SmallInt() const{return vale %256}
Private:
Std:size_t val;
}
Int calc(int);
Integral intVal;
SmallInt si(intVal);// intVal转换为 SmallInt
Int I =calc(si); //si转换为int
Int j =calc(intVal);// 错误,intVal不能二次转换为int
只能先将Integral 转换为SmallInt ,然后通过SmallInt转换为int,不能直接通过Integral转换为int,也就是转换操作符不能进行二次转换。
详细见:
C++ primer的455-457
45. c++ 代码换行符
#define EXCEPTION_COMMON_IMPL(cls) \
virtual cls* clone() { \
return new cls(*this); \
} \
virtual void throwException() { \
Deleter deleter(this); \
throw *this; \
}
C++中的宏需要在一行中定义,但是由时候代码过多,所以用\来进行换行标注
46. C 可变参数实现
如:
int demo(char *msg, ... )
参见如下网页内容:
http://blog.csdn.net/weiqubo/article/details/4857567
47. 指针值
#include <stdio.h>
#include <malloc.h>
int main(){
int *a= (int *)malloc(sizeof(int));
int b=111;
//指针地址
printf("%p-----\n",a);
//指针的值
*a=b;
//指针值
printf("%d-----\n",*a);
//指针地址
printf("%p-----\n",a);
free(a);
return 0;
}
输出:
0x209b010-----
111-----
0x209b010-----
48. pthread_mutex_lock 互斥锁
pthread_mutex_lock
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
49. C 断言assert使用
在c语言中使用断言可以保证其健壮性,如果在断言的位置错误的时候,程序将终止;
例子:
#include <assert.h>
#include <stdio.h>
int main(){
assert(1==1);
printf("assert test!");
}
上面的是可以正常输出的,如果将
assert(1==1);
换为
assert(1==0);
报出如下错误:
test_assert: test_assert.cpp:5: int main(): Assertion `1==0' failed.
Aborted
断言失败,也就是该地方是false
断言使用的位置:
(1)空指针。
(2)输入或者输出参数的值不在预期范围内。
(3)数组的越界。
不适合使用断言的位置:
断言语句不是永远会执行,可以屏蔽也可以启用
因此:
1.不要使用断言作为公共方法的参数检查,公共方法的参数永远都要执行
2.断言语句不可以有任何边界效应,不要使用断言语句去修改变量和改变方法的返回值.
50. C++ main 方法之前加载内容
我们都知道一个C++的程序要先从main函数执行起这是基本的编程常识但是我们却可以在main函数执行之前先来执行一段代码这是利用全局变量和构造函数的特性再有全局变量的时候要先创建全局变量然后在执行main函数
例:
#include <stdio.h>
static int label= 0;
class Test
{
public:
Test();
};
Test::Test(){
label=11;
printf("call Test!");
}
Test test;
int main(){
// Test test;
printf("%d--\n",label);
return 0;
}
这样输出就会是:
call Test!11—
label已经被初始化好内容了
这是利用了构造函数初始化,构造函数加载在main之前了
如果把没有Test test;这行语句,那么久会输出
0—
51. C语言中宏#和##的作用
http://blog.sina.com.cn/s/blog_95b655e001013jdc.html
例如下面的宏定义
#define STR( s ) #s
那么在程序中
printf("The string is %s/n", STR(OPEN) );
会被展开成
printf("The string is %s/n", "OPEN" );
也就是说,会对#后跟的参数加引号,使其变成一个字符串。
2. ##的作用是对文本进行连接
例如下面的宏定义
#define CONS( a, b ) a##e##b
那程序中
printf("the number is %d/n", CONS(2,3) );
会被展开成
printf("the number is %d/n", 2e3 );
需要注意的是,在有#和##的宏中,如果参数本身是宏,则这个参数宏不会展开。
加一层中间转换宏可以解决这个问题。
#define _STR(s) #s
#define STR(s) _STR(s) // 转换宏
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b) _CONS(a,b) // 转换宏
52. 函数指针
#include <stdio.h>
int testcall(int &a);
int main(){
int a=3;
//testcall(a);
//声明函数指针
int (*f)(int &b);
//给函数指针赋值
f=testcall;
printf("%d++\n",f(a));
printf("f address %p,testcall address %p\n",f,testcall);
printf("%d--\n",a);
return 0;
}
int testcall(int &a){
a=a+1;
return a;
}
输出结果:
4++
f address 0x40060f,testcall address 0x40060f
4—
53. boost::program_options的使用
参照:
http://www.rosoo.net/a/201109/14993.html
从命令行提取程序运行时选项的方法有很多。你可以自己编写相对应的完整的解析函数,或许你有丰富的C语言编程经验,熟知getopt()函数的用法,又或许使用Python的你已经在使用optparse库来简化这一工作。
program_options 提供程序员一种方便的命令行和配置文件进行程序选项设置的方法。使用program_options库而不是你自己动手写相关的解析代码,因为它更简单,声明程序选项的语法简洁,并且库自身也非常小。将选项值转换为适合的类型值的工作也都能自动完成。库有着完备的错误检查机制,如果自己手写解析代码时,就可能会错过对一些出错情况的检查了。最后,选项值不仅能从命令行获取,也能从配置文件,甚至于环境变量中提取,而这些选择不会增加明显的工作量。
1. 首先看一个简单的程序吧:
#include <string>
#include <iostream>
#include <boost/program_options.hpp>
using namespace std;
int main (int ac, char* av[])
{
boost::program_options::options_description options("command line options");
options.add_options() ("help", "Use -h or --help to list all arguments")
("file", boost::program_options::value<string>(),
"Provide input file name");
boost::program_options::variables_map vmap;
boost::program_options::store(
boost::program_options::parse_command_line(ac, av, options), vmap);
boost::program_options::notify(vmap);
if (vmap.count("help")) {
cout << options << endl;
}
if (vmap.count("file")){
cout <<"Your input file: " << vmap["file"].as<string>() << "\n";
}
return 0;
}
//compile: g++ boost_test.cpp -o boost_test -lboost_program_options
程序的工作方式如下:
options_description 类声明所有的有效命令行选项。
使用方法 add_options,您可以注册命令和跟在命令后面的参数类型。在此例中,help 选项不需要任何参数,但是 file 选项需要一个字符串参数。
variables_map 类在运行时存储命令行选项及其参数。
Boost 的 parse_command_line 函数解析 argc 和 argv 参数。store 和 notify 方法帮助存储 vmap对象中的数据。
vmap.count()用于检测输入的是哪个命令行参数,并采取适当的动作
2. 提供多个参数和缩写的命令选项
命令行处理通常同时需要同一个命令选项的短名称和长名称。此外,您通常必须多次使用某个选项,以便收集该选项的所有参数。例如,您可能希望使用 -h 和 --help 来打印可用的命令:
#include <string>
#include <iostream>
#include <boost/program_options.hpp>
using namespace std;
int main (int ac, char* av[])
{
boost::program_options::options_description options("command line options");
options.add_options() ("help,h", "Use -h or --help to list all arguments")
("file", boost::program_options::value<vector<string> >( ),
"Provide input file name");
boost::program_options::variables_map vmap;
boost::program_options::store(
boost::program_options::parse_command_line(ac, av, options), vmap);
boost::program_options::notify(vmap);
if (vmap.count("help")) {
cout << options << endl;
}
if (vmap.count("file")) {
vector<string> ifiles(vmap["file"].as< vector<string> > ());
vector<string>::iterator vI;
cout << "Number of input files: " << ifiles.size() << endl;
cout << "Input file list: " << endl;
for(vI = ifiles.begin(); vI != ifiles.end(); ++vI)
cout << "\t" << *vI << endl;
} else {
cout << "No file specified\n";
}
return 0;
}
在使用 add_options 来添加命令选项时,较长和较短的选项之间使用逗号进行分隔。请注意,较长的选项(help) 必须在较短的选项 (h) 之前,代码才能正常工作。与使用单个字符串不同,file 选项现在是使用一个字符串向量来定义的。如果指定了 --file 选项多次,则会将所有收集到的file命令选项参数存储在关联的vector<string>中。下面是使用不同的参数来多次指定 --h 和 --file 所获得的输出:
./a.out -h
command line options:
-h [ --help ] Use -h or --help to list all arguments
--file arg Provide input file name
No file specified
./a.out --file abc --file pqr
Number of input files: 2
Input file list:
abc
pqr
3. 解析位置选项
下面的程序,第一个参数转换为 --file=<first parameter>,第二个参数转换为 --do-file=<second parameter>。
#include <string>
#include <iostream>
#include <boost/program_options.hpp>
using namespace std;
int main (int ac, char* av[])
{
boost::program_options::options_description options("command line options");
options.add_options() ("help,h", "Use -h or --help to list all arguments")
("file", boost::program_options::value<string>(),
"Provide input file name")
("do-file", boost::program_options::value<string>(),
"Specify commands file");
boost::program_options::variables_map vmap;
boost::program_options::positional_options_description poptd;
poptd.add("file", 1);
poptd.add("do-file", 2);
boost::program_options::store(
boost::program_options::command_line_parser(ac, av).
options(options).positional(poptd).run(), vmap);
boost::program_options::notify(vmap);
if (vmap.count("file")) {
cout << "file: " << vmap["file"].as<string> ( ) << endl;
}
if (vmap.count("do-file")) {
cout << "do-file: " << vmap["do-file"].as<string> ( ) << endl;
}
return 0;
}
下面是输出内容:
./a.out file1 dofile1
file: file1
do-file: dofile1
程序引入了新的类 positional_options_description。该类的 add 方法(add("command option", N))将位置 N 处的输入参数与命令行选项 "command option" 相关联。因此,./a.out file1 在内部解析为./a.out --file=file1。另一个区别在于调用 program_options::store 方法的方式。与使用parse_command_line 例程不同,Boost 库要求您将 command_line_parser 例程与 store 方法结合在一起使用。
请注意,仍然可以使用 --file 和 --do-file 选项来调用该程序。最后,若要将所有的输入参数与同一个命令行选项相关联,您需要使用值-1 将该命令行选项添加到 positional_options_description 对象。下面是代码:
…
boost::program_options::positional_options_description poptd;
poptd.add("file", -1);
...
String test;
boost::program_options::value<string>(&test)
这样可以将值赋值给test
54. Gdb 条件断点
使用gdb 调试时,如果想在某个条件触发时停下,那么可以这么做:
gdb test.cpp:7 if a == 1
这个就是当a 等于1时停下
但是在test.cpp中没有a这个变量,比如我们想在某个条件停下,但是其中的值非常复杂,那么我们就在代码内部自己先做一层逻辑,然后做一个临时变量a,然后再打断点时触发,即可
55. 设置全局变量
在main方法的文件中定义一个全局变量如:
Int temp;
然后在需要使用该全局变量的文件中,声明:
extern int temp;
即可,注意只能放在外层,不能放在类内,否则会报如下错误:
/export/dev/hiphop-php/src/compiler/expression/scalar_expression.h:85: error: storage class specified for 'temp_gdb_hzg
56. Vector 的reserve
vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector的capacity同时也增加了它的size!
原因如下:
reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素。加入新的元素时,要调用push_back()/insert()函数。
resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。此时再调用push_back()函数,是加在这个新的空间后面的。
两个函数的参数形式也有区别的,reserve函数之后一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小, 第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。
57. BOOST_FOREACH用法
参照网址:
http://blog.csdn.net/jiangfriend/article/details/1713619
例子:
#include <string>
#include <iostream>
using namespace std;
#include <boost/foreach.hpp>
int main(int argc, char* argv[])
{
string str("The quick brown fox jumps over the lazy dog.");
BOOST_FOREACH(char c,str)
{
cout<<c<<endl;
}
return 0;
}
这里的BOOST_FOREACH 就是foreach迭代器,前面的是一个obj对象,后面的是一个该对象类型的集合
58. a storage class can only be specified for objects and functions
/export/dev/hiphop-php/src/compiler/test_gdb_hzg.h:6: error: a storage class can only be specified for objects and functions
Static class Test_Gdb_Hzg{
….
};
由于在static 中没有添加变量名字;
Static class Test_Gdb_Hzg{
….
}test;
这样声明上名字即可
59. 当头文件引用冲突时加入if def
在.h 文件中加入:
#ifndef __TEST_GDB_HZG_H__
#define __TEST_GDB_HZG_H__
………
#endif
然后再多文件中引用就没有问题了
如果不这个,但文件引用会报如下错误:
has not been declared
当前文件中没有引入该头文件
如果引用了,会报重复声明了
所以在需要多文件引用时加入if def即可
60. Vector back pop_back push_back
back 是获取尾元素;
pop_back是删除尾元素;
push_back是在尾部添加元素
61. 报未引用警告处理办法
当报了如下问题后:
/export/dev/hiphop-php/src/compiler/analysis/file_scope.cpp:413: warning: unused variable 'temp'
是由于声明了temp,但是后面未进行过调用,所以会报这个警告,这个不影响程序;
一般来说如:
Int temp=0;
Printf(“%d”,temp);
只要进行过调用就不会有问题了
62. Gdb 读取vector数据
(gdb) p m_scopes
$89 =
{
{{
px = 0x7fffe0277120,
pn = {
pi_ = 0x7fffe0277700
}
} {
px = 0x7fffe027d290,
pn = {
pi_ = 0x7fffe027dad0
}
} }
}
如上面这样的就无法用:
p m_scopes[0].px读取了;上面是有2个数据
那么这样去读取:
p m_scopes.back()[0].px
63. ~a取反的意思
如 a 二进制为:110110
那么~a则为:001001
64. C++ map操作
map中元素的查找:
find()函数返回一个迭代器指向键值为key的元素,如果没找到就返回指向map尾部的迭代器。
map<int ,string >::iterator l_it;;
l_it=maplive.find(112);
if(l_it==maplive.end())
cout<<"we do not find 112"<<endl;
else cout<<"wo find 112"<<endl;
如果I_it!=maplive.end()时则是查找成功
其他map 操作详见:
http://blog.csdn.net/allovexuwenqiang/article/details/5686583
65. gdb 读取first 和second这样的vector 这样的值
p val
$37 = {
first = {
<std::tr1::__detail::_Hashtable_iterator_base<std::pair<HPHP::hphp_raw_ptr<HPHP::BlockScope> const, int>, false>> = {
_M_cur_node = 0x5f228e0,
_M_cur_bucket = 0x7fffc829f4e0
}, <No data fields>},
second = true
}
然后可以通过打印first
(gdb) p val.first
$38 = {
<std::tr1::__detail::_Hashtable_iterator_base<std::pair<HPHP::hphp_raw_ptr<HPHP::BlockScope> const, int>, false>> = {
_M_cur_node = 0x5f228e0,
_M_cur_bucket = 0x7fffc829f4e0
}, <No data fields>}
打印first的值
(gdb) p *val.first
$39 = (std::pair<HPHP::hphp_raw_ptr<HPHP::BlockScope> const, int> &) @0x5f228e0: {
first = {
px = 0x7fffc8276bc0
},
second = 2097152
}
就是在前面加星,如果想打里面的*val.first中的first,那么可以这样去打:
(gdb) p (*val.first).first
$41 = {
px = 0x7fffc8276bc0
}
(gdb) p (*val.first).first.px
$42 = (HPHP::ClassScope *) 0x7fffc8276bc0
(gdb)
就是在值外面(*val.first)加上括号即可,然后调用first
也可以通过val.second调用value
66. Std find查找
string variableName="$a->";
if(variableName.find("->") != std::string::npos){
cout<<"++"<<endl;
}
查找到后会输出++
67. Stl map erase使用
Erase是删除map 中的某个对象
map testMap;
testMap. Erase(name)
68. 函数指针用法
#include "stdio.h"
//函数指针类型定义 void 返回值类型 fun_ptr 类型名称; int是参数
typedef void (*fun_ptr)(int);
void test(int a){
printf("%d==========\n",a);
}
int main(){
test(1);
printf("%p\n",&test);
//把test的地址给t
fun_ptr t=test;
//执行t
t(2);
return 1;
}
t 和 test的地址一样
69. linux 查看动态链接库的信息
ldd <可执行文件名> 查看可执行文件链接了哪些 系统动态链接库
nm <可执行文件名> 查看可执行文件里面有哪些符号
strip <可执行文件名> 去除符号表可以给可执行文件瘦身
如果我们想从可执行程序里面提取出来一点什么文本信息的话,还可以用strings命令
strings <可执行文件名>
70. 执行时报找不到动态链接库解决
执行编译语句:
gcc --std=c99 -I/export/servers/mongodb_c/include/ -L/export/servers/mongodb_c/lib -o test_mongo_c test_mongo_c.c –lmongoc
编译成功后执行
./test_mongo_c时报了如下错误:
./test_mongo_c: error while loading shared libraries: libmongoc.so.0.7: cannot open shared object file: No such file or directory
通过ldd test_mongo_c 查看动态链接库,发现
linux-vdso.so.1 => (0x00007fff7c3c7000)
libmongoc.so.0.7 => not found
libc.so.6 => /lib64/libc.so.6 (0x00007f4afc602000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4afcbbf000)
找不到 libmongoc.so.0.7这个库,那么我们用root用户登录,然后打开
/etc/ld.so.conf
这个文件,在/etc/ld.so.conf文件中,加入我们xx.so的目录即可,如:
/usr/local/lib
/export/servers/mongo/lib
然后保存后执行,/sbin/ldconfig –v这样就生效了,然后重新编译文件,再执行就可以了,并且执行ldd查看动态链接库也有内容了
71. 指针调用异常解决
printf("%d---\n",cursor.limit);
编译时报如下错误:
test_connect.c:39:26: error: request for member 'limit' in something not a structure or union
cursor是个指针不是结构体
所以应改为:
printf("%d---\n",cursor->limit);
这样调用即可
72. error: crosses initialization of 'time_t t'解决方法
当报如下错误时:
error: crosses initialization of 'time_t t'
原代码:
case BSON_DATE:
bson_printf( "%ld" , ( long int )bson_iterator_date( &i ) );
struct tm *lt;
time_t time_value=bson_iterator_time_t (&i);
………………….
break;
将case用花括号括起来即可编译通过
case BSON_DATE:
{
bson_printf( "%ld" , ( long int )bson_iterator_date( &i ) );
struct tm *lt;
time_t time_value=bson_iterator_time_t (&i);
………………….
break;
}
参照:
http://blog.sina.com.cn/s/blog_7a18bae10100s0lp.html
73. socket 编程非阻塞
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int fd, retval;
struct sockaddr_in addr;
struct timeval timeo = {3,0};
socklen_t len = sizeof(timeo);
fd_set set;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (argc == 4) timeo.tv_sec = atoi(argv[3]);
/*
F_SETFL 设置文件描述词状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响
设置非阻塞O_NONBLOCK (阻塞为~O_NONBLOCK)
*/
int savefl = fcntl(fd,F_GETFL);
fcntl(fd, F_SETFL, savefl | O_NONBLOCK);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
printf( "%d\n ", time(NULL));
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0)
{
close(fd);
printf( "connected..1\n ");
return 0;
}
/*
当连接状态为-1(connect),但是errno是EINPROGRESS
表示此时tcp三次握手仍旧进行,如果errno不是EINPROGRESS,则说明连接错误,程序结束。
*/
if (errno != EINPROGRESS)
{
close(fd);
perror( "connect..2 ");
return -1;
}
/*将set清零使集合中不含任何fd*/
FD_ZERO(&set);
/*将fd加入set集合*/
FD_SET(fd, &set);
/*
args0:最大的fd数量 最大值加1,不能错
非阻塞机制
args1:读fd 的set
args2:写fd的set
args3:fds错误的set
args4 时间
retval:
负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
*/
retval = select(fd + 1, NULL, &set, NULL, &timeo);
if (retval == -1)
{
close(fd);
perror( "select ");
return -1;
}
else if(retval == 0)
{
close(fd);
fprintf(stderr, "timeout\n ");
printf( "%d\n ", time(NULL));
return 0;
}
/*测试fd是否在set集合中*/
if(FD_ISSET (fd,&set))
{
int error = 0;
socklen_t len = sizeof (error);
if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
{
printf ( "getsockopt fail,connected fail\n ");
return -1;
}
if (error == ETIMEDOUT)
{
printf ( "connected timeout\n ");
}
if(error == ECONNREFUSED)
{
printf( "No one listening on the remote address.\n ");
return -1;
}
}
printf ( "connected .. 3\n ");
fcntl(fd, F_SETFL, savefl);
close (fd);
return 0;
}
上面是一个连接超时验证的一个socket连接代码;
非阻塞连接参照:
http://hi.baidu.com/johntech/item/75ef7aea922cd23286d9dee1
注意:
(1)errno的值为111 (ECONNREFUSED ),正确我应该需要的是115(EINPROGRESS )
正确写法:errno 为EINPROGRESS
fd = socket(AF_INET, SOCK_STREAM, 0);
int savefl = fcntl(fd,F_GETFL);
fcntl(fd, F_SETFL, savefl | O_NONBLOCK);
错误:errno为ECONNREFUSED (111)
int savefl = fcntl(fd,F_GETFL);
fcntl(fd, F_SETFL, savefl | O_NONBLOCK);
fd = socket(AF_INET, SOCK_STREAM, 0);
记住错误红色部分需要在socket下面才对,先连接socket让,后在设置阻塞状态
Errno状态参照:
http://baike.baidu.com/view/3485007.htm
Socket函数:
头文件:
#include <sys/types.h>
#include <sys/socket.h>
函数原型 int socket(int domain, int type, int protocol);
例子:
fd = socket(AF_INET, SOCK_STREAM, 0);
第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET;
第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW(WinSock接口并不适用某种特定的协议去封装它,而是由程序自行处理数据包以及协议首部);
第三个参数指定应用程序所使用的通信协议。此参数可以指定单个协议系列中的不同传输协议。在Internet通讯域中,此参数一般取值为0,系统会根据套接字的类型决定应使用的传输层协议。
第三个参数protocol:
一般设置为0(IPPROTO_IP 0),6(IPPROTO_TCP)
参照:
http://baike.baidu.com/view/9796742.htm
connect函数:
表头文件
#include<sys/types.h>
#include<sys/socket.h>
定义函数
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
函数说明
connect()用来将参数sockfd 的socket 连至参数serv_addr 指定的网络地址。结构sockaddr请参考bind()。参数addrlen为sockaddr的结构长度。
参数
参数一:套接字描述符
参数二:指向数据机构sockaddr的指针,其中包括目的端口和IP地址
参数三:参数二sockaddr的长度,可以通过sizeof(struct sockaddr)获得
返回值
成功则返回0,失败返回-1,错误码GetLastError()。
参照:
http://baike.baidu.com/view/888434.htm
getaddrinfo函数
参照:
http://baike.baidu.com/view/6757218.htm
74. c 指针
void test(int *a){
//a是a的地址,*a 是指向a的值
}
75. error: no matching function for call to错误解决
报如下问题:
/export/dev_hhvm/hiphop-php_hzg/src/runtime/ext/ext_ice.cpp:39:55: error: no matching function for call to 'Ice::Communicator::stringToProxy(const HPHP::String&)'
是由于方法的参数不匹配造成的,如Ice::Communicator::stringToProxy(const HPHP::String&)
传入的类型是const HPHP::String& ,但是实际的类型是std::string,所以就会报这个问题了
76. multiple definition of `HPHP::classInfoMap'
当编译时报了如下错误:
multiple definition of `HPHP::classInfoMap'
在头文件中定义了一个classInfoMap全局变量
Classinfo.h
std::map<std::string,int> classInfoMap;
这样定义全局变量时,需要在头文件在定义的变量前加上extern
extern Std::map<std::string,int> classInfoMap;
然后在cpp上进行一次声明即可:
std::map<std::string,int> classInfoMap;
77. undefined reference to `HPHP::Ice_ClassInfo::Ice_ClassInfo()
当编译时报了如下错误:
/export/dev_hhvm/hiphop-php_hzg/src/runtime/ext/ext_ice.cpp:349: undefined reference to `HPHP::Ice_ClassInfo::Ice_ClassInfo()'
找到349行,发现时一个实例化语句如:
Ice_ClassInfo* ci = new Ice_ClassInfo();
但是Ice_ClassInfo这个类已经在头文件声明了
在头文件中找到了Ice_ClassInfo类:
Ice_ClassInfo();
发现只对Ice_ClassInfo类进行了构造函数的声明,并没有实现,所以报了没有引用的问题,
那么我们在cpp中进行实现的定义或者在头文件中
Ice_ClassInfo(){};这样实现即可
78. undefined reference to `vtable for SequenInfo‘
当编译时,报了如下错误:
undefined reference to `vtable for SequenInfo‘
提示虚函数表没有SequenInfo声明这个类,
这是由于我的SequenInfo中有一个虚函数在基类中没有声明,所以删除掉SequenInfo类中的在基类中没有用到的虚函数即可编译通过,而且需要注意的是,使用多态时,子类和基类都需要实现构造和虚构函数,并且实现虚函数,否则都会出现如上错误;
例:
Class Base{
public:
Base(){};
~Base(){};
virtual bool usesClasses(){};
};
Class SubClass{
public:
SubClass(){};
~ SubClass(){};
virtual bool usesClasses(){};
/*
这个虚函数在实例化SubClass时就会报如上错误,因为在Base类中没有定义这个虚函数,所以删除它即可,而且上面的构造函数,虚构函数都需要对应实现
*/
virtual bool getId(){};
}
79. List 用法
#include <iostream>
#include <list>
using namespace std;
class A{
public:
int v;
list<A> next;
};
int main(){
list<A> test_list;
for(int i=0;i<4;i++){
A *a=new A();
a->v=i;
list<A> list;
A *b=new A();
b->v=i+2;
//插入内容
list.push_back(*b);
a->next=list;
test_list.push_back(*a);
}
list<A>::iterator itr;
//遍历
for(itr=test_list.begin();itr!=test_list.end();itr++){
cout<<itr->v<<endl;
list<A>::iterator itr_n;
for(itr_n=itr->next.begin();itr_n!=itr->next.end();itr_n++){
cout<<"==================="<<endl;
cout<<itr_n->v<<endl;
}
}
return 0;
}
80. syscall exception: Resource temporarily unavailable
pthread_create 报了如下错误:
syscall exception: Resource temporarily unavailable
有几种暂时解决方式:
(1) ulimit –u 65535 这样是设置是改子进程数量
(2) ulimit –a查看
然后修改 ulmit –s 1024 是修改stack size也就是线程栈的大小
还有代码解决方式
81. Gdb 根据地址和类型取值
p {VolumePath} 0x6141d60
p {类型} 地址
82. source type is not polymorphic
当c++ 使用
P_ClassInfo *p_classInfo=dynamic_cast<HPHP::P_ClassInfo*>(ismc->typeInfo);
转换是报了如下错误:
source type is not polymorphic
源类型不是多态,那么是由于在类中没有定义虚函数
83. crosses initialization
当编译器报如下错误时:
error: crosses initialization of 'HPHP::P_PrimitiveInfo* p_primitiveInfo'
是 由于 switch的case后面没有加大括号,因为
如果大括号,则会出现crosses initialization错误,因为编译器会认为局部变量i可能会被绕过申明而被直接进入其作用域。
见:
http://blog.sina.com.cn/s/blog_489239370100asc4.html
84. test.cpp:16:47: error: 'malloc' was not declared in this scope
c++ 编译报了这个错误,添加个头文件就可以;
#include<stdlib.h>
85. 安装libevent报错解决
当执行make 时,libevent报了如下错误,由于libtool版本产生的
Version mismatch error. This is libtool 2.4, but the libtool: definition of this LT_INIT comes from libtool 2.2.6b
解决方法下载libtool2.2.6b,然后安装,需要默认安装,配置环境变量不知道为何无作用
安装完毕后解决该问题
86. Const char* 强转为 char *
const char * str = "asd";
char* p =const_cast<char*>(str.c_str());
87. int 转换成枚举类型
如枚举类型为:
Enum Kind{
Kind_a,
Kind_b,
Kind_c
} kind;
Int a=1;
kind=Kind(a);
红色部分是强转方法;
相关推荐
百度-胡志广-百度大规模机器治理
【Java Web 开发总结】 Java Web 开发是一个涵盖了...胡志广的总结提供了一个很好的学习资源,帮助开发者快速定位和解决Java Web开发中的各种挑战。对于想要深入学习Java Web的人来说,参考这些实际经验会非常有益。
在2016年中国软件开发者大会上,百度的技术专家胡志广分享了关于支撑千亿级流量的PHP引擎HHVM在百度的实战经验。这篇讨论主要涵盖了PHP语言、HHVM的架构设计以及其在大规模互联网环境中的应用。以下是这次分享的一些...
基于springboot教育资源共享平台源码数据库文档.zip
linux开发篇,配套视频:https://www.bilibili.com/list/474327672?sid=4493702&spm_id_from=333.999.0.0&desc=1
ReadEra 这个阅读应用能够打开下列任何格式的文档: EPUB, PDF, DOC, RTF, TXT, DJVU, FB2, MOBI, 和 CHM. 基本上来说,你可以用它阅读你的设备内存中的任何书籍或者文本文档。 这个应用与划分成章节的文档兼。,有一个书签功能,可以在你阅读的时候,自动保存你的进度。另外,它让你更改页面模式,从几种不同的主题中进行挑选(夜间,白天,棕黑色调,还有控制台)。
软件环境:KEIL4 硬件环境:STM32单片机+舵机 控制原理:通过控制输出信号的占空比调节舵机旋转的角度
基于springboot仓库管理系统源码数据库文档.zip
酒店管理系统源码C++实现的毕业设计项目源码.zip,个人大四的毕业设计、经导师指导并认可通过的高分设计项目,评审分98.5分。主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 酒店管理系统源码C++实现的毕业设计项目源码.zip,酒店管理系统源码C++实现的毕业设计项目源码.zip个人大四的毕业设计、经导师指导并认可通过的高分设计项目,评审分98.5分。主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。酒店管理系统源码C++实现的毕业设计项目源码.zip酒店管理系统源码C++实现的毕业设计项目源码.zip酒店管理系统源码C++实现的毕业设计项目源码.zip,个人大四的毕业设计、经导师指导并认可通过的高分设计项目,评审分98.5分。主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。酒店管理系统源码C++实现的毕业设计项目源码.zip,个人大四的毕业设计、经导师指导并认可通过的高分设计项目,评审分98.5分。主要针对计算机相关专业的正在做毕
58商铺全新UI试客试用平台网站源码
springboot vue3前后端分离 基于SpringBoot+Vue的轻量级定时任务管理系统.zip
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过严格测试运行成功才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
4D毫米波雷达点云数据处理方法研究.caj
S M 2 2 5 8 X T 量产工具供大家下载使用
基于springboot的文物管理系统源码数据库文档.zip
基于springboot的电影院售票管理系统源码数据库文档.zip
基于Java web 实现的仓库管理系统源码,适用于初学者了解Java web的开发过程以及仓库管理系统的实现。
美容美发项目,使用django框架,前后端一体化项目
在线票务:2023年中国在线票务行业市场规模约为24.99亿元,挖掘市场蓝海新机遇 在数字浪潮的席卷下,传统的票务销售模式正经历着前所未有的变革。纸质门票逐渐淡出人们的视野,取而代之的是便捷、高效的数字和移动票务。这一转变不仅为消费者带来了前所未有的购票体验,更为在线票务平台开辟了广阔的发展空间和市场机遇。随着国民经济的持续增长和文体娱乐行业的蓬勃发展,中国在线票务行业正站在时代的风口浪尖,等待着每一位有志之士的加入。那么,这片蓝海市场究竟蕴藏着怎样的潜力?又该如何把握机遇,实现突破?让我们一同探索。 市场概况: 近年来,中国在线票务行业市场规模持续扩大,展现出强劲的增长势头。据QYResearch数据显示,2023年中国在线票务行业市场规模约为24.99亿元,尽管受到宏观经济的影响,市场规模增速放缓,但整体趋势依然向好。这一增长主要得益于国民人均收入的不断提高、电影及演出行业的快速发展以及政府政策的支持。例如,2023年财政部、国家电影局发布的《关于阶段性免征国家电影事业发展专项资金政策的公告》,为电影行业注入了强劲动力,进而推动了在线票务市场规模的扩大。 技术创新与趋势: 技术进步
基于SpringBoot的养老院管理系统源码数据库文档.zip