C/C++ 知识点
子进程正常退出会不会触发SIGCHLD\SIGSEV信号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
321
2
3
4
5
6 void sig_handler(int sig)
7 {
8 printf("catch signal:%d SIGCHILD=%d getpid=%d getppid=%d\n",sig,SIGCHLD,getpid(),getppid());
9 int status;
10 pid_t pid = wait(&status);
11 printf("pid %d exit\n",pid);
12 kill(getpid(),SIGABRT);
13 }
14 int main( int argc,char* argv[])
15 {
16 struct sigaction action;
17 action.sa_handler = sig_handler;
18 sigaction(SIGCHLD,&action,NULL);
19
20 pid_t pid = 0;
21 if((pid=fork()) == 0){
22 printf("this is child process:%d %d\n",getpid(),getppid());
23 exit(-1); // 这里如果是exit(0) 那么将不会触发SIGCHLD 信号,这里也有可能是需要exit(EXIT_FAILURE)产生SIGSEV;才会触发信号
24 }
25 while(1) sleep(100000);
26 return 0;
27 }
output:
this is child process:881 880
catch signal:17 SIGCHILD=17 getpid=880 getppid=6871
pid 881 exit
Aborted编译期的factorial
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
template<typename ResultType,int N>
class Factorial {
public:
enum { value = (ResultType)N*Factorial<ResultType,N-1>::value };
};
template<typename ResultType>
class Factorial<ResultType,1> {
public:
enum { value =(ResultType)1 };
};
template<typename ResultType>
ResultType factorial(int n)
{
if(n != 1)
{
return n * factorial<ResultType>(n-1);
} else {
return 1;
}
}
int main(int argc,char* argv[])
{
printf("Factorial:" PRINTTYPE "\n",Factorial<unsigned long long,PRIME>::value);
time_t btime,etime;
btime = time(NULL);
printf("Factorial2:" PRINTTYPE "\n",factorial<unsigned long long>(PRIME));
etime = time(NULL);
printf("time cost:%lu\n",etime - btime);
return 0;
}select 1024 限制问题
KERNEL部分
1.首先我需要重新修改
/usr/src/linux/include/linux/posix_types.h中FD_SETSIZE的大小
2.重新编译KERNEL并启动进入这个新的KERNEL
用户应用部分
3.修改
/usr/include/linux/posix_types.h中FD_SETSIZE的大小
/usr/include/bits/typesizes.h中__FD_SETSIZE的大小
4.重新编译应用程序
5.修改可以打开的最大文件数
(1)通过ulimit -n 修改 或者
(2)在自己应用程序中调用setrlimit修改
这里我有几个小问题:
1.有文档提到还要同时修改
/proc/sys/fs/file-max
/proc/sys/fs/file-nr
/proc/sys/fs/inode-nr
这样类似的文件,但是以我的理解,好像对2.6的KERNEL不再需要
2.有些文档好像还提到了修改glibc库
按我的理解,似乎也不需要
3.还有文档提到编译自己的应用程序的时候需要加上
-DFD_SETSIZE=nnn
按我的理解,也不需要
4./proc下的文件有没有详细的说明,我是说对内涵2.6来说map 循环删除元素
1
2for(map<int,int>::iterator it = m.begin(); it != m.end();it++)
m.erase(it++); // 或者 it = m.erase(it);c++ 萃取技术(根据参数获取对应的成员)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61struct MyTest
{
typedef int value_type;
value_type get(){return 1;}
};
template<typename TYPE>
TYPE::value_type GetValue(TYPE t)
{
reutrn t.get();
}
在没有使用萃取技术前,上面的方法对于下面的调用返回的是:
struct MyTest a;
GetValue(a); // 返回1
int b=1;
GetValue(b); // 此时由于b是内建类型无法定义value_type 和get这两个属性
那么我们如何做呢?
------------------------------------------------------
template<typename T>
struct MyTest{
typedef T value_type;
value_type get() { return "test";}
};
//泛化模板(1)
template<typename T>
struct Traits {
typedef typename T::value_type value_type;
value_type get(T obj) { return obj.get();}
};
//模板完全特化(由于所有木板参数都特化了,如果只是部分参数特化则称为偏特化)(2)
template<>
struct Traits<int>
{
typedef int value_type;
value_type get(int obj){return 1;}
};
// 对int*特化(3)
template<>
struct Traits<int*>
{
typedef int value_type;
value_type get(int *obj) { return 2;}
};
//此时GetValue方法可以改写成:
template<typename T>
typename Traits<T>::value_type GetValue(T t)
{
Traits<T>().get(t);
}
int main()
{
int a = 1;
printf("%d\n",GetValue(a));//对应调用Traits(2)
printf("%d\n",GetValue(&a));// 调用Traits(3)
struct MyTest<char*> tt;
printf("%s\n",GetValue<MyTest<char*> >(tt)); //调用traits(1)
}
输出:
1
1
test
由上可见,此时GetValue已经支持int int*,如果想要支持其他类是的内建模型则需要实现其对应的Traits(相当与代理)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43class CComplexObject
{
public:
void clone(){ printf("in clone");}
};
template<typename T,bool isClonable>
class XContainer
{
public:
enum { Clonable=isConable};
void clone(T*pobj)
{
Traits<isCloneable>().clone(pobj);
}
template<bool flag>
class Traits<true>
{
public:
void clone(T* pobj)
{
pobj->clone();
}
};
template<bool flag>
class Traits<false>
{
void clone(T*pobj)
{
printf(" non clonable");
}
};
};
int main()
{
int *p1 = 0;
CComplexObject *p2 = 0;
XContainer<int,false> n1;
XContainer<CComplexObject,true> n2;
n1.clone(p1);
n2.clone(p2);
return 0;
}
结构体成员偏移量
1
2
3
4
5
6
7
8struct Test
{
int a;
int b;
};
GET_MEMBER_OFFSET(Test,a) ---->0
GET_MEMBER_OFFSET(Test,b) ---->4递归全排列
算法很简单:将每个数组元素都依次与最后一个元素交换,然后递归的生成其余排列。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45void printfResult(const int a[],size_t n)
{
printf("{");
for(int i=0;i<n;i++)
printf ("%d ",a[i]);
printf("}\n");
}
void swap(int* x,int *y)
{
if(x != y)
{
*x ^=*y;
*y ^=*x;
*x ^=*y;
}
}
void permgen(int a[],size_t n)
{
static size_t total_size = n;
if(n <= 1)
{
printfResult(a,total_size);
}
else
{
// 依次与最后一位交换然后获取其排列
int last = n-1;
for(int i = 0;i<= last;i++)
{
{
swap(&a[last],&a[i]);
permgen(a,last);
//恢复
swap(&a[last],&a[i]);
}
}
}
}
int main()
{
int a [] = {1,2,3,4};
permgen(a,sizeof(a)/sizeof(int));
return 0;
}交换两个数
1
2
3
4
5
6
7
8
9void swap(int *a ,int *b)
{
if(a != b) //注意这里必须要判断,否则当传入的是同一个变量的地址时将会置为0: swap(&a,&a);
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
}类成员方法的函数指针调用问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1 class A;
2
3 class A
4 {
5 public:
6 int AAA(int(A::*pFunc)(int))
7 {// 类方法内部调用类成员指针,运算符->* 是Member pointer selector
8 return (this->*pFunc)(1);
9 }
10 int print(int a)
11 {
12 printf("%d\n",a);
13 return 0;
14 }
15 };
16
18 int test(A* pA,int(A::*pFunc)(int), int a)
19 {
20 return (pA->*pFunc)(a);
21 }
22 int test1(A pA,int(A::*pFunc)(int), int a)
23 {
24 // 类方法内部调用类成员指针,运算符.* 是Member pointer selector
25 return (pA.*pFunc)(a);
26 }
27
28
29 int main()
30 {
31 A a;
32 a.AAA(&A::print);
33
34 test(&a,&A::print,2);
35 test1(a,&A::print,3);
36 return 0;
37 }linux epoll 、网络编程注意事项
epoll_event
中的data是一个union
结构,所以使用时需要注意- 对socket调用
recv
采用buff[2]
将会使得recv
返回-1,且errno=EAGAIN
或者EWOULDBLOCK
[non-block 模式] - 对socket调用
recv
返回0表示客户端关闭了链接;如果返回值-1除了EAGAIN \EWOULDBLOCK
其他的都认为链接有问题需要关闭 - 对socket调用
send
如果返回值-1除了EAGAIN \EWOULDBLOCK
其他的都认为链接有问题需要关闭
如何把字符串转换成int或者long
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24long atoi(char* str)
{
char c;
long result=0;
int mult = 1;
if(str && *str == '-'){
mult = -1;
str++;
}
int desc;
while((c=*str++) != '\0' )
{
int desc = c - '0';
if(desc >=0 && desc < 10)
{
result *=10;
result +=desc;
}
else
{
break;
}
return result * mult;
}c++中虚函数表存储在哪里?
对于不同的编译器是不一样的,在g++中存放在.rodata 段,可以通过下面命令查看一个有虚函数的目标文件,每个有1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39class T1
{
public:
T1()
{
cout<<"this is T1 constructor"<<endl;
}
virtual void print()
{
cout << "this is the T1::print "<<endl;
}
};
class T2: public T1
{
public:
T2()
{
cout<<"this is T2 constructor" << endl;
}
virtual void print()
{
cout << "this is T2::print" <<endl;
}
};
class T3:public T1
{
};
int main()
{
cout << "begin main"<<endl;
class T1 tt;
tt.print();
class T2 t2;
t2.print();
class T3 t3;
t3.print();
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20objdump -x -d -s a.out|c++filt |grep vtable
0000000000400c20 w O .rodata 0000000000000018 vtable for T1
0000000000400be0 w O .rodata 0000000000000018 vtable for T3
0000000000400c00 w O .rodata 0000000000000018 vtable for T2
0000000000602080 w O .bss 0000000000000058 vtable for __cxxabiv1::__class_type_info@@CXXABI_1.3
0000000000602200 w O .bss 0000000000000058 vtable for __cxxabiv1::__si_class_type_info@@CXXABI_1.3
```
关于c++虚函数表可以参考 [C++虚函数表解析](http://blog.csdn.net/haoel/article/details/1948051)
0. 如何初始化map
```c
typedef map<int,const char*>::value_type VALUETYPE;
const static VALUETYPE desArray[] =
{
VALUETYPE(100,"test1"),
VALUETYPE(101,"test2")
};
//用desArray 初始化map
const static map<int,const char*> g_codeDespMap(desArray,desArray+sizeof(desArray));指针操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(void)
{
int a=0x12345678;
char* pa = (char*)&a;
printf("&a=%p\n",&a);
for(int i=0;i<sizeof(int);i++)
printf("%p=%p\n",pa+i,(char)*(pa+i));
short *spa = (short*)&a;
printf("\n");
for(int i =0;i<sizeof(int);i++)
{
printf("%p=%p\n",spa+i,(int)*(spa+i));
}
}
output:
&a=0x7ffff01c4684
0x7ffff01c4684=0x78
0x7ffff01c4685=0x56
0x7ffff01c4686=0x34
0x7ffff01c4687=0x120x7ffff01c4684=0x5678
0x7ffff01c4686=0x1234
0x7ffff01c4688=(nil)
0x7ffff01c468a=(nil)
数组 a 与&a的区别(指针数组与数组指针)
指针数组是值一个数组里面装着指针
数组指针是指一个指向数组的指针,它代表指针,指向整个数组1
2
3
4
5
6
7
8
void main(void)
{
int a[5]={1,2,3,4,5};
int *prt = (int*)(&a+1);
printf("%d,%d\n",*(a+1),*(ptr-1));
return
}因为&a是数组的指针(数组的首地址即a)而在栈中数组的分布情况如下:
|bb|高地址
|a4|
|a3|
|a2|
|a1|
|a0|
|cc|低地址
&a=a0的地址
而&a+c ,这里c=1 则将会使得指针移动到bb所在的地址,
(因为它所以移动的是c*n个元素单位,n为数组的元素个数这里c为1)同理要将二维数组赋给一指针,应这样赋值:
1
2
3
4int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。
1
2
3
4
5
6
7
8
9int main()
{
int v[2][10] = {{1,2,3,4,5,6,7,8,9,10},{11,12,13,14,15,16,17,18,19,20}};
int (*a)[10] = v;
printf("%d ",**a); //1
printf("%d ",**(a+1)); //11===*a[1]
printf("%d ",*(*a+1)); //2====*(a[0]+1)
}
`
分析:
a+1
|
v a —–> [1, 2, 3,4,5,6,7,8,9,10]
(a+1)—> [11,12,13,14,15,16,17,18,19,20]
a+1 一共向后移动了:字节数 = 列元素个数×sizeof(int)
STL
stl容器只支持“实值语义”,不支持引用语义,下面的写法无法通过编译:vector<shape&> v;
一个空类对象的
1
2class TT {}; //sizeof TT=1
class TT0{int test(){}}; //sizeof TT0 =1
sizeof TT=1–linux gcc,这是由c++标准规定的:c++标准规定一个空类以及那些不包含虚函数和非静态数据的成员时,其对象大小也是1. 其主要目的是为了类的实例化,如果大小为0完全不占用内存空间,那么空类无法获取实例的地址,this指针失效,因此不能被实例化。
另外在类中的static成员不存储在对象成员中所以sizeof是不包含static类的成员.根据继承的权限来区分子类成员是否被继承如果非虚拟继承那么类对象大小与基类以至于,如果是虚拟继承那么需要考虑一个指向指针虚拟函数表的指针的大小,如果是多个虚拟继承也需要将该指针叠加其他情况需要考虑对齐,其对齐计算方法与结构体类似.
变参使用
1
2
3
4
5
6
7
8
9
10
11
12void func(char* szFormat,...)
{
va_list args
va_start(args, szFormat);
vsprintf(buf,szFormat,args);
va_end(args);
//取参数
va_args(args);
}ifstream使用
1
2
3
4
5
6
7
8
9char c;
ifstream input;
input.open();
input.fail();
while(!input.eof())
{
input.get(c);
}
input.close();
析构
1 |
|
数组 指针
- 0长度数组的含义
linux总进程的结构体定义如下,也有0长数组:1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
unsigned long flags;
__u32 status;
__u32 cpu;
int preempt_count;
mm_segment_t addr_limit;
struct restart_block restart_block;
void __user *sysenter_return;
unsigned long previous_esp;
__u8 supervisor_stack[0];
};
标准C或者C++中由于不支持 0 长度的数组,只有GNU C允许使用这种用法,目的是为了访问不定长结构体时节省空间和便利性。
例如:
1
2
3
4
5struct demo {
int a;
char b[256];
char follow[0];
};
假如,现在程序中要分配一个struct demo结构体,并紧邻其后分配长度为LEN个字节空间,则可以使用如下方法得到:
struct demo demo = (struct demo ) malloc (sizeof(strcut demo) + LEN);
这样我们就可以用 demo->follow 来访问结构体demo随后的空间数据。
当然,我们可以使用指针来达到这样的目的。
1
2
3
4
5
6
struct demo {
int a;
char b[256];
char *follow;
};
struct demo *demo = (struct demo *) malloc (sizeof(strcut demo) + LEN);
同样可以达到零长度数组的效果,但是却多分配了一个char指针。如果分配额外数据空间还好,否则就是白白浪费了空间。
例子
C,pasted just now:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int func(char ar[2])
{
printf(sizeof=%d\n,sizeof ar);
printf(strlen = %d,strlen(ar));
}
int main(){
char *p = hello;func(p);
return 0;
}
Output:
sizeof=4
strlen = 5
`
例子
C,pasted 1 second ago:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
int func(char ar[2]){
int a = (int)((int*)0+4);
int b = (int)((short*)0+4);
printf(a=%d\n,a);
printf(b=%d\n,b);
}
int main(){
func(NULL);
return 0;
}
Output:
a=16
b=8
```
```c
** 注意:**
这里只是4✘sizeof(int)或者4*sizeof(short)个字节,只不过是将他们细分为16份而已
0x0010
0x000f
0x000e
。。。
0x003
0x002
0x001
0x000----(int *) 0
```
0. 指针例子
C,pasted just now:
```c
#include stdio.h
#include string.h
int func(long L)
{
L=L+1;
}
int func2(long *pL)
{
pL +=1;
}
int func3(long *pL){
*pL +=1;
}
int main(){
long lValue =20;
long *plValue = lValue;
func(lValue);
printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
func(*plValue);
printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
func2(lValue);
printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
func2(plValue);
printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
func3(lValue);
printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
func3(plValue);
printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
return 0;
}
Output:
lvalue = 20,*plValue=20lvalue = 20,*plValue=20lvalue = 20,*plValue=20
lvalue = 20,*plValue=20lvalue = 21,*plValue=21lvalue = 22,*plValue=22string 查找
C++中使用的string 中有一个属性npos即string::npos可以用来判断string中查找的字符串是否存在1
2
3
4
5public static member constant
<string>
std::string::npos
static const size_t npos = -1;
Maximum value for size_t
npos is a static member constant value with the greatest possible value for an element of type size_t.
This value, when used as the value for a len (or sublen) parameter in string’s member functions, means “until the end of the string”.
As a return value, it is usually used to indicate no matches.
This constant is defined with a value of -1, which because size_t is an unsigned integral type, it is the largest possible representable value for this type.
1
2
3
4
5string str;
pos=str.find_first_of("h");
if(pos!=string::npos)// match
- linux下整型转换字符
在windows下可以通过,itoa(value,buffer,type),value是待转换的值,buffer,存放转换后的字符串,最后我一个参数是表示需要转换的进制的形式,另外,她的头文件是stdlib.h,但是很奇怪在Linux下gcc并不支持他,另外在windows下还可以通过CString::format来进行转换,该方法在linux下依然是行不同的,在Linux下可以通过sprintf(buffer,”format”,param) 5此方法在window下依然是行的通的,或者通过宏来实现1
那么展开后就是const char*即”xxx”
- c/c++ 数组做形参:
1
int test(float a[10]);
注意:形参中给出数组长度是没有意义的,编译器并不为它分配内存,将上面 test 函数的形参改为 float a[1]、float a[10] 依然是正确的。所以一般用指针变量来代替,可以改为float *a。
1
2
3
4
5int test(float a[10],float aa[1000],float* aaa)
{
printf("%d %d %d",sizeof(a),sizeof(aa),sizeof(aaa));
}
在64bit系统上输出:8 8 8
- c全局变量、局部变量都是先定义的在低地址,后定义的在高地址(注意是定义不是声明)
- 编译器什么时候为类提供默认构造函数
- 本类、本类定义的成员对象或者父类中有虚函数存在的情况
- 父类或者本类中定义的成员对象带有构造函数
编译器如何传递this指针
this指针保存所属对象的首地址,在使用默认的调用约定时(thiscall)在调用成员函数的过程中编译其做了一个小动作
利用寄存器(ECX或者RAX)保存对象的首地址,并以寄存器传参的方式传递到成员函数中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38class TT
{
public:
void print()
{
}
public:
int tn1;
int tn2;
};
int main()
{
TT a;
a.tn1=0x01;
a.tn2=0x02;
a.print();
return 0;
}
//以下是反汇编代码
int main()
{
4004ed: 55 push %rbp
4004ee: 48 89 e5 mov %rsp,%rbp
4004f1: 48 83 ec 10 sub $0x10,%rsp
TT a;
a.tn1=0x01;
4004f5: c7 45 f0 01 00 00 00 movl $0x1,-0x10(%rbp)
a.tn2=0x02;
4004fc: c7 45 f4 02 00 00 00 movl $0x2,-0xc(%rbp)
a.print();
400503: 48 8d 45 f0 lea -0x10(%rbp),%rax //取出a对象的地址放入到rax寄存器中
400507: 48 89 c7 mov %rax,%rdi
40050a: e8 07 00 00 00 callq 400516 <_ZN2TT5printEv>
return 0;
40050f: b8 00 00 00 00 mov $0x0,%eax
}
400514: c9 leaveq
400515: c3 retq