C/C++

来源:互联网 发布:网络覆盖 编辑:程序博客网 时间:2024/06/05 02:53
1,C语言的指针
1)指针变量
int **p,*s,k=20;
s=&k;p=&s;                指针变量也是有类型的,该变量的值存放的是一个具体变量内存的首地址。p--->s--->k.p为指向指针的指针变量。
2)指针移动
int m=*s++;                *与++的优先级相同,则从右到左结合。先取s++,因为s++为后加,所以先把*s赋给m,然后s移动四个字节,只想下一个int。因为s为整形指针,所以一次移动4个字节。
3)函数中的指针
形参:void hanshu(int *p) {.....}
实参:int s=0;hanshu(&s);
这样不需要函数返回值就可以通过*p改变s的值。
在java中,当形参和实参都指向堆里同一个对象的时候,形参和实参都可以改变该对象的属性而不需要返回值。
4)数组
数组名代表了数组首元素的地址,是个地址常量不可改变。
一维数组a[i],其中的a相当于*p中的p;二维数组a[i][j]中的a相当于**p中的p。
5)字符串
c语言中的字符串实际上是以一个一维字符数组的形式存放,以'\0'表示结尾。
赋初值:char*sp="hahahahaha";或者 int *sp;          sp="hahahaha";           这样赋值字符串长度可以改变。
char str[]="hahahaha";     但str=“hahahaha”是错误的,因为str是数组名。

常用字符串处理函数
函数名: strcat 
功 能: 字符串拼接函数 
用 法: char *strcat(char *destin, char *source); 

函数名: stpcpy 
功 能: 拷贝一个字符串到另一个 
用 法: char *stpcpy(char *destin, char *source); 

函数名: strlen
功 能: 计算字符串的长度,但不包括\0.


6)字符串数组
char s1[][5] = {"haha","ha","a"};
char *s2[3] = {"haha","ha","a"}
数组s2中每个元素都是个char型指针。
7)几组易混淆关系
优先级:()>[]>*
指针数组:int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
数组指针:也称为指向数组的指针,int (*p)[4]; int *b=new int[4]
 //数组指针,这个指针能够用来指向含有4个元素的整数数组,如
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
 p=a;        //将该二维数组的首地址赋给p
 p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

------------函数指针--------------------- . 
int (*p)( ); // 指向函数的指针,这里声明了一个指针p,
该指针指向返回值是整型(即函数类型为整型)的函数。

----------------指针函数--------------------------- ... 
int *p(int a,float b); //返回值为指针的函数...该函数返回指向整型变量的指针! 

指针常量:如数组a[5],a就是指针常量,不可以修改所指向的地址,但可以修改指向的内容。还可以表示成char* const p1;
常量指针:指向常量的指针,如 char* s="aaa"; 指向一个常量字符串,不可修改所指向的内容。还可以表示成char const* p2;const char* p3;
由此可见:const位于*左侧的话,限制修饰值;位于*右侧的限制指针本身,即指向的地址。

2,C语言其他
1)全局变量与局部变量
全局变量:定义在函数外面,作用域从变量定义到整个c程序运行结束;
局部变量:定义在函数内,作用域到函数结束,仅限于该函数内部使用;

static修饰符:作用于局部变量时,相当于开辟了一个持久内存空间,每次调用该函数,该变量仍是上次处理的值,不会从新赋值。作用于全局变量时,与extern相反,限制全局变量的使用范围,只限于该C文件内部使用,不可以被其他c文件调用。

extern:扩大全局变量的使用范围,可以在多个C文件中使用一个已定义的全局变量,使用前用用extern声明一下。

当全局变量与局部变量重名时,全局变量的作用于需要减去该局部变量的作用域。

2)include     包含另一个文件,可以调用另一个文件中的常量,全局变量和函数。
一般用include包含.h文件,在.h中定一些需要被外部调用的内容,如需要被外部调用的函数的声明,变量声明,常量等。
3)动态存储分配

int *p1,*p2;

P1=(int *)malloc(sizeof(int)*4);

p2=(int *)calloc(20, sizeof(int));

free(p1);free(p2);  

这两个相对于new/delete的区别在于产生对象不调用构造和析构函数。

4)结构体和共用体
结构体:定义新的数据类型,此数据类型是基本数据类型的组合形式。类似于class,但内部只能有成员变量,不能有方法。需要对该结构体操作只能将结构体的引用变量传到另外的函数中实现。而class内部的方法就可以实现。
公用体:共用体的所有变量共用同一个存储单元。



3,C++内存管理
1)堆对象:使用new/delete 或者 malloc产生的。对空间较大,但空间释放需要自己控制,且易产生内存碎片。
2)栈对象:对空间较小,但函数执行完毕自动释放堆空间,效率高。
3)常量区对象:全局变量或者静态变量。对象中静态成员的生命周期为该类的第一个对象产生到程序结束。

4)内存泄露的原因:

总结下来,内存泄露大概有一下几个原因:

1、编码错误:malloc、realloc、new申请的内存在堆上,需要手动显示释放,调用free或delete。申请和释放必须成对出现malloc/realloc对应free,new对应delete。前者不会运行构造/析构函数,后者会。对于C++内置数据类型可能没差别,但是对于自己构造的类,可能在析构函数中释放系统资源或释放内存,所以要对应使用。

2、“无主”内存:申请内存后,指针指向内存的起始地址,若丢失或修改这个指针,那么申请的内存将丢失且没有释放。

3、异常分支导致资源未释放:程序正常执行没有问题,但是如果遇到异常,正常执行的顺序或分支会被打断,得不到执行。所以在异常处理的代码中,要确保系统资源的释放。

4、隐式内存泄露:程序运行中不断申请内存,但是直到程序结束才释放。有些服务器会申请大量内存作为缓存,或申请大量Socket资源作为连接池,这些资源一直占用直到程序退出。服务器运行起来一般持续几个月,不及时释放可能会导致内存耗尽。

5、类的析构函数为非虚函数:析构函数为虚函数,利用多态来调用指针指向对象的析构函数,而不是基类的析构函数。


5)内存泄漏检测:http://www.educity.cn/develop/478773.html

最常用的VC自带的DebugView工具来查看,如果程序main退出时有泄漏,会显示内存泄露信息。基本原理是重载了new,在每一块分配的内存中加入了动态指针来跟踪。

6)内存碎片:operator new的一个用处就是内存池优化,内存池的一个常见策略就是分配一次性分配一块大的内存作为内存池(buffer或pool),然后重复利用该内存块,每次分配都从内存池中取出,释放则将内存块放回内存池。在我们客户端调用的是new运算符,我们可以改写operator new函数,让它从内存池中取出(当内存池不够时,再从系统堆中一次性分配一块大的),至于构造和析构则在取出的内存上进行,然后再重载operator delete,它将内存块放回内存池。这样解决内存碎片。placement newoperator new的一个重载版本,placement new允许你在一个已经分配好的内存中(栈或堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数。
void *operator new( size_t, void *p ) throw()     { return p; }
pi = new (ptr) int;

4,const的作用
C/C++ - Garfield - 张广辉的博客
const在修饰指针或引用时有三种写法
const int* a1=&b;           *a1=1错误
int* const a2=&b;           a2=&c错误
const int* const a2=&b    哪样都错

5,sizeof,返回占用的内存大小
1)基本数据类型的长度
http://blog.csdn.net/zhongzhiwei/article/details/8678885
数组的长度为内部存放元素的长度,而当数组作为函数参数传递后,就只有4个字节。
2)结构体大小,注意补齐原则:即结构体的长度要能被最长数据类型的长度所整除,不足的补齐。
3)共用体大小是成员变量中最大的一个,也要遵循补齐原则。
4)含有虚函数的对象,
如果没有虚函数,普通函数不占内存,则对象的大小就是成员变量的大小。
如果有虚函数,不管有多少,对象需要一个指针来维护虚函数表,占4个字节。
 
6,内联函数
主要用于定义seters and getters函数。
优点:
C/C++ - Garfield - 张广辉的博客
缺点:
C/C++ - Garfield - 张广辉的博客
  
7,引用
1)只能在初始化时赋值,int &pa=a;
2)  指针引用,*&a,类似于二维指针
3)当作为函数参数时,形参其实就是实参,可以改变传入参数得值。函数的参数尽量使用引用,因为当形参为其他时,执行该函数时,相当于产生出一个局部变量,占用空间较大;而引用就是变量的别名,不单独占空间。
4)指针和引用:指针的指向可以随时改变并且可以为null而引用必须在初始化时指定不可改变不可为空。

8,virtual的作用,多态时的动态绑定(子类重写父类方法时的绑定)
C/C++ - Garfield - 张广辉的博客
爷爷类中fun函数加virtual,输出Father call
爷爷类中fun函数不加virtual,输出GrandFather call
 
多态的定义形式:GrandFather *gf = new Father;
或者通过函数传参:
void print(GrandFather &gf) {
gf.fun();
void print(GrandFather *gf) {
gf->fun();
但值传入不可以
void print(GrandFather gf) {
gf.fun();

虚函数表:存在于类的首部,以指针的形式存放。类的每个对象都维护一个该表。

9,指针动态内存分配的三种方式
C/C++ - Garfield - 张广辉的博客
 void GetMemory5(){
char p[] = "hello world";
return p;
}

其中1和5不可以。1中虽然传入了指针,但参数传入后,相当于产生了一个新指针,而函数在结束前并没有把新指针返回,所以无效;5中返回的是一个指向栈内存的指针,所以此函数结束后,虽然指针指向的地址没有变,但内容已经消失。
2和3用了指向指针的指针和指针引用,都可以改变传入指针的指向。

10,空类中默认的成员函数
http://blog.csdn.net/makuiyu/article/details/8047340
1)构造函数能重载,不能是虚函数;
2)析构函数不能被重载,且没有参数,当作为父类被继承时,尽量声明为虚函数;
比如 Father f = new son;
delete f;
此时,如果虚构函数不是虚函数,则在delete是指会调用父类的析构函数。
3)拷贝构造与赋值函数
class    A;     
A   a;  
A   b=a;    //拷贝构造函数调用 ,动态生成一个对象,返回新对象
//或  
A   b(a);    //拷贝构造函数调用  
///////////////////////////////////     
A   a;  
A   b;  
b =a;    //赋值运算符调用  ,已经存在的对象,返回原对象的引用

一个对象以值传递的方式传入函数体 或者一个对象以值传递的方式从函数返回 也会自动调用拷贝构造,会产生一些临时对象。

C/C++ - Garfield - 张广辉的博客C/C++ - Garfield - 张广辉的博客
 
C/C++ - Garfield - 张广辉的博客
 
11,继承分为 public protected和private
以private为例,class Son : private Father { }
如果Father中存在private成员,则不能被继承。Father中的public和protect成员则在子类中变为private。

12,友元函数和友元类
http://baike.baidu.com/link?url=pc2cfXFv6J__nYVGrBQQagJsGVwYyppq_3MzDZwZ226LyP-ISK8-jGevQ-7jgIvbivNARJYoZd1eq2NhePWoKK

13,运算符重载
1)类内部重载
C/C++ - Garfield - 张广辉的博客C/C++ - Garfield - 张广辉的博客
 
2)类外部函数重载
C/C++ - Garfield - 张广辉的博客C/C++ - Garfield - 张广辉的博客
 
http://blog.csdn.net/assemble8086/article/details/6842766
0 0