C++学习总结 写C++程序时的注意事项

本文深入探讨了C++编程的基础知识与高级技巧,包括命名空间、指针操作、类的实例大小、类指针的隐式转换等问题,旨在帮助开发者理解和掌握C++的核心概念。


1. 包含以下头文件时

#include <iostream>

要加上这个
using namespace std;

      命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加".h"

       当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用< iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。

2. 如果想通过指针返回参数值千万别只定义指针而没给指针分配空间,就用这个指针来获取返回的值。要用如下的方法先为指针分配空间。
例如:int *p;
           p = (int *)malloc(sizeof(int));
           只有这样给指针分配了空间,才将返回值在于*p中,最后得到返回结果。
3. 更改指针中存放的地址的方法

 

#include "stdafx.h"
#include <iostream>
using namespace std;
void change_p(int **p)
{
	*p = new int;//更改指针的内容
}

int main(int argc, char* argv[])
{
	int *p = NULL;
	printf("&p:%d\n",&p);//打印指针的地址
	printf("p:%d\n",p);//打印指针中存放的地址

	change_p(&p);//更改指针中存放的地址
	printf("&p:%d\n",&p);//打印指针的地址
	printf("p:%d\n",p);//打印更改指针中存放的地址后是否指针中存放的地址发生变化了
	printf("Hello World!\n");
	return 0;
}


       如在链表中查找某一元素,然后在另一函数中删除该元素。则就需要先返回这个元素的前驱结点的地址,则就用上述方法去解决吧


4. 指针数组与数组指针

#include "StdAfx.h"
#include <iostream>
using namespace std;


int main(int argc, char* argv[])
{
	static int c[2] = {1,2};

	int *ptr[5];//指针数组,每个成员都相当于一个指针

	int a = 5;
	int b=2;
	int *page,*page2;

	page = &a;
	page2 = &b;

	ptr[0] = &a;
	ptr[1] = page2;

	cout << *ptr[0] <<endl;
	cout << *page <<endl;
	cout << *ptr[1] << endl;
	return 0;
}


 

include "StdAfx.h"
#include <iostream>
using namespace std;


int main(int argc, char* argv[])
{
	int Test1[2][3] = {{1,2,3},{4,5,6}};//二维数组

	int Test2[3] = {1,2,3};//一维数组

	int (*p_A)[3];//数组指针
	int *p_B[3];

	p_A = &Test1[0];//要保证二维的大小相同,或者用下面的方法
	//p_A = Test1;
	p_B[0] = Test2;

	cout << (*p_A)[0]<<" "<<(*p_A)[1]<<" "<< (*p_A)[2]<<endl;//访问第一行的元素
	cout << (*(p_A+1))[0]<<" "<<(*(p_A+1))[1]<<" "<< (*(p_A+1))[2]<<endl;//访问第二行的元素
	cout << *p_B[0]<<endl;

	return 0;
}


 

5. 静态成员变量定义时必须赋初值

 

6. 静态成员调为私有 通过公有静态成员函数访问

 

#include "stdafx.h"
#include <iostream>
using namespace std;

class Cat{

public:
	Cat(int age):itsAge(age)
	{
		HowManyCats++;
	}

	virtual ~Cat()
	{
		HowManyCats--;
	}

	virtual int GetAge()
	{
		return itsAge;
	}

	virtual void SetAge(int age)
	{
		itsAge = age;
	}

	static int GetHowMany()//静态公有成员函数
	{
		return HowManyCats;
	}

private:
	int itsAge;
	static int HowManyCats;
};


int Cat::HowManyCats = 0;//静态成员变量一定要记得初始化

void tele()//调用静态公有成员函数
{
	cout << "There are " << Cat::GetHowMany() << " Cats alive!\n";
}

int main(int argc, char* argv[])
{
	const int MaxCats = 5;
	int i;
	Cat *CatHouse[MaxCats];

	for(i = 0; i<MaxCats; i++)
	{
		CatHouse[i] = new Cat(i);
		tele();
	}

	for(i = 0; i<MaxCats; i++)
	{
		delete CatHouse[i];
		tele();
	}
	printf("Hello World!\n");
	return 0;
}


7. 自己实现String类,加深拷贝构造函数,运算符重载的理解

#include "stdafx.h"
#include <iostream>
#include <string.h>
using namespace std;

class String{

public:
	String(const char *str = NULL);
	String(const String &other);
	String & operator = (const String &other);
	void Print()
	{
		cout << m_data <<endl;
	}
	~String();
private:
	char *m_data;
};
//因为成员变量为一个指针,构造函数已经new操作了,所以析构函数中应该释放已申请的内存
String::~String()
{
	delete [] m_data;
}
//构造函数,初始化成员变量/为成员变量赋值
String::String(const char *str)
{
	if(NULL == str)//查空操作
	{
		str = new char[1];
		*m_data = '\0';
	}
	else
	{
		int length = strlen(str);
		m_data = new char[length+1];
		strcpy(m_data,str);
	}
}
//拷贝构造函数,当创建一个对象的同时,用已有的对象来初始化该对象时要用到
//说明:其实拷贝构造函数和赋值函数(即下面的运算操作符重载函数)功能是相同的,为了不造成重复的代码,用了如下的方法
String::String(const String &other)
{
	/*int length = strlen(other.m_data);
	m_data = new char[length+1];
	strcpy(m_data,other.m_data);*/

	*this = other;
}
//运算操作符“=”重载,当创建了一个对象,然后用旧对象来给新对象赋值时要用到
String & String::operator = (const String &other)
{
	if(this == &other)//检查自赋值
		return *this;
	//释放原有的资源
	delete [] m_data;
	int length = strlen(other.m_data);
	//分配新资源,并复制内容
	m_data = new char[length+1];
	strcpy(m_data,other.m_data);
	//返回本对象的引用
	return *this;
}

int main(int argc, char* argv[])
{
	String s1("this is a dog.");
	s1.Print();
	String s2("this is a monkey.");
	s2.Print();
	s2 = s1;
	s2.Print();
	printf("Hello World!\n");
	return 0;
}

 

更专业的写法

#include "stdafx.h"
#include <iostream>
#include <string.h>
using namespace std;

class String{

public:
	String(const char *str = NULL);
	String(const String &other);
	String & operator = (const String &other);
	void Print()
	{
		cout << m_data <<endl;
	}
	~String();
private:
	char *m_data;
};
//因为成员变量为一个指针,构造函数已经new操作了,所以析构函数中应该释放已申请的内存
String::~String()
{
	delete [] m_data;
}
//构造函数,初始化成员变量/为成员变量赋值
String::String(const char *str)
{
	if(NULL == str)//查空操作
	{
		str = new char[1];
		*m_data = '\0';
	}
	else
	{
		int length = strlen(str);
		m_data = new char[length+1];
		strcpy(m_data,str);
	}
}
//拷贝构造函数,当创建一个对象的同时,用已有的对象来初始化该对象时要用到
//说明:其实拷贝构造函数和赋值函数(即下面的运算操作符重载函数)功能是相同的,为了不造成重复的代码,用了如下的方法
String::String(const String &other)
{
	/*int length = strlen(other.m_data);
	m_data = new char[length+1];
	strcpy(m_data,other.m_data);*/

	*this = other;
}
//运算操作符“=”重载,当创建了一个对象,然后用旧对象来给新对象赋值时要用到
String & String::operator = (const String &other)
{
	if(this == &other)//检查自赋值
		return *this;

	char *pTemp = NULL;
	
	int length = strlen(other.m_data);
	//分配新资源,并复制内容
	pTemp = new char[length+1];
	if(pTemp != NULL)
	{
		//释放原有的资源
		delete [] m_data;
		//m_data指向新分配的资源
		m_data = pTemp;
		strcpy(m_data,other.m_data);
		pTemp = NULL;
	}
	//返回本对象的引用
	return *this;
}

int main(int argc, char* argv[])
{
	String s1("this is a dog.");
	s1.Print();
	String s2("this is a monkey.");
	s2.Print();
	s2 = s1;
	s2.Print();
	printf("Hello World!\n");
	return 0;
}


8.全局变量和静态变量分别存储在什么地方的

       都存在静态数据区,编译时就分配好了。全局静态变量和局部静态变量存储的位置相同,只是作用域不同      
      静态存储区用来保存全局变量或者静态变量,全局变量在程序开始执行时始终占据此存储单元,直到程序结束才释放,如果在定义静态变量时不赋初值,系统对数值型变量赋初始值为0,而对字符型变量自动赋初始值为'\0',如果全局变量 int a在main()函数外定义,则编译时自动初始化为0,char aa自动初始化为‘\0’

      初始化的全局变量和静态变量放在一块,没有初始化的全局变量和静态变量放在一块。


#include "stdafx.h"
#include <iostream>
using namespace std;

int a;
char aaa;

int main(int argc, char* argv[])
{
	
	static int aa;
	cout << a <<" " << aa << " " <<aaa<<endl;
	printf("%d\n",(int)aaa);
	printf("%d,%d,%d\n",&a,&aa,&aaa);
	printf("Hello World!\n");
	return 0;
}

有图有真像



9. 类的实例 sizeof 测其所占空间的大小

1) 空类

#include <iostream>
#include <string.h>
using namespace std;

class A{
};

int main(void)
{
	A a;

	cout << sizeof(a)<<endl;
	
	return 0;
	
}


      分析 :空类的实例中不包含任何信息,本来求 sizeof应该是 0,但是当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占用多少内存,由编译器决定。Visual Studio中每个空类型的实例占用1字节的空间。

如果在该类中添加一个构造函数和析构函数,再对该类型求 sizeof,得到的结果又是多少?

      答案:还是1。调用构造函数和析构函数只需要知道函数的地址即可,而这些函数的地址只与类型相关,而与类型的实例无关,编译器也不会因为这两个函数而在实例内添加任何额外的信息。

 那如果把析构函数标记为虚函数呢?

      答案:C++的编译器一旦发现一个类型中有虚拟函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。在 32 位的机器上,一个指针占 4 字节的空间,因此求 sizeof 得到 4;如果是 64位的机器,一个指针占8 字节的空间,因此求sizeof则得到8。

2)  派生类
#include "stdafx.h"
#include <iostream>
using namespace std;

class A{
	int m_nA;

};

class B:public A{
	int m_nB;
};

class C:public B{
	int m_nC;
};

int main(void)
{
	A a;
	B b;
	C c;
	
	cout << sizeof(a)<<endl;
	cout << sizeof(b)<<endl;
	cout << sizeof(c)<<endl;
	
	return 0;
	
}


3)  派生类 另一种情况
#include "stdafx.h"
#include <iostream>
using namespace std;

class A{
	int m_nA;

};

class B{
	int m_nB;
};

class C:public A,public B{
	int m_nC;
};

int main(void)
{
	A a;
	B b;
	C c;
	
	cout << sizeof(a)<<endl;
	cout << sizeof(b)<<endl;
	cout << sizeof(c)<<endl;
	
	return 0;
	
}



3) 当含有纯虚函数的时候

#include "stdafx.h"
#include <iostream>
using namespace std;

class A{
	int m_nA;
	virtual void fun() = 0;
};

class B: public A{
	int m_nB;
	virtual void fun(){};
};

class C:public B{
	int m_nC;
	virtual void fun(){};
};

int main(void)
{
	//A a;//会报错,因为A中有一个纯虚函数,所以不能初始化实例
	B b;
	C c;
	
	//cout << sizeof(a)<<endl;
	cout << sizeof(b)<<endl;
	cout << sizeof(c)<<endl;
	
	return 0;
	
}


4) 基类中含virtual虚函数,然后派生子类并用sizeof 测其大小

在VC 下测试 


不用 virtual 派生时

#include "stdafx.h"
#include <iostream>
using namespace std;

class A{
	char k[3];

public:
	A(){}
	virtual void aa(){};
	~A(){}
};

class B: public A{

		char j[3];
public:
	virtual void bb(){}
	
};
class C:public B{

	char i[3];
public:
	virtual cc(){}
};





int main(int argc, char* argv[])
{
	cout << "A " <<sizeof(A)<<endl;
	cout << "B " <<sizeof(B)<<endl;
	cout << "C " <<sizeof(C)<<endl;
	printf("Hello World!\n");
	return 0;
}


分析:3(个char型) + 4(一个指向虚函数的虚表指针),对齐后 = 8; 应该是将现在的这个虚函数的虚表指针加入虚表,又加了3(个char型) 对齐后 = 12,同理 =16


基类 A ,派生类B,派生类C中都有同名虚函数,而B类继承A,C类继承B,结果也是一样的

#include "stdafx.h"
#include <iostream>
using namespace std;

class A{
	char k[3];
	
public:
	A(){}
	virtual void aa(){};
	~A(){}
};

class B: public A{
	
	char j[3];
public:
	virtual void aa(){}
	
};
class C:public B{
	
	char i[3];
public:
	virtual void aa(){}
};





int main(int argc, char* argv[])
{
	cout << "A " <<sizeof(A)<<endl;
	cout << "B " <<sizeof(B)<<endl;
	cout << "C " <<sizeof(C)<<endl;
	printf("Hello World!\n");
	return 0;
}

分析:3(个char型) + 4(一个指向虚函数的虚表指针),对齐后 = 8; 应该是将现在的这个虚函数的虚表指针加入虚表,又加了3(个char型) 对齐后 = 12,同理 =16


只有基类 A 中有虚函数,而B类继承A,C类继承B。B类、C类中aa 函数A类虚函数的覆盖,结果也是一样的
#include "stdafx.h"
#include <iostream>
using namespace std;

class A{
	char k[3];
	
public:
	A(){}
	virtual void aa(){};
	~A(){}
};

class B: public A{
	
	char j[3];
public:
	void aa(){}
	
};
class C:public B{
	
	char i[3];
public:
	void aa(){}
};





int main(int argc, char* argv[])
{
	cout << "A " <<sizeof(A)<<endl;
	cout << "B " <<sizeof(B)<<endl;
	cout << "C " <<sizeof(C)<<endl;
	printf("Hello World!\n");
	return 0;
}


分析:3(个char型) + 4(一个指向虚函数的虚表指针),对齐后 = 8; 应该是将现在的这个虚函数的虚表指针加入虚表,又加了3(个char型) 对齐后 = 12,同理 =16




5)  基类中含virtual虚函数,派生自虚(virtual)基类并用 sizeof 测其大小
在 VC 下测试,加 virtual 派生时

#include "stdafx.h"
#include <iostream>
using namespace std;

class A{
	char k[3];

public:
	A(){}
	virtual void aa(){};
	~A(){}
};

class B: public virtual A{

		char j[3];
public:
	virtual void bb(){}
	
};
class C:public virtual B{

	char i[3];
public:
	virtual cc(){}
};





int main(int argc, char* argv[])
{
	cout << "A " <<sizeof(A)<<endl;
	cout << "B " <<sizeof(B)<<endl;
	cout << "C " <<sizeof(C)<<endl;
	printf("Hello World!\n");
	return 0;
}


分析:3(3个char型) + 4(一个指向虚函数的虚表指针),对齐后 = 8;4(指向B 类自己的虚函数的虚表指针) + 4(指向 A 类的虚表指针) +3(B类的3个char型),对齐后 = 20;  同理 = 32 ,注意指向虚函数的虚表指针与指针虚类的虚表指针是不同的,并且与上面不同的是:每个类指向自己虚函数的虚表指针也是不同的了

 

在 gcc 下测试,基类中含virtual虚函数,派生自虚(virtual)基类,结果就与上面 VC 下不同了



分析:指向虚函数的虚表指针与指针虚类的虚表指针是不同的,但又与上面不同的是:各类指向自己虚函数的虚表指针可能都放在最古老的基类的虚表指针中了:20-4=16;32-8=24

A类含有虚函数,B 虚继承A,C虚继承B,并且都有同名的虚函数

#include "stdafx.h"
#include <iostream>
using namespace std;

class A{
	char k[3];

public:
	A(){}
	virtual void aa(){};
	~A(){}
};

class B: public virtual A{

		char j[3];
public:
	virtual void aa(){}
	
};
class C:public virtual B{

	char i[3];
public:
	virtual void aa(){}
};





int main(int argc, char* argv[])
{
	cout << "A " <<sizeof(A)<<endl;
	cout << "B " <<sizeof(B)<<endl;
	cout << "C " <<sizeof(C)<<endl;
	printf("Hello World!\n");
	return 0;
}



分析:aa函数是放在同一个虚表里的,是共用一个虚表

总结一下:
前提:如果父类A中有虚函数!
① 派生类是虚继承 A类而且有同名虚函数则所占空间只增加 4(指向虚基类的虚表指针)派生类是虚继承 A类而且有同名函数覆盖则所占空间只增加 4(指向虚基类的虚表指针)。说明只要是与虚基类A的同名函数所占空间只增加 4(指向虚基类的虚表指针)
② 派生类是虚继承 A类而且有多个不同名虚函数则所占空间增加4(派生类虚表指针,多个不同名虚函数共享一个虚函数虚表指针)+4(指向虚基类的虚表指针)


另外注意虚函数继承虚继承(如class B :public virtual A)是不同的,上面的例子中用的就是虚继承

 

 10. 类指针的隐式转换问题

 

当继承顺序为:public A,public B

#include <iostream>
#include <string.h>
using namespace std;

class A{
	int m_nA;
};

class B{
	int m_nB;
};

class C:public A,public B{
	int m_nC;
};

int main(void)
{
	C *pC = new C;
	//B *pB = dynamic_cast<B *>(pC);
	//A *pA = dynamic_cast<A *>(pC);

	B *pB = (B *)pC;
	A *pA = (A *)pC;

	cout<<"pC "<<pC<<endl;
	cout<<"pB "<<pB<<endl;
	cout<<"pA "<<pA<<endl;
	cout<<"(C *)pB "<<(C *)pB <<endl;
	cout<<"(A *)pC "<<(A *)pC <<endl;
	cout<<"pC "<<pC<<endl;

	if(pC == pB)
		cout<<" pC == pB equal"<<endl;
	else
		cout<<"pC == pB Not equal"<<endl;
	cout<<"pC "<<pC<<endl;
	cout<<"pB "<<pB<<endl;
	cout<<"int (pC) "<<int (pC)<<endl;
	cout<<"int (pB) "<<int (pB)<<endl;

	if(int (pC) == int (pB))
		cout<<"int (pC) == int (pB) equal"<<endl;
	else
		cout<<"int (pC) == int (pB) Not equal"<<endl;

	if(int (pC) == int ((C*)pB))
		cout<<"int (pC) == int ((C*)pB equal"<<endl;
	else
		cout<<"int (pC) == int ((C*)pB Not equal"<<endl;
	return 0;
	
}


有图有真像

 

当继承顺序为:public B,public A

 

#include <iostream>
#include <string.h>
using namespace std;

class A{
	int m_nA;
};

class B{
	int m_nB;
};

class C:public B,public A{
	int m_nC;
};

int main(void)
{
	C *pC = new C;
	//B *pB = dynamic_cast<B *>(pC);
	//A *pA = dynamic_cast<A *>(pC);

	B *pB = (B *)pC;
	A *pA = (A *)pC;

	cout<<"pC "<<pC<<endl;
	cout<<"pB "<<pB<<endl;
	cout<<"pA "<<pA<<endl;
	cout<<"(C *)pB "<<(C *)pB <<endl;
	cout<<"(A *)pC "<<(A *)pC <<endl;
	cout<<"pC "<<pC<<endl;

	if(pC == pB)
		cout<<" pC == pB equal"<<endl;
	else
		cout<<"pC == pB Not equal"<<endl;
	cout<<"pC "<<pC<<endl;
	cout<<"pB "<<pB<<endl;
	cout<<"int (pC) "<<int (pC)<<endl;
	cout<<"int (pB) "<<int (pB)<<endl;

	if(int (pC) == int (pB))
		cout<<"int (pC) == int (pB) equal"<<endl;
	else
		cout<<"int (pC) == int (pB) Not equal"<<endl;

	if(int (pC) == int ((C*)pB))
		cout<<"int (pC) == int ((C*)pB equal"<<endl;
	else
		cout<<"int (pC) == int ((C*)pB Not equal"<<endl;
	return 0;
	
}


有图有真像

综上说明了什么问题?

说明了派生类如果就基于多个基类,那么派生类指针pC应该与下面的情况有关, 当继承顺序为:public A,public B,

B *pB = (B *) pC,pB = pC + 4,[4原因为 C 类的 int 型变量的大小],所以pC != pB;

而public B,public A,B *pB = (B *) pC,则pC == pB,


派生类对象自身成员变量与基类成员变量的空间分配

#include <iostream>
#include <string.h>
using namespace std;

class A{
public:
	int m_nA;
};

class B{
public:
	int m_nB;
};

class C:public B,public A{
public:
	int m_nC;
};

int main(void)
{
	C *pC = new C;
	//B *pB = dynamic_cast<B *>(pC);
	//A *pA = dynamic_cast<A *>(pC);
	
	B *pB = (B *)pC;
	A *pA = (A *)pC;
	
	cout<<"pC          "<<pC<<endl;
	cout<<"&(pC->m_nC) "<<&(pC->m_nC)<<endl;
	cout<<"&(pB->m_nB) "<<&(pB->m_nB)<<endl;
	cout<<"&(pA->m_nA) "<<&(pA->m_nA)<<endl;



	
	cout<<"pC "<<pC<<endl;
	cout<<"pB "<<pB<<endl;
	cout<<"pA "<<pA<<endl;
	cout<<"(C *)pB "<<(C *)pB <<endl;
	cout<<"(A *)pC "<<(A *)pC <<endl;
	cout<<"pC "<<pC<<endl;
	
	if(pC == pB)
		cout<<" pC == pB equal"<<endl;
	else
		cout<<"pC == pB Not equal"<<endl;
	cout<<"pC "<<pC<<endl;
	cout<<"pB "<<pB<<endl;
	cout<<"int (pC) "<<int (pC)<<endl;
	cout<<"int (pB) "<<int (pB)<<endl;
	
	if(int (pC) == int (pB))
		cout<<"int (pC) == int (pB) equal"<<endl;
	else
		cout<<"int (pC) == int (pB) Not equal"<<endl;
	
	if(int (pC) == int ((C*)pB))
		cout<<"int (pC) == int ((C*)pB equal"<<endl;
	else
		cout<<"int (pC) == int ((C*)pB Not equal"<<endl;
	return 0;
	
}

有图说明


说明空间分布:B类的成员m_nB,A类成员m_nA,派生类的成员m_nC,由继承的顺序决定class C:public B,public A

 11. 定义一个类,即使构造函数为空,但是自己显示声明的,一定要在其后加上 { },其它如果它是基类的话,再派生其它类时可能在运行时出错,因为你只是声明了构造函数,而非实现了

 

不好的写法

class A{

public:
	A();
	~A();
private:
	int m_number;
};

 

正确的写法

class A{

public:
	A()
	{

	}
	~A()
	{

	}
private:
	int m_number;
};


 12. 重载 赋值运算符 (assignmet operation) “=”时,一定要在最后返回一个 reference to*this (reference: 引用)

String & String::operator = (const String &other)
{
	if(this == &other)//检查自赋值
		return *this;
	//释放原有的资源
	delete [] m_data;
	int length = strlen(other.m_data);
	//分配新资源,并复制内容
	m_data = new char[length+1];
	strcpy(m_data,other.m_data);
	//返回本对象的引用
	return *this;
}

 

 13. 嵌入式细节问题

 

int main(void)
{	
	
	char *ptr;
	char *ptr2;
	if((ptr = (char *)malloc(sizeof(0))) == NULL)
		printf("Got a null pointer!\n");
	else
		printf("Got a valid pointer!\n");


	printf("\n Second example:\n");
	if(int pp = (sizeof(ptr2 = (char *)malloc(0))) == 4)
		printf("Got a null pionter!\n");
	else
		printf("Got a valid pointer!\n");


	typedef struct bitstruct{
		int b1:5;
		int :2;
		int b2:2;
	}bitstruct;
	bitstruct b;
	printf("sizeof(bitstruct):%d\n",sizeof(bitstruct));
	memcpy(&b,"EMC EXAMINATION", sizeof(b));
	printf("%d,%d\n",b.b1,b.b2);
	return 0;
	
}

结果

 

 

 14. 注意 printf 的秘密


 

int main(void)
{
	int i = 1;
	printf("%d,%d\n",++i,++i);
	i = 1;
	printf("%d,%d\n",i++,i++);

	return 0;
}

 

 

15. 理解多维数组,和数组指针

#include "StdAfx.h"
#include "stdio.h"
int main(void)
{
	int apricot[2][3][5];
	int (*r)[5] = apricot[0];
	int *t = apricot[0][0];
	printf("r:  %x,  t:%x\n",r++,t++);
	printf("r+1:%x,t+1:%x\n",r,t);
	printf("&(*r)[5]:%x\n",&(*r)[5]);
	return 1;
}



r+1 的偏移 5*4 = 20 = (1C - 8)
t+1 的偏移 = 4 = (C - 8)


16. 以数组形式传参和以指针形式传参对比

#include "StdAfx.h"
#include "stdio.h"

char ga[] = "abcdefghijklm";

void my_array_func(char ca[20])
{
	printf("addr of array param= %x\n", ca);
	printf("addr of &(ca[0]) = %x\n", &(ca[0]));
	printf("addr of &(ca[1]) = %x\n", &(ca[1]));
	printf("++ca = %x\n", ++ca);
}

void my_pionter_func(char *pa)
{
	printf("addr of ptr param= %x\n", pa);
	printf("addr of &(pa[0]) = %x\n", &(pa[0]));
	printf("addr of &(pa[1]) = %x\n", &(pa[1]));
	printf("++pa = %x\n", ++pa);

}


int main(void)
{
	printf("addr of global array = %x\n", ga);
	printf("addr of &(ga[0]) = %x\n", &(ga[0]));
	printf("addr of &(ga[1]) = %x\n", &(ga[1]));
	
	printf("\ncall my_array_func(ga)\n");
	my_array_func(ga);

	printf("\ncall my_pionter_func(ga)\n");
	my_pionter_func(ga);
	return 1;
}


说明数组名当指针用了,指针的内容是一样的,都是地址:427318

#include "StdAfx.h"
#include "stdio.h"

char ga[] = "abcdefghijklm";

void my_array_func(char ca[20])
{
	printf("addr of array param= %x\n", &ca);
	printf("addr of &(ca[0]) = %x\n", &(ca[0]));
	printf("addr of &(ca[1]) = %x\n", &(ca[1]));
	printf("++ca = %x\n", ++ca);
}

void my_pionter_func(char *pa)
{
	printf("addr of ptr param= %x\n", &pa);
	printf("addr of &(pa[0]) = %x\n", &(pa[0]));
	printf("addr of &(pa[1]) = %x\n", &(pa[1]));
	printf("++pa = %x\n", ++pa);

}


int main(void)
{
	printf("addr of global array = %x\n", ga);
	printf("addr of &(ga[0]) = %x\n", &(ga[0]));
	printf("addr of &(ga[1]) = %x\n", &(ga[1]));
	
	printf("\ncall my_array_func(ga)\n");
	my_array_func(ga);

	printf("\ncall my_pionter_func(ga)\n");
	my_pionter_func(ga);
	return 1;
}



 
说明是对传入的数组名(当成指针了),作为一个指针拷贝作形式

17. STL 中 vector 的用法

#include <iostream>
#include <vector>
using namespace std;


int main(void)
{
	int i = 0;
	vector<int> vec;
	vec.push_back(20);
	vec.push_back(30);
	vector<int>::iterator p;
	p = vec.begin();
	cout << vec[i++]<<endl;
	cout << vec[i]<<endl;
//	cout << *p++<<endl;
//	cout << *p<<endl;
	while(p != vec.end())
			cout << *p++<<endl;
	cout << vec.front()<<endl;
	cout << vec.back()<<endl;
	vec.pop_back();
	cout <<vec.size()<<endl;

	return 1;
}


 18. 浅拷贝问题

#include <iostream>
//#include "stdio.h"
#include <vector>
using namespace std;

class CDemo{

public:
	CDemo():str(NULL){};
	~CDemo()
	{
		if(str)
			delete[]str;
	}
	char *str;
};
int main(void)
{
	CDemo d1;
	d1.str = new char[32];
	strcpy(d1.str, "trend micro");

	vector<CDemo> *a1 = new vector<CDemo>();
	a1->push_back(d1);
	delete a1;

	return 1;
}


会出问题,CDemo类的 str 被析构了再次,因为a1->push_back(d1)是调用了默认的得制构造函数

 解决办法,通过复制构造函数深拷贝

#include "stdafx.h"

#include <iostream>
//#include "stdio.h"
#include <vector>
using namespace std;

class CDemo{

public:
	CDemo():str(NULL){};
	CDemo(const CDemo &cd)
	{
		this->str = new char[strlen(cd.str)+1];
		strcpy(str, cd.str);
	}
	~CDemo()
	{
		if(str)
			delete[]str;
	}
	char *str;
};
int main(void)
{
	CDemo d1;
	d1.str = new char[32];
	strcpy(d1.str, "trend micro");

	vector<CDemo> *a1 = new vector<CDemo>();
	a1->push_back(d1);
	delete a1;

	return 1;
}


 19. vector 的 erase() 操作

#include "stdafx.h"

#include <iostream>
//#include "stdio.h"
#include <vector>
using namespace std;


int main(void)
{
	vector<int> array;
	array.push_back(1);
	array.push_back(6);
	array.push_back(6);
	array.push_back(3);
	vector<int>::iterator itor;
	vector<int>::iterator itor2;

	itor = array.begin();
	while(itor != array.end())
		cout<< *itor++<<endl;

	itor = array.begin();
	for (itor = array.begin();itor != array.end();)
	{
		if(6 == *itor)
		{
			itor2 = itor;
			array.erase(itor2);
		}
		itor++;
	}

	itor = array.begin();
	while(itor != array.end())
		cout<< *itor++<<endl;
	return 1;
}


只有第一个6被删除了array.erase(itor2) 即array.erase(itor); 这样itor++了一次,所以跳过了下一个6

 

 20. struct 按位定义,sizeof测其大小

struct Sa{
	int x:1;
	int y:2;
	int z:32;//超过32会报错
};
struct Sb{
	int x:1;
	int y:2;
	int z:29;
};
struct Sc{
	int x:1;
	int y:2;
	int z:10;
};
struct Sd{
	int x:1;
	int y:2;
	int z:5;
};
int main(int argc, char* argv[])
{
	cout <<"sizeof(struct Sa):"<<sizeof(struct Sa)<<endl;
	cout <<"sizeof(struct Sb):"<<sizeof(struct Sb)<<endl;
	cout <<"sizeof(struct Sd):"<<sizeof(struct Sc)<<endl;
	cout <<"sizeof(struct Sd):"<<sizeof(struct Sd)<<endl;
	return 0;
}


 

 

 

 

 

 

 

 

 

 

 




 

 









 

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值