C++每日一课(十五)

20 篇文章 0 订阅
指针和字符串


char name[10] = "xiesheng";
cout<<name<<" is a good man!"<<endl;


数组名是第一个元素的地址,因而cout语句中,name是包含字符x的char元素地址,cout对象认为char的地址是字符串地址,因此它打印该地址处的字符,然后继续打印后面的字符,直到遇到空字符\0为……
从上面可以知道则可以把指向char的指针变量作为cout的参数,因为它是一个char地址。
对于上面的" is a good man!",它也使用cout进行输出,这个引号括起来的也是一个地址
在C++中,用引号括起来的字符串像数组名一样,也是第一个元素的地址。这个时候发送给cout的不是整个字符串而是字符串的地址
对于数组中的字符串、用引号括起来的字符串常量以及指针所描述的字符串,处理的方式都是一样的,都是把它们的地址传给cout


/*
作者:xiesheng
时间:2017-07-09
版本:v1.0
说明:指针
*/
#include <iostream>
#include <cstring>	//其中定义了strlen()、strcpy()


int main() {


	using namespace std;


	char name[20] = "xiesheng";
	const char * addr = "hunan";
	char * ps;	


	cout <<"name:"<< name << " address:" << addr<<endl;
	cout << "请输入一个名称:";
	cin >> name;
	ps = name;
	cout << ps << endl;
	cout << "使用strcpy()之前:" << endl;
	cout << name << " at " << (int *)name<<endl;
	cout << ps << " at " << (int *)ps << endl;
	ps = new char[strlen(name) + 1];
	strcpy(ps, name);
	cout << endl;
	cout << "使用strcopy之后:" << endl;
	cout << name << " at " << (int *)name << endl;
	cout << ps << " at " << (int *)ps << endl;


	delete[] ps;


	system("pause");
	return 0;
}





name:xiesheng address:hunan
请输入一个名称:advent
advent
使用strcpy()之前:
advent at 0075EBBC
advent at 0075EBBC


使用strcopy之后:
advent at 0075EBBC
advent at 00BC3660
请按任意键继续. . .


const char * addr = "hunan";
上面这个语句中"hunan"实际上表示的是字符串的地址,因而是把字符串的地址赋值给指会addr
一般来说,编译器在内存中留出一些空间,来存储程序源代码中所有用引号括起来的字符串,并把每个被存储的字符串与其地址关联起来。


通过上面的语句,可以像使用字符串"hunan"那样使用指针addr
字符串字面值是常量,因而指针在声明时使用了const关键字。
按上面的声明,则可以用addr来访问字符串,但是不可以修改它


C++中对于字符串字面值的处理
C++不保证字符串字面值被唯一地存储,也就是说如果在程序中多次使用"hunan",那么编译器可能存储这个字符串的多个副本,也有可能只存储一个副本。
如果是只有一个副本,那么addr设置指向"hunan",将使它只是指向这个字符串的唯一一个副本。把值读入一个字符串可能会影响被认为是独立、位于其他地方的字符串。
不管怎么样addr指针被声明为const,这时编译器会禁止改变addr指向的位置中的内容。


指针ps是没有初始化的,因而不知道信息会被存储在哪里,如果赋值可能会改写内存中的信息。
要避免上面的问题可以使用足够大的char类组来接收输入就可以了,一定要注意不要使用字符串常量或未被初始化的指针来接收输入,为了避免这种呢况也可以使用std::string对象。


对于cout来说如果提供一个指针则会打印它的地址,但是如果给的类型是char *则cout会显示指向的字符串。如果想显示字符串的地址可以把这种指针强制转成另一种指针类型 ,比如int *


对于语句:ps = name; 它赋值的不是字符串而是地址,这个时候两个指针就指向了相同的内存单元和字符串
如果要获得字符串的副本可以按如下做
1.需要分配内存来存储这个字符串(另外声明一个数组、使用new来完成)


ps = new char[strlen(name)+1];
2.把原数组中的字符串复制到新分配的空间中,这个时候需要使用库函数strcpy()
strcpy()中有两个参数,第一个是目标地址,第二个是要复制的字符串地址
注意:一定要保证目标地址有足够的空间来存储要复制的字符串 
另外一个库函数,strncpy(),这个函数接收3个参数,除了strcpy()中的两个参数外,第三个参数是要复制的最大字符数。
比如:
char name[20];
strncpy(name,"abcdefghigklmnopqrstuvwxyz",19)
name[19] = '\0';
这样的话最多把19个字符复制到数组当中,最后一个元素被置成空字符




使用new创建动态结构
在运行的时候创建数组比编译时创建数组,对于结构来说也是如此的。
如果在在运行时为结构分配所需要的空间,也可以使用new运算符来完成。
new用于结构由两步组成:
1.创建结构
2.访问结构


创建结构
需要同时使用结构类型和new
比如:如果要创建一个未命名的struct1类型,并把它的地址赋给一个指针,可以如下:
struct1 * ps = new struct1;
这个时候会把足够存储struct1结构的一块可用内存的地址赋值给到ps。


访问结构
需要访问成员,这个时候不能使用运算符.来用于结构名,因为这个时候就是没有结构变量名称的,只知道它的地址
这个时候C++提供了一个运算符,箭头成员运算符->,它可以用于指向结构的指针,就像点运算符可用于结构名一样的。
注意:在指定结构成员时,何时使用点运算符何时使用箭头运算符
      如果结构标识符是结构名,则使用点运算符,如果标识符是指向结构的指针,则使用箭头运算符


/*
作者:xiesheng
时间:2017-07-09
版本:v1.0
说明:指针
*/
#include <iostream>


//定义一个结构
struct s {
	char name[20];
	float volume;
	double price;
};


int main() {


	using namespace std;
	s * ps = new s;	//为结构体分配内存
	cout << "请输入名称:";
	cin.get(ps->name, 20);
	cout << "输入体积:";
	cin >> (*ps).volume;
	cout << "输入价格:";
	cin >> ps->price;
	cout << "Name:" << (*ps).name << endl;
	cout << "Volume:" << ps->volume << endl;
	cout << "Price:" << ps->price << endl;


	delete ps;


	system("pause");
	return 0;
}




请输入名称:table
输入体积:100
输入价格:12.78
Name:table
Volume:100
Price:12.78
请按任意键继续. . .


/*
作者:xiesheng
时间:2017-07-09
版本:v1.0
说明:指针
*/
#include <iostream>
#include <cstring>	//这里也可以使用string.h
//声明一个函数原型
using namespace std;	//这里写在这里是因为自定义的函数中也要使用到std空间
char * getname(void);


int main() {


	char * name;
	
	name = getname();	//函数getname()返回的是一个char *
	cout << name << " at " << (int *)name << endl;
	delete[] name;	


	name = getname();	//函数getname()返回的是一个char *
	cout << name << " at " << (int *)name << endl;
	delete[] name;


	system("pause");
	return 0;
}


char * getname() {
	char temp[80];
	cout << "请输入名称:";
	cin >> temp;
	char * pn = new char[strlen(temp) + 1];


	strcpy(pn, temp);	//复制string到分配的pn指针内存空间中
	return pn;
}




请输入名称:xiesheng
xiesheng at 014B95C8
请输入名称:advent
advent at 014413A8
请按任意键继续. . .


getname函数
使用cin把输入的字符串放到temp的数组中,然后使用new分配新内存,以存储这个字符串
程序中需要使用strlen(temp)+1来动态计算出需要分配的存储空间,这里+1表示除了要存这些字符外还要存一个空字符,有了这个值后把这个值提供给new。
获得空间后,getname()使用标准库函数strcpy()把temp中的字符串复制到新的内存块中。
注意:strcpy并不检查内存块是否能够容纳字符串,在getname()中通过使用new请求合适的字节数来完成了这个工作
最后返回pn指针,这时返回的是字符串副本的地址。


在上面的例子中,getname()分配内存,而main()函数中释放内存,把new和delete放在不同的函数中通常不是一个好办法,因为这样容易忘记使用delete.




C++有3种管理数据内存的方式
1.自动存储
2.静态存储
3.动态存储


自动存储
在函数内部定义的常规变量使用自动存储空间,被称为自动变量,这意味着它物业在所属的函数被调用时自动产生,在这个函数调用结束时自动消亡。
实际上,自动变量是一个局部变量,其作用域为包含它的代码块。
自动变量通常存储在栈中,这就是说在执行代码块时,其中的变量会依次加入到栈中,而离开代码块时,将按相反的顺序释放这些变量。这个过程称为后进先出LIFO


静态存储
静态存储是整个程序执行期间都存在的存储方式,使变量成为静态的方式有两种
1.在函数外面定义它
2.在声明变量时使用关键字static
static double fee = 1.20;


动态存储
new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理一个内存池,在C++中称为自由存储空间或堆,它会与静态变量和自动变量的内存是分开的。
new和delete中还支持在一个函数中分配内存,而在另一个函数中释放它。
在使用new和delete让程序员对程序如何使用内存有了更大的控制权,并且内存管理也就更复杂了。在栈中,自动添加和删除机制使得占用内存总是连续的,但new和delete的相互影响可能导致占用的自由存储区不连续,这使得跟踪新分配内存的位置更困难。


注意:指针是C++中的强大工具之一,但也最为危险。它可以执行对计算机不友好的操作,如使用未经初始化的指针来访问内存或试图释放同一个内存块两次





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值