对于基本类型,初始化和赋值在语法上比较相像,功能通常也差别不大,例外情况是字符串。字符串在C语言中是个比较特殊的东西,有些特别的处理,C语言学习者通常要花很大精力来学习字符串的使用。对于字符数组,可以用初始化列表和字符串常量两种方式初始化,而后赋值却必须逐位操作,因此对于字符串,初始化和赋值至少在语法上是完全两样的。
C语言中不同变量和数据存储在不同的区域:
代码段 可执行代码、字符串常量
数据段 已初始化全局变量、已初始化全局静态变量、局部静态变量、常量数据
BSS段 未初始化全局变量,未初始化全局静态变量
栈 局部变量、函数参数
堆 动态内存分配
具体内容在此不详述,可以很方便地搜索到。
对于复杂类型例如类,初始化和赋值也是不同的,它们的语义不同,初始化是构造一个新的对象,在初始化时,编译器知道要进行初始化的变量还没有被构造,明确知道这个变量现在的状态,就可以相对简单的对这个变量进行操作;而在赋值时,这个变量先前的状态是什么样的编译器并不清楚,于是就需要进行许多操作来做一些清理等方面的工作。
例如百度百科string类词条:
class String
{
public:
String(const char *str = NULL);// 普通构造函数
String(const String &other); //拷贝构造函数
~ String(void); //析构函数
String & operator =(const String &other);//赋值函数
private:
char *m_data;// 用于保存字符串
};
//普通构造函数
String::String(const char *str)
{
if(str==NULL)
{
m_data = new char[1]; // 对空字符串自动申请存放结束标志'\0'的//加分点:对m_data加NULL 判断
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length+1]; // 若能加 NULL 判断则更好
strcpy(m_data, str);
}
}
// String的析构函数
String::~String(void)
{
delete[] m_data; // 或delete m_data;
}
//拷贝构造函数
String::String(const String &other) // 输入参数为const型
{
int length = strlen(other.m_data);
m_data = new char[length+1]; //对m_data加NULL 判断
strcpy(m_data, other.m_data);
}
//赋值函数
String & String::operator =(const String &other) // 输入参数为const
型
{
if(this == &other) //检查自赋值
return *this;
delete[] m_data; //释放原有的内存资源
int length = strlen( other.m_data );
m_data = new char[length+1]; //对m_data加NULL 判断
strcpy( m_data, other.m_data );
return *this; //返回本对象的引用
}
初始化时调用的是构造函数如String::String(const char *str),而赋值时调用的是String & String::operator =(const String &other),这两个函数主要的区别就是赋值前要先清理旧的数据。对于这个实现,差别还不是特别大,但已经可以看出初始化和赋值的主要不同了。在更复杂的类里,初始化和赋值所做的操作可能相差甚远。
初始化和赋值还有一点不同,即初始化需要检查输入数据的合法性。初始化时,类的构造函数以某些数据作为参数构造对象时,要先检查这些数据(参数)是否合法;但在赋值时,左值和右值的对象都已构造完毕(或由编译器处理,例如常量字符串对象的构造),无须检查对象属性的合法性。按Bjarne的说法,“构造函数需要建立起这个类的不变式,其他函数可以依靠这个不变式,然而又必须维护它”。有过相关经验的人都知道,检查输入合法性之类保障程序健壮性的代码是很繁琐的,所以这是初始化和赋值的一个比较重要的区别。
这篇文章只是点到即止,详细的知识都可以在网上搜到,由于某些原因就不展开了,下篇见~