您的位置:首页 > 编程语言 > C语言/C++

C++每日一课(十五)

2017-07-09 13:02 337 查看
指针和字符串

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++中的强大工具之一,但也最为危险。它可以执行对计算机不友好的操作,如使用未经初始化的指针来访问内存或试图释放同一个内存块两次
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++每日一课 c++ 编程