C++中有关的复合类型变量的知识点的一些见解

转载请注明出处

之前学过C++的内容,但是好久不看,又鉴于编程水准实在是惨不忍睹,因此对于自己的C++编程能力始终“耿耿于怀”,遂在前段时间入手一本《C++ Primer(第五版)中文版》,从头开始复习。但我使用的是蓝色封皮的版本,有些地方讲得不是很详细,遂在看的时候,在书上随手做了些标记,现将其整理出来,以作以后查询复习之用。

好了,闲话少说,进入正题。

稍微有些C++基础的同学会知道,在类的定义出现前,C++中的数据类型可以分为两种:简单类型和复合类型。

简单类型是诸如int,char,float,double等结构比较简单的数据格式。但是我们在处理一些复杂一些的问题时,仅仅依靠简单类型的变量定义是无法解决问题的,由此C++中又引入了一些结构稍微复杂一点的数据格式,这就是复合类型。而我们经常用到的数组、字符串、结构、枚举以及指针等都属于这一范畴。下面我们将分析这些复合类型的特点、用法及注意事项。

1.     数组

数组的声明与定义

数组是一种数据格式,能存储多个同类型的值(摘自《C++ Primer (第五版)中文版》)。数组是有长度的。创建一个新的数组可以使用声明语句,数组声明应指出一下三点:

(1)      数组的类型。如int、char、double等。

(2)      数组名。  关键字以外的字母或字母与数字或下划线的组合

(3)      数组长度

在C++中可以通过修改简单变量的声明,添加中括号(其中包含元素数目)来完成数组声明。例如:

int num[20];   //即是声明一个长度为20,数组名为num的整型数组。

数组的初始化

规则:只有在定义数组时才能使用初始化,并且不能将一个数组赋给另一个数组。

int stu[4] ={3,6,8,9};               //正确

int hand[4];                      //正确

hand[4] ={2,4,6,8};                //错误

hand = stu;                      //错误

如果只对数组的一部分进行初始化,则编译器将其他部分设置为0(初始化数组比较好的方法)。

 

 

 

2.     字符串

字符串是存储在内存的连续字节中的一系列字符(摘自《C++ Primer (第五版)中文版》)。这意味着可以将字符串存储在char数组中。在C语言中,字符串与数组的转化具有特殊性质,请看下面两个声明:

char dog [5] = {‘b’,’e’,’a’,’u’,’x’};      //非字符串

char cat[5]={‘f’,’a’,’t’,’s’,’\0’};        //字符串

也可以用下面的声明方式:

char bird[10] = “Mr.Cheeps”; 引号括起的字符串隐式的包含结尾的空字符。

char fish[] = “Bubbles”; 

字符串的长度

可以使用标准库函数strlen()来确定字符串的长度。strlen的参数只能是char*,并且必须是”\0”结尾的。例如:strlen(cat)。strlen()在头文件cstring (老式为string.h)中声明。与sizeof()的区别是,sizeof()获取的是数组占用的bytes,它的值只与在数组声明时的数组长度有关。

字符串的输入

面向行的输入1:cin.getline(数组名或指向数组的指针,读取输入的长度)。该函数在读取指定书目的字符或遇到换行符时停止读取,允许输入的字符串中含有空格。

面向行的输入2:cin.get(数组名或指向数组的指针,读取输入的长度)。与getline()的功能几乎完全一致,只是get()不再读取并丢弃换行符,而是将其留在输入队列中。这样的话只执行一次cin.get(name,size)不会报错,然而若程序未执行完毕,仍有cin.get(desert,size)操作,就会报错。如:

cin.get(name,size);

cin.get(desert,size);

由于第一次调用后,换行符将留在输入队列中,因此第二次调用时看到的第一个字符便是换行符’\n’。导致get()认为已经到达行尾,而没有发现任何可读取的内容。

使用不带任何参数的cin.get()调用可读取当前输入队列中的下一个字符(即使是换行符)。我们平时使用cin>>读取变量时,回车键生成的换行符留在了输入队列中。然而cin流有一个默认状态是为输入操作符(即“>>"操作符)跳过空白,所以默认情况下用输入操作符读取时会跳过回车符等空白字符,实际上它是在缓冲区中的,可以通过noskipws操纵符让输入操作符不跳过空白字符,这样就可以读取到'\n'等空白字符了。因此我们可以连续使用cin>>而不用担心读取到空白会换行、回车符等。但是混合输入数字和面向行的字符串会导致问题。

 

 

 

3.     String类

首先,要使用string类,必须在程序中包含头文件cstring(旧版本为string.h)。string类位于名称空间std中,因此在使用时必须提供一条using编译指令,或使用std::string来引用它。本人猜测:string头文件提供了string类的声明,std则提供了string类的实现方法。

string类的赋值、拼接和附加

stringstr1;         //定义string 对象

string str2=”test”;   //str2=”test”

str1=str2;          // 将str2赋给str1。赋值后,str1=”test”

string str3=”name”;

str1=str2+str3;   //str3的值加到str2的末尾,并将最后的结果赋给str1。str1=”testname”

使用到的函数

应使用strcpy_s(两个参数)、strcpy(两个参数)、strncpy(三个参数)和strncpy_s(三个参数),而不是使用赋值操作符“=”来将字符串赋给数组。

strcpy(charr1,charr2) ;           //将字符串charr2复制到字符数组charr1中

strcat(charr1,charr2);             //将字符串charr2附加到字符数组charr1中

str1.size();                      //获取str1的长度

将字符数组内容传给字符串变量可以直接使用赋值操作符“=”:

字符串变量=数组名;

strcpy/strncpy/strcpy_s的用法详见下面的链接

http://blog.csdn.net/caomiao2006/article/details/4766416

string类的输入与输出

C++中,我们直接用cin>>str1命令,可以读取一个单词,遇到空白则停止读入。因此当我们要将一行读取到string对象中时,需要利用如下代码:

getline(cin,str1);                 //此getline()为string类的一个友元函数。

输出时可以直接使用cout<<str1。

 

 

 

4.     结构简介

结构是一种比数组更灵活的数据格式,因为同一个结构可以存储多种类型的数据。

结构的定义和初始化:

Struct infat

{

char name[20];

float volume;

double price;

};          //分号不能少

上面的定义中,关键字struct表明,这些代码定义的是一个结构的布局。标识符infat是这种数据格式的名称。

infat inf;        //inf即为一个infat类型的对象

结构的初始化可以在定义对象时进行,如:

infat inf = {

“Glaria ruiq”,

1.88,

29.99

};     //分号不能少

定义结构数组:

infat   stu [100];

在定义时初始化:

infatguest[2] =

{

{ “Butawa”,0.5,21.99},

{“Godzilla”,2000,565.99},

};

 

5.     共用体

共用体(union)是一种数据格式,它能够存储不同类型的数据格式,但只能同时存储其中的一种类型。也就是说结构可以同时存储int、double和long,而共用体只能存储int、long或double。共用体的句法与结构相似,但含义不同。声明如下:

union one

{

intint_val;

longlong_val;

doubledouble_val;

};   //仍然有分号

可以使用one变量来存储int、long或double,条件是在不同的时间进行:

one pail;

pail.int_val= 15;  //存储整型变量

cout<<pail.int_val;

pail.double_val= 1.38;  //存储double变量,整型变量的值丢失

 

 

 

6.     枚举

C++的enum工具提供了另外一种创建符号常量的方式,这种方式可以代替const。

使用enum的句法如下:

enum spectrum{ zero,null=0,one,two=8,three};

spectrum 成为新类型的名称,被成为枚举。其中zero=0,null=0,one=1,two=8,three=9。zero在默认状态下被初始化为0,后面没有被初始化的枚举量的值将比前面的枚举量大1。在接下来的程序中,凡是出现zero被引用为int的地方,zero均被解析为0。

 

7.     指针和自由存储空间

声明和初始化指针

指针是一个变量,其存储的是值的地址,而不是值本身。指针是C++实现OPP特性的重要手段。指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。指针名表示的是地址,*操作符被称为间接值或解除引用操作符,将其应用于指针,可以得到该地址处存储的值。指针声明如下:

int*p;   //定义了一个指向int型变量的指针

int q=2;

int*w=&q;   //在定义时初始化指针

p=&q;   //令p指向q,此时*p=2

指针的危险

在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存。为数据提供空间是一个独立的步骤,不能忽略。例如:

long *fellow;     //定义一个long型指针

*fellow =2233;

fellow确实是一个指针,但是指向不明。上述代码没有将地址赋给fellow。我们不能确定2233会被放到哪里。由于fellow没有被初始化,它可能有任何值,不管值是什么,程序都将它解释为存储2233的地址。如果fellow的值碰巧为1200,计算机把数字存在地址1200上,而不管这个地址是否已被当前程序使用,这种错误会导致一些最隐匿、最难跟踪的bug。

切记:一定要在对指针应用解除引用操作符(*)之前,将指针初始化为一个确定的、适当的地址。

将数字值作为地址来使用,应通过强制类型转换将数字强制转换为适当的地址类型。

int *pt;

pt=(int*)0XB8000000;   //赋值有效,pt内存储的地址为0XB8000000

使用new来分配内存

int*p=new int;

*p=1000;

char *q;

上面第一行代码是分配内存空间,此时p指向一个分配好的地址,该地址没有被初始化,所以里面的是随机值。第二行代码是将1000赋给p指向的地址中的变量。

切记:当使用strcpyq,”strcpy”)将字符串”strcpy”拷贝到q之前,必须让q指针指向一个已分配好的内存地址。即是执行 q= new char [size] 语句。

程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456在常量区,p3在栈上。
static int c =0;全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}

如果计算机没有足够的内存而无法满足new的请求。在这种情况下,new将返回0。可以通过检查new是否返回的是空指针,从而防止程序超界。

使用delete来释放内存

delete释放内存实质是解除指针与存储空间的指向关系。在程序中需要配对的使用new和delete,否则会发生内存泄露。

另外,不可重复释放已经释放的内存块,一般在简单的程序中比较容易发现这个错误。然而当应用比较复杂时就很难发现了。例如,在使用C++的类A时,假设类A中定义有指针变量char *p,且在构造函数中动态分配p指向的内存大小

(p=new char [10]),当我们用已存在的A的对象 a 初始化一个A的新对象 b时,若没有显式的定义复制构造函数(深度拷贝函数),那么程序的主体执行不会出什么问题,但是当程序执行完毕需要调用析构函数时,就会出现问题,因为需要析构两个对象,但是这两个对象中指针p指向的内存会被释放两次,因此会出错,甚至导致程序的终止。

还有delete不仅能释放用new释放的内存,还能释放空指针。

使用new来创建动态数组

声明语句如下:

int *psome = new int [10];

*psome=5;            //psome[0]=5

psome指向数组的第一个元素,因此*psome是第一个元素的值。

使用指针访问数组有两种方式:

当使用int型数组和指针时:

第一:直接把指针名当数组名来使用。psome[2]是数组的第三个元素,&psome[2]是psome[2]在内存中的地址。

第二:使用*(psome+2)访问数组的第三个元素,(psome+2)即是第三个元素在内存中的地址。

注意一点,当使用psome=psome+sizeof(数据类型t)语句时,等价于:

int len =sizeof(psome指向的的数据类型 int) * sizeof(数据类型 t);  //t为已定义的任意数据类型

psome+=len;                                      //获得新的内存地址

还有一点,执行代码psome = psome +1;后,psome[0]是原数组的第二个元素。因为psome指向的初始位置发生了变化。

以上是使用数字型数组与指针时,遵守的规则。

当使用char数组和指针时:

charname[10] = “qweasd”;

char * p= name;

使用指针访问字符数组的方式:

Cout<<p[0]<<endl;    //得到的值为‘q’,

cout<<p<<endl;       //得到“qweasd”;

cout<<*P<<endl;      //得到q

cout<<(int*)p<<endl;  //得到p指向的数组的首地址,等价于printf("%X\n",(char *)p);

cout<<&p[2]<<endl;   //得到“easd”

cout<<&name<<endl;  //得到name的首地址

同时,使用*p[0]的形式是非法的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值