北京天海网站建设公司,泉州网站设计招聘网,公司法人查询系统,网站关键词选取的步骤和方法文章目录4.1 数组C11数组初始化的方法4.2 字符串**cin是如何确定已完成字符串输入呢#xff1f;****如何每次读取一行字符串输入#xff1f;****面向行的输入#xff1a;getline()****面向行的输入#xff1a;get( )****为什么推荐使用get( )#xff0c;而不是getline( )呢…
文章目录4.1 数组C11数组初始化的方法4.2 字符串**cin是如何确定已完成字符串输入呢****如何每次读取一行字符串输入****面向行的输入getline()****面向行的输入get( )****为什么推荐使用get( )而不是getline( )呢****如果读取到空行怎么办**4.3 stringstring类的拼接与复制如何计算字符串长度原始字符串4.4 结构体4.5共用体4.6枚举枚举值的取值范围4.7指针指针与自由存储空间指针的危险使用new来分配内存使用delete释放内存new关键字的意义使用new创建动态数组数组与指针指针小结指针与字符串自动存储静态存储与动态存储自动存储静态存储动态存储4.8数组的替代品vector:array:总结4.1 数组
数组array是一种数据格式能够存储多个同类型的值
要创建数组可使用声明语句。数组声明应指出以下三点
存储在每个元素中的值的类型数组名数组中的元素数。
typeName arrayName[arraySize];
arraySize不能是变量变量的值是在程序运行时设置的。稍后将介绍如何使用new运算符来避开这种闲置
只有在定义数组时才能使用初始化此后就不能使用了也不能将一个数组赋给另一个数组
int card[4] {3,6,8,10};//okay
int hand[4];
hand[4] {5,6,7,8};//not allowed
hand cards;//not allowed初始化数组时提供的值可以少于数组的元素数目编译器会将剩下的全部赋值为零
C11数组初始化的方法
首先初始化数组时可省略等号
double k[4] {1.0,2.0};其次可不在大括号内包含任何东西这将把所有元素都设置为零
double k[4]{};第三列表初始化禁止缩窄转换
4.2 字符串
char dog[8] {b,e,a,u,x, ,I,I};
char cat[8] {b,e,a,u,x,I,I,\0};这两个数组都是char数组但只有第二个数组是字符串
在cat数组示例中将数组初始化为字符串的工作看上去冗长乏味——使用大量单引号且必须记住加上空字符。不必担心有一种更好的、将字符数组初始化为字符串的方法—只需使用一个用引号括起的字符串即可这种字符串被称为字符串常量string constant或字符串字面值string literal如下所示
char bird[11] Mr.Cheeps;
char fish[] Bubbles;用引号括起的字符串隐式地包括结尾的空字符因此不用显式地包括它
char sh S;//字符常量如S是字符串编码的简写表示,该语句将83赋给sh
char sh S;//“S表示的是字符S和\0组成的字符串事实上”S表示的是该字符串所在的内存地址。因此这条语句实际上试图将一个内存地址赋值为sh.但是由于地址在C中是一种独立的类型因此编译器不允许这种不合理的做法有时候字符串很长无法放到一行中。C允许拼接字符串字面值即将两个用引号括起的字符串合并为一个。事实上任何两个由空白空格、制表符和换行符分隔的字符串常量都将自动拼接成一个。 因此下面所有的输出语句都是等效的
coutI had give my right arm to be a great violinist.endl;
coutI had give my right arm to be a great violinist.endl;
coutI had give my right ar
m to be a great violinist.endl;注意拼接时不会在被连接的字符串之间添加空格第二个字符串的第一个字符将紧跟在第一个字符串的最后一个字符不考虑\0后面。第一个字符串中的\0字符将被第二个字符串的第一个字符取代。
sizeof运算符指出整个数组的长度15字节但strlen( )函数返回的是存储在数组中的字符串的长度而不是数组本身的长度。另外strlen( )只计算可见的字符而不把空字符计算在内。
cin是如何确定已完成字符串输入呢
由于不能通过键盘输入空字符因此cin需要用别的方法来确定字符串的结尾位置。cin使用空白空格、制表符和换行符来确定字符串的结束位置这意味着cin在获取字符数组输入时只读取一个单词。读取该单词后cin将该字符串放到数组中并自动在结尾添加空字符。
如何每次读取一行字符串输入
每次读取一个单词通常不是最好的选择。例如假设程序要求用户输入城市名用户输入New York。您希望程序读取并存储完整的城市名而不仅仅是New或Sao。要将整条短语而不是一个单词作为字符串输入需要采用另一种字符串读取方法。具体地说需要采用面向行而不是面向单词的方法。
幸运的是istream中的类如cin提供了一些面向行的类成员函数getline( )和get( )。这两个函数都读取一行输入直到到达换行符。然而随后getline( )将丢弃换行符而get( )将换行符保留在输入序列中。 面向行的输入getline()
getline( )函数读取整行它使用通过回车键输入的换行符来确定输入结尾。要调用这种方法可以使用cin.getline( )。该函数有两个参数。第一个参数是用来存储输入行的数组的名称第二个参数是要读取的字符数。如果这个参数为20则函数最多读取19个字符余下的空间用于存储自动在结尾处添加的空字符。getline( )成员函数在读取指定数目的字符或遇到换行符时停止读取。
cin.getline(charr,20)这种句点表示法表明函数getline( )是istream类的一个类方法还记得吗cin是一个istream对象。正如前面指出的第一个参数是目标数组第二个参数数组长度getline( )使用它来避免超越数组的边界。
下面是将一行输入读取到string对象中的代码
getline(cin,str);那么为何一个getline( )是istream的类方法而另一个不是呢
在引入string类之前很久C就有istream类。因此istream的设计考虑到了诸如double和int等基本C数据类型但没有考虑string类型所以istream类中有处理double、int和其他基本类型的类方法但没有处理string对象的类方法。 面向行的输入get( )
istream类有另一个名为get( )的成员函数该函数有几种变体。其中一种变体的工作方式与getline( )类似它们接 受的参数相同解释参数的方式也相同并且都读取到行尾。但get并不再读取并丢弃换行符而是将其留在输入队列中。假设我们连续两次调用get( )
cin.get(name,ArSize);
cin.get(dessert,ArSize);由于第一次调用后换行符将留在输入队列中因此第二次调用时看到的第一个字符便是换行符。因此get( )认为已到达行尾而没有发现任何可读取的内容。如果不借助于帮助get( )将不能跨过该换行符。
幸运的是get( )有另一种变体。使用不带任何参数的cin.get( )调用可读取下一个字符即使是换行符因此可以用它来处理换行符为读取下一行输入做好准备。也就是说可以采用下面的调用序列
cin.get(name,ArSize);
cin.get();
cin.get(dessert,ArSize);另一种使用get( )的方式是将两个类成员函数拼接起来合并如下所示
cin.get(name,ArSize).get();之所以可以这样做是由于cin.getnameArSize返回一个cin对象该对象随后将被用来调用get( )函数。同样下面的语句将把输入中连续的两行分别读入到数组name1和name2 中其效果与两次调用cin.getline( )相同
cin.getline(name1,ArSize).getline(name2,ArSize);为什么推荐使用get( )而不是getline( )呢
首先老式实现没有getline( )。
其次get( )使输入更仔细。例如假设用get( )将一行读入数组中。如何知道停止读取的原因是由于已经读取了整行而不是由于数组已填满呢查看下一个输入字符如果是换行符说明已读取了整行否则说明该行中还有其他输入。
如果读取到空行怎么办
当getline( )或get( )读取空行时将发生什么情况最初的做法是下一条输入语句将在前一条getline( )或get( )结束读取的位置开始读取但当前的做法是当get( )不是getline( )读取空行后将设置失效位failbit。这意味着接下来的输入将被阻断但可以用下面的命令来恢复输入
cin.cleanr();另一个潜在的问题是输入字符串可能比分配的空间长。如果输入行包含的字符数比指定的多则getline( )和get( )将把余下的字符留在输入队列中而getline( )还会设置失效位并关闭后面的输入。
4.3 string
string类的拼接与复制
strcpy(charr1,charr2);//将charr2的内容复制到charr1里面
strcat(charr1,charr2);//将charr2拼接到charr1的后面
str3 str1 str2;如何计算字符串长度
str1.size();//类成员函数
// 函数strlen( )从数组的第一个元素开始计算字节数直到遇到空字符。在这个例子中在数组末尾的几个字节后才遇到
// 空字符。对于未被初始化的数据第一个空字符的出现位置是随机的因此您在运行该程序时得到的数组长度很可能与此不同。
strlen(str1);//普通函数传入类对象原始字符串
C11新增的另一种类型是原始raw字符串。在原始字符串中字符表示的就是自己例如序列\n不表示换行符而表示两个常规字符—斜杠和n因此在屏幕上显示时将显示这两个字符。另一个例子是可在字符串中使用而无需像程序清单4.8中那样使用繁琐的。当然既然可在字符串字面量包含就不能再使用它来表示字符串的开头和末尾。因此原始字符串将(和)用作定界符并使用前缀R来标识原始字符串
coutRJimKing)endl;如果要在原始字符串中包含)该如何办呢
编译器见到第一个)时会不会认为字符串到此结束会的。
但原始字符串语法允许您在表示字符串开头的和(之间添加其他字符这意味着表示字符串结尾的和)之间也必须包含这些字符。因此使用R*(标识原始字符串的开头时必须使用)*标识原始字符串的结尾。
coutR*((who is he?),she said.)*endl;因此上面的语句 “(who is he?)”,she said. 4.4 结构体
与C结构struct不同C结构除了成员变量之外还可以有成员函数。
结构中的位字段
与C语言一样C也允许指定占用特定位数的结构成员这使得创建与某个硬件设备上的寄存器对应的数据结构非常方便。字段的类型应为整型或枚举稍后将介绍接下来是冒号冒号后面是一个数字它指定了使用的位数。可以使用没有名称的字段来提供间距。每个成员都被称为位字段bit field。下面是一个例子
struct torgle{unsigned int SN : 4;unsigned int : 4;bool goodin : 1;bool goodTorgle : 1;
}位字段通常用在低级编程中。此部分只做了解即可
4.5共用体
共用体union是一种数据格式它能够存储不同的数据类型但只能同时存储其中的一种类型。也就是说结构可以同时存储int、long和double共用体只能存储int、long或double。共用体的句法与结构相似但含义不同。例如请看下面的声明
union one4all{int int_val;long long_val;double double_val;
}可以使用one4all变量来存储int、long或double条件是在不同的时间进行
one4all pail;
pail.int_val 15;//store an int
coutpail.int_val;
pail.double_val 1.38;//store a double,int value is lost;
coutpail.double_val;因此pail有时可以是int变量而有时又可以是double变量。成员名称标识了变量的容量。由于共用体每次只能存储一个值因此它必须有足够的空间来存储最大的成员所以共用体的长度为其最大成员的长度。
共用体的用途之一是当数据项使用两种或更多种格式但不会同时使用时可节省空间。
例如假设管理一个小商品目录其中有一些商品的ID为整数而另一些的ID为字符串。在这种情况下可以这样做
struct widget{char brand[20];int type;union id{long id_num;char id_char[20];}id_val;
}匿名共用体anonymous union没有名称其成员将成为位于相同地址处的变量。显然每次只有一个成员是当前的成员
struct widget{char brand[20];int type;union {long id_num;char id_char[20];};
};由于共用体是匿名的因此id_num和id_char被视为prize的两个成员它们的地址相同所以不需要中间标识符id_val。程序员负责确定当前哪个成员是活动的。
共用体常用于但并非只能用于节省内存。当前系统的内存多达数GB甚至数TB好像没有必要节省内存但并非所有的C程序都是为这样的系统编写的。C还用于嵌入式系统编程如控制烤箱、MP3播放器或火星漫步者的处理器。对这些应用程序来说内存可能非常宝贵。另外共用体常用于操作系统数据结构或硬件数据结构。
4.6枚举
C的enum工具提供了另一种创建符号常量的方式这种方式可以代替const。使用enum的句法与使用结构相似。例如请看下面的语句
enum spectrum{red,orange,yellow,green,blue,violet,indigo,ultraviolet};spectrum band;
band blue;//valid;
band 2000;//invalid;//spectrum变量受到限制只有8个可能的值为获得最大限度的可移植性应将把非enum值赋给enum变量视为错误。这条语句完成两项工作。
让spectrum成为新类型的名称spectrum被称为枚举enumeration就像struct变量被称为结构一样。将red、orange、yellow等作为符号常量它们对应整数值07。这些常量叫作枚举量enumerator。
在默认情况下将整数值赋给枚举量第一个枚举量的值为0第二个枚举量的值为1依次类推。可以通过显式地指定整数值来覆盖默认值。
对于枚举只定义了赋值运算符。具体地说没有为枚举定义算术运算.
枚举量是整型可被提升为int类型但int类型不能自动转换为枚举类型:
int color blue;
band 3;//不合法
color 3red;
band orangered;//非法没有为枚举定义运算符但用于算术表达式中时被视为10但是类型为int因此不能赋值。
band spectrum(3);//合法设置枚举量的值
可以使用赋值运算符来显式地设置枚举量的值
enum bits{one 1 , two 2, four 4, eight 8};
enum bigstep{first, second 100,thired};//0,100,101
enum {zero,null 0,one,numero_uno 1};//可以创建多个值相同的枚举量其中zero和null都为0one和umero_uno都为1。在C早期的版本中只能将int值或提升为int的值赋给枚举量但这种限制取消了因此可以使用long甚至long long类型的值。
枚举值的取值范围
最初对于枚举来说只有声明中指出的那些值是有效的。然而C现在通过强制类型转换增加了可赋给枚举变量的合法值。每个枚举都有取值范围range通过强制类型转换可以将取值范围中的任何整数值赋给枚举变量即使这个值不是枚举值。例如假设bits和myflag的定义如下
enum bits{one 1, two 2,four 4,eight 8};
bits myflag;
//则下面的代码是合法的
myflag bits(6);//其中6不是枚举值但它位于枚举定义的取值范围内。取值范围的定义如下。首先要找出上限需要知道枚举量的最大值。找到大于这个最大值的、最小的2的幂将它减去1得到的便是取值范围的上限。例如前面定义的bigstep的最大值枚举值是101。在2的幂中比这个数大的最小值为128因此取值范围的上限为127。要计算下限需要知道枚举量的最小值。如果它不小于0则取值范围的下限为0否则采用与寻找上限方式相同的方式但加上负号。例如如果最小的枚举量为−6而比它小的、最大的2的幂是−8加上负号因此下限为−7。
4.7指针
指针与自由存储空间
计算机程序在存储数据时必须跟踪的3种基本属性。为了方便这里再次列出了这些属性
信息存储在何处存储的值是多少存储的信息是什么类型
您使用过一种策略来达到上述目的定义一个简单变量。声明语句指出了值的类型和符号名还让程序为值分配内存并在内部跟踪该内存单元。下面来看一看另一种策略它在开发C类时非常重要。这种策略以指针为基础指针是一个变量其存储的是值的地址而不是值本身。
在讨论指针之前我们先看一看如何找到常规变量的地址。只需对变量应用地址运算符就可以获得它的位置例如如果home是一个变量则home是它的地址。 指针与C基本原理 面向对象编程与传统的过程性编程的区别在于OOP强调的是在运行阶段而不是编译阶段进行决策。运行阶段指的是程序正在运行时编译阶段指的是编译器将程序组合起来时。运行阶段决策就好比度假时选择参观哪些景点取决于天气和当时的心情而编译阶段决策更像不管在什么条件下都坚持预先设定的日程安排。 运行阶段决策提供了灵活性可以根据当时的情况进行调整。例如考虑为数组分配内存的情况。传统的方法是声明一个数组。要在C中声明数组必须指定数组的长度。因此数组长度在程序编译时就设定好了这就是编译阶段决策。您可能认为在80%的情况下一个包含20个元素的数组足够了但程序有时需要处理200个元素。为了安全起见使用了一个包含200个元素的数组。这样程序在大多数情况下都浪费了内存。OOP通过将这样的决策推迟到运行阶段进行使程序更灵活。在程序运行后可以这次告诉它只需要20个元素而还可以下次告诉它需要205个元素。总之使用OOP时您可能在运行阶段确定数组的长度。为使用这种方法语言必须允 许在程序运行时创建数组。稍后您看会到C采用的方法是使用关键字new请求正确数量的内存以及使用指针来跟踪新分配的内存的位置。在运行阶段做决策并非OOP独有的但使用C编写这样的代码比使用C语言简单。 处理存储数据的新策略刚好相反将地址视为指定的量而将值视为派生量。一种特殊类型的变量—指针用于存储值的地址。因此指针名表示的是地址。*运算符被称为间接值indirect velue或解除引用dereferencing运算符将其应用于指针可以得到该地址处存储的值。
//如何去声明一个指针变量int *P_updates;
-
//这表明 *P_updates 的类型为int由于*运算符被用于指针因此p_updates变量本身必须是指针。我们说p_updates指向int类型我们还说p_updates的类型是指向int的指针或int*。可以这样说p_updates是指针地址而*p_updates是int而不是指针int *ptr;//这强调的是*ptr是一个int类型的值。
int* ptr;//这强调的是int*是一种类型——指向int的指针。//事实上在哪里添加空格对于编译器来讲是没有区别的。int *p1,p2;//表示声明创建一个指针p1和一个int变量p2;int*是一种复合类型。double * tax_ptr;
char * str;尽管它们都是指针却是不同类型的指针。和数组一样指针都是基于其他类型的。
虽然tax_ptr和str指向两种长度不同的数据类型但这两个变量本身的长度通常是相同的。也就是说char的地址与double的地址的长度相同这就好比1016可能是超市的街道地址而1024可以是小村庄的街道地址一样。地址的长度或值既不能指示关于变量的长度或类型的任何信息也不能指示该地址上有什么建筑物。一般来说地址需要2个还是4个字节取决于计算机系统有些系统可能需要更大的地址系统可以针对不同的类型使用不同长度的地址。
指针的危险
在C中创建指针时计算机将分配用来存储地址的内存但不会分配用来存储指针所指向的数据的内存。为数据提供空间是一个独立的步骤忽略这一步无疑是自找麻烦如下所示
long * fellow;
*fellow 223333;fellow确实是一个指针但它指向哪里呢上述代码没有将地址赋给fellow。那么223323将被放在哪里呢我们不知道。由于fellow没有被初始化它可能有任何值。不管值是什么程序都将它解释为存储223323的地址。如果fellow的值碰巧为1200计算机将把数据放在地址1200上即使这恰巧是程序代码的地址。fellow指向的地方很可能并不是所要存储223323的地方。这种错误可能会导致一些最隐匿、最难以跟踪的bug。 一定要在对指针应用解除引用运算符*之前将指针初始化为一个确定的、适当的地址。
使用new来分配内存
程序员要告诉new需要为哪种数据类型分配内存new将找到一个长度正确的内存块并返回该内存块的地址。程序员的责任是将该地址赋给一个指针。下面是一个这样的示例
int *pn new int;//方法一
typename * pointer_name new typeName;int higgens;
int * pt higgens;//方案二new int告诉程序需要适合存储int的内存。new运算符根据类型来确定需要多少字节的内存。然后它找到这样的内存并返回其地址。接下来将地址赋给pnpn是被声明为指向int的指针。现在pn是地址而*pn是存储在那里的值。
地址本身只指出了对象存储地址的开始而没有指出其类型使用的字节数。
对于指针需要指出的另一点是new分配的内存块通常与常规变量声明分配的内存块不同。变量nights和pd的值都存储在被称为栈stack的内存区域中而new从被称为堆heap或自由存储区free store的内存区域分配内存。
在C中值为0的指针被称为空指针null pointer。C确保空指针不会指向有效的数据因此它常被用来表示运算符或函数失败如果成功它们将返回一个有用的指针。
使用delete释放内存
delete运算符它使得在使用完内存后能够将其归还给内存池这是通向最有效地使用内存的关键一 步。归还或释放free的内存可供程序的其他部分使用。使用delete时后面要加上指向内存块的指针这些内存块最初是用new分配的
int *ps new int;
delete ps;这将释放ps指向的内存但不会删除指针ps本身。可以将ps重新指向另一个新分配的内存块。一定要配对地使用new和delete否则将发生内存泄漏。也就是说被分配的内存再也无法使用了。但是不要重复释放内存。
注意使用delete的关键在于将它用于new分配的内存。这并不意味着要使用用于new的指针而是用于new的地址
int *ps new int;//开辟内存
int *pq ps;//设置第二个指针指向同一块内存
delete pq;//释放该内存一般来说不要创建两个指向同一个内存块的指针因为这将增加错误地删除同一个内存块两次的可能性。但稍后您会看到对于返回指针的函数使用另一个指针确实有道理。
new关键字的意义
通常对于大型数据如数组、字符串和结构应使用new这正是new的用武之地。例如假设要编写一个程 序它是否需要数组取决于运行时用户提供的信息。如果通过声明来创建数组则在程序被编译时将为它分配内存空间。不管程序最终是否使用数组数组都在那里它占用了内存。在编译时给数组分配内存被称为静态联编static binding意味着数组是在编译时加入到程序中的。但使用new时如果在运行阶段需要数组则创建它如果不需要则不创建。还可以在程序运行时选择数组的长度。这被称为动态联编dynamic binding意味着数组是在程序运行时创建的。这种数组叫作动态数组dynamic array。使用静态联编时必须在编写程序时指定数组的长度使用动态联编时程序将在运行时确定数组的长度。
使用new创建动态数组
int *psome new int [10]type_name * pointer_name new type_name [num_elements];//如何跟踪这10个元素
//完全可以将psome当做数组名 psome[0],psome[1]new运算符返回第一个元素的地址。当程序使用完new分配的内存块时应使用delete释放它们。然而对于用new创建的数组而言应当这样释放
delete [] psome;方括号告诉数组应当释放整个数组而不仅仅是指针指向的元素。 请注意delete和指针之间的方括号。如果使用new时不带方括号则使用delete时也不应带方括号。如果使用new时带方括号则使用delete时也应带方括号。 int *pt new int;
short * ps new short [500];
delete []pt;
delete ps;总之使用new和delete时应遵守以下规则
不要使用delete来释放不是new分配的内存。不要使用delete释放同一个内存块两次。如果使用new [ ]为数组分配内存则应使用delete [ ]来释放。如果使用new [ ]为一个实体分配内存则应使用delete没有方括号来释放。对空指针应用delete是安全的。
数组与指针
#include iostream
#include climits
using namespace std;int main() {int *a new int[3];a[0] 3;a[1] 1;a[2] 2;cout 原来的数组是 endl;cout a[0] a[1] a[2] endl;cout 偏移后的数组是 endl;a a 1;cout a[0] a[1] a[2] endl;cout 二次偏移后的数组是 endl;a ;cout a[0] a[1] a[2] endl;return 0;
}输出如下
原来的数组是3 1 2
偏移后的数组是1 2 0
二次偏移后的数组是2 0 2119321413将整数变量加1后其值将增加1但将指针变量加1后增加的量等于它指向的类型的字节数。将指向double的指针加1后如果系统对double使用8个字节存储则数值将增加8将指向short的指针加1后如果系统对short使用2个字节存储则指针值将增加2。
将指针变量加1后其增加的值等于指向的类型占用的字节数。
stacks[1] 相当于 *shacks1)区别一可以修改指针的值而数组名是常量区别二对数组应用sizeof运算符得到的是数组的长度而对指针应用sizeof得到的是指针的长度即使指针指向的是一个数组数组的地址
对数组取地址时数组名也不会被解释为其地址。等等数组名难道不被解释为数组的地址吗
不完全如此数组名被解释为其第一个元素的地址而对数组名应用地址运算符时得到的是整个数组的地址
short tell[10];
couttellendl;//tell[0]
couttellendl;从数字上说这两个地址相同但从概念上说tell[0]即tell是一个2字节内存块的地址而tell是一个20字节内存块的地址。因此表达式tell 1将地址值加2而表达式tell2将地址加20。换句话说tell是一个short指针* short而tell是一个这样的指针即指向包含20个元素的short数组short (*) [20].
前面有关tell的类型描述是如何来的呢首先您可以这样声明和初始化这种指针
short (*pas)[20] tell;如果省略括号优先级规则将使得pas先与[20]结合导致pas是一个short指针数组它包含20个元素因此括号是必不可少的。其次如果要描述变量的类型可将声明中的变量名删除。因此pas的类型为short () [20]。另外由于pas被设置为tell因此pas与tell等价所以(*pas) [0]为tell数组的第一个元素。 指针小结
声明
typename * pointerName;给指针赋值应将内存地址赋给指针
double * pn;对指针解除引用*运算符区分指针和指针所指向的值如果pt是指向int的指针则*pt不是指向int的指针而是完全等同于一个int类型的变量。pt才是指针。数组名在多数情况下C将数组名视为数组的第一个元素的地址。指针算术C允许将指针和整数相加。加1的结果等于原来的地址值加上指向的对象占用的总字节数。还可以将一个指针减去另一个指针获得两个指针的差。后一种运算将得到一个整数仅当两个指针指向同一个数 组也可以指向超出结尾的一个位置时这种运算才有意义这将得到两个元素的间隔。数组的动态联编和静态联编使用数组声明来创建数组时将采用静态联编即数组的长度在编译时设置使用new[]运算符创建数组时将采用动态联编即将在运行时为数组分配空间其长度也在运行时设置。使用完这种数组之后应使用delete[]释放其占用的内存。数组表示法和指针表示法stacks[1] 相当于 *shacks1)
指针与字符串
char flower[10] rose;
coutflowes are redendl;数组名是第一个元素的地址因此cout语句中的flower是包含字符r的char元素的地址。cout对象认为char的地址是字符串的地址因此它打印该地址处的字符然后继续打印后面的字符直到遇到空字符\0为止。总之如果给cout提供一个字符的地址则它将从该字符开始打印直到遇到空字符为止。
这里的关键不在于flower是数组名而在于flower是一个char的地址。这意味着可以将指向char的指针变量作为cout的参数因为它也是char的地址。当然该指针指向字符串的开头稍后将核实这一点。
前面的cout语句中最后一部分的情况如何呢如果flower是字符串第一个字符的地址则表达式“s are red\n”是什么呢为了与cout对字符串输出的处理保持一致这个用引号括起的字符串也应当是一个地址。在C中用引号括起的字符串像数组名一样也是第一个元素的地址。上述代码不会将整个字符串发送给cout而只是发送该字符串的地址。这意味着对于数组中的字符串、用引号括起的字符串常量以及指针所描述的字符串处理的方式是一样的都将传递它们的地址。与逐个传递字符串中的所有字符相比这样做的工作量确实要少。 在cout和多数C表达式中char数组名、char指针以及用引号括起的字符串常量都被解释为字符串第一个字符的地址。 一般来说如果给cout提供一个指针它将打印地址。但如果指针的类型为char *则cout将显示指向的字符串.
ps new char[strlen(animal)1];//一种分配空间的好方法char food[20] carrots;
strcpy(food,flan);
strcpy(food.a picnic basket filled with many goodies)在这种情况下函数将字符串中剩余的部分复制到数组后面的内存字节中这可能会覆盖程序正在使用的其他内存。要避免这种问题请使用strncpy( )。该函数还接受第3个参数—要复制的最大字符数。然而要注意的是如果该函数在到达字符串结尾之前目标内存已经用完则它将不会添加空字符。因此应该这样使用该函数
strncpy(food.a picnic basket filled with many goodies,19);
food[19] \0;这样最多将19个字符复制到数组中然后将最后一个元素设置成空字符。如果该字符串少于19个字符则strncpy( )将在复制完该字符串之后加上空字符以标记该字符串的结尾。 有时C新手在指定结构成员时搞不清楚何时应使用句点运算符何时应使用箭头运算符。规则非常简单。如果结构标识符是结构名则使用句点运算符如果标识符是指向结构的指针则使用箭头运算符。 自动存储静态存储与动态存储
#include iostream
#include cstring
using namespace std;
char *getname();int main() {char *name;name getname();cout name at (int *)name endl;delete [] name;
}char *getname() {char temp[80];cout Enter your name:;cin temp;char *pn new char[strlen(temp)];strcpy(pn, temp);return pn;
}根据用于分配内存的方法C有3种管理数据内存的方式自动存储、静态存储和动态存储有时也叫作自由存储空间或堆。在存在时间的长短方面以这3种方式分配的数据对象各不相同。下面简要地介绍每种类型C11新增了第四种类型—线程存储。
自动存储
在函数内部定义的常规变量使用自动存储空间被称为自动变量automatic variable这意味着它们在所属的函数被调用时自动产生在该函数结束时消亡。例如程序清单4.22中的temp数组仅当getname( )函数活动时存在。当程序控制权回到main( )时temp使用的内存将自动被释放。如果getname( )返回temp的地址则main( )中的name指针指向的内存将很快得到重新使用。这就是在getname( )中使用new的原因之一。
实际上自动变量是一个局部变量其作用域为包含它的代码块。代码块是被包含在花括号中的一段代码。到目前为止我们使用的所有代码块都是整个函数。然而在下一章将会看到函数内也可以有代码块。如果在其中的某个代码块定义了一个变量则该变量仅在程序执行该代码块中的代码时存在。自动变量通常存储在栈中。这意味着执行代码块时其中的变量将依次加入到栈中而在离开代码块时将按相反的顺序释放这些变量这被称为后进先出LIFO。因此在程序执行过程中栈将不断地增大和缩小。
静态存储
静态存储是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种
一种是在函数外面定义它另一种是在声明变量时使用关键字static
自动存储和静态存储的关键在于这些方法严格地限制了变量的寿命。变量可能存在于程序的整个生命周期静态变量也可能只是在特定函数被执行时存在自动变量。
动态存储
new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池这在C中被称为自由存储空间free store或堆heap。该内存池同用于静态变量和自动变量的内存是分开的。上述表明new和delete让您能够在一个函数中分配内存而在另一个函数中释放它。因此数据的生命周期不完全受程序或函数的生存时间控制。与使用常规变量相比使用new和delete让程序员对程序如何使用内存有更大的控制权。然而内存管理也更复杂了。在栈中自动添加和删除机制使得占用的内存总是连续的但new和delete的相互影响可能导致占用的自由存储区不连续这使得跟踪新分配内存的位置更困难。
栈、堆和内存泄漏 如果使用new运算符在自由存储空间或堆上创建变量后没有调用delete将发生什么情况呢 如果没有调用delete则即使包含指针的内存由于作用域规则和对象生命周期的原因而被释放在自由存储空间上动态分配的变量或结构也将继续存在。实际上将会无法访问自由存储空间中的结构因为指向这些内存的指针无效。这将导致内存泄漏。 被泄漏的内存将在程序的整个生命周期内都不可使用这些内存被分配出去但无法收回。极端情况不过不常见是内存泄漏可能会非常严重以致于应用程序可用的内存被耗尽出现内存耗尽错误导致程序崩溃。另外这种泄漏还会给一些操作系统或在相同的内存空间中运行的应用程序带来负面影响导致它们崩溃。 即使是最好的程序员和软件公司也可能导致内存泄漏。要避免内存泄漏最好是养成 这样一种习惯即同时使用new和delete运算符在自由存储空间上动态分配内存随后便释 放它。C智能指针有助于自动完成这种任务。 4.8数组的替代品
vector:
vectortypeName vt(n_elem);array:
arraytypeName,n_elem arr;a1[-2] 20.2; 相当于 *(a1 - 2) 20.2这表示找到a1指向的地方向前移两个double元素并将20.2存储到目的地。也就是说将信息存储到数组的外面。与C语言一样C也不检查这种超界错误。所以说数组是不安全的。
vector和array对象能够禁止这种行为吗不能。
但是如果使用成员函数at(),就能够在运行期间捕获非法索引而程序默认中断。
总结
数组、结构和指针是C的3种复合类型。数组可以在一个数据对象中存储多个同种类型的值。通过使用索引或下标可以访问数组中各个元素。结构可以将多个不同类型的值存储在同一个数据对象中可以使用成员关系运算符.来访问其中的成员。使用结构的第一步是创建结构模板它定义结构存储了哪些成员。模板的名称将成为新类型的标识符然后就可以声明这种类型的结构变量。共用体可以存储一个值但是这个值可以是不同的类型成员名指出了使用的模式。指针是被设计用来存储地址的变量。我们说指针指向它存储的地址。指针声明指出了指针指向的对象的类型。对指针应用解除引用运算符将得到指针指向的位置中的值。字符串是以空字符为结尾的一系列字符。字符串可用引号括起的字符串常量表示其中隐式包含了结尾的空字符。可以将字符串存储在char数组中可以用被初始化为指向字符串的char指针表示字符串。函数strlen( )返回字符串的长度其中不包括空字符。函数strcpy( )将字符串从一个位置复制到另一个位置。在使用这些函数时应当包含头文件cstring或string.h。头文件string支持的C string类提供了另一种对用户更友好的字符串处理方法。具体地说string对象将根据要存储的字符串自动调整其大小用户可以使用赋值运算符来复制字符串。new运算符允许在程序运行时为数据对象请求内存。该运算符返回获得内存的地址可以将这个地址赋给一个指针程序将只能使用该指针来访问这块内存。如果数据对象是简单变量则可以使用解除引用运算符*来获得其值如果数据对象是数组则可以像使用数组名那样使用指针来访问元素如果数据对象是结构则可以用指针解除引用运算符-来访问其成员。指针和数组紧密相关。如果ar是数组名则表达式ar[i]被解释为*ar i其中数组名被解释为数组第一个元素的地址。这样数组名的作用和指针相同。反过来可以使用数组表示法通过指针名来访问new分配的数组中的元素。运算符new和delete允许显式控制何时给数据对象分配内存何时将内存归还给内存池。自动变量是在函数中声明的变量而静态变量是在函数外部或者使用关键字static声明的变量这两种变量都不太灵活。自动变量在程序执行到其所属的代码块通常是函数定义时产生在离开该代码块时终止。静态变量在整个程序周期内都存在。 文章转载自: http://www.morning.thzwj.cn.gov.cn.thzwj.cn http://www.morning.skbbt.cn.gov.cn.skbbt.cn http://www.morning.drspc.cn.gov.cn.drspc.cn http://www.morning.bxgpy.cn.gov.cn.bxgpy.cn http://www.morning.clccg.cn.gov.cn.clccg.cn http://www.morning.rwdbz.cn.gov.cn.rwdbz.cn http://www.morning.drspc.cn.gov.cn.drspc.cn http://www.morning.pcrzf.cn.gov.cn.pcrzf.cn http://www.morning.tmsxn.cn.gov.cn.tmsxn.cn http://www.morning.zwsgl.cn.gov.cn.zwsgl.cn http://www.morning.sdamsm.com.gov.cn.sdamsm.com http://www.morning.dbphz.cn.gov.cn.dbphz.cn http://www.morning.ngcbd.cn.gov.cn.ngcbd.cn http://www.morning.prgnp.cn.gov.cn.prgnp.cn http://www.morning.wqhlj.cn.gov.cn.wqhlj.cn http://www.morning.tddrh.cn.gov.cn.tddrh.cn http://www.morning.zsthg.cn.gov.cn.zsthg.cn http://www.morning.kpbq.cn.gov.cn.kpbq.cn http://www.morning.rdxp.cn.gov.cn.rdxp.cn http://www.morning.zbkwj.cn.gov.cn.zbkwj.cn http://www.morning.dglszn.com.gov.cn.dglszn.com http://www.morning.mkhwx.cn.gov.cn.mkhwx.cn http://www.morning.pqrhb.cn.gov.cn.pqrhb.cn http://www.morning.gnhsg.cn.gov.cn.gnhsg.cn http://www.morning.ntcmrn.cn.gov.cn.ntcmrn.cn http://www.morning.hxrfb.cn.gov.cn.hxrfb.cn http://www.morning.drnfc.cn.gov.cn.drnfc.cn http://www.morning.hzqjgas.com.gov.cn.hzqjgas.com http://www.morning.wschl.cn.gov.cn.wschl.cn http://www.morning.bswxt.cn.gov.cn.bswxt.cn http://www.morning.lbrrn.cn.gov.cn.lbrrn.cn http://www.morning.pqqzd.cn.gov.cn.pqqzd.cn http://www.morning.kfysh.com.gov.cn.kfysh.com http://www.morning.hlxxl.cn.gov.cn.hlxxl.cn http://www.morning.nkhdt.cn.gov.cn.nkhdt.cn http://www.morning.qgmbx.cn.gov.cn.qgmbx.cn http://www.morning.rwjh.cn.gov.cn.rwjh.cn http://www.morning.mgtmm.cn.gov.cn.mgtmm.cn http://www.morning.xqspn.cn.gov.cn.xqspn.cn http://www.morning.glrzr.cn.gov.cn.glrzr.cn http://www.morning.yckwt.cn.gov.cn.yckwt.cn http://www.morning.bmlcy.cn.gov.cn.bmlcy.cn http://www.morning.whpsl.cn.gov.cn.whpsl.cn http://www.morning.shnqh.cn.gov.cn.shnqh.cn http://www.morning.hlfgm.cn.gov.cn.hlfgm.cn http://www.morning.gcjhh.cn.gov.cn.gcjhh.cn http://www.morning.xysxj.com.gov.cn.xysxj.com http://www.morning.ydmml.cn.gov.cn.ydmml.cn http://www.morning.rxkl.cn.gov.cn.rxkl.cn http://www.morning.ybhjs.cn.gov.cn.ybhjs.cn http://www.morning.zdhxm.com.gov.cn.zdhxm.com http://www.morning.pljxz.cn.gov.cn.pljxz.cn http://www.morning.knlbg.cn.gov.cn.knlbg.cn http://www.morning.gcdzp.cn.gov.cn.gcdzp.cn http://www.morning.mlgsc.com.gov.cn.mlgsc.com http://www.morning.hpggl.cn.gov.cn.hpggl.cn http://www.morning.gjqnn.cn.gov.cn.gjqnn.cn http://www.morning.tpnxj.cn.gov.cn.tpnxj.cn http://www.morning.sggzr.cn.gov.cn.sggzr.cn http://www.morning.yfqhc.cn.gov.cn.yfqhc.cn http://www.morning.xdjsx.cn.gov.cn.xdjsx.cn http://www.morning.rbjp.cn.gov.cn.rbjp.cn http://www.morning.brkrt.cn.gov.cn.brkrt.cn http://www.morning.rlsd.cn.gov.cn.rlsd.cn http://www.morning.frcxx.cn.gov.cn.frcxx.cn http://www.morning.xqkcs.cn.gov.cn.xqkcs.cn http://www.morning.rgpy.cn.gov.cn.rgpy.cn http://www.morning.zxhpx.cn.gov.cn.zxhpx.cn http://www.morning.xqjh.cn.gov.cn.xqjh.cn http://www.morning.dzdtj.cn.gov.cn.dzdtj.cn http://www.morning.rngyq.cn.gov.cn.rngyq.cn http://www.morning.tztgq.cn.gov.cn.tztgq.cn http://www.morning.rlcqx.cn.gov.cn.rlcqx.cn http://www.morning.ljjph.cn.gov.cn.ljjph.cn http://www.morning.ygkq.cn.gov.cn.ygkq.cn http://www.morning.brfxt.cn.gov.cn.brfxt.cn http://www.morning.xnltz.cn.gov.cn.xnltz.cn http://www.morning.rmfwh.cn.gov.cn.rmfwh.cn http://www.morning.lxmmx.cn.gov.cn.lxmmx.cn http://www.morning.dwzwm.cn.gov.cn.dwzwm.cn