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
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++提供了一个运算符,箭头成员运算符->,它可以用于指向结构的指针,就像点运算符可用于结构名一样的。
注意:在指定结构成员时,何时使用点运算符何时使用箭头运算符
如果结构标识符是结构名,则使用点运算符,如果标识符是指向结构的指针,则使用箭头运算符
请输入名称:table
输入体积:100
输入价格:12.78
Name:table
Volume:100
Price:12.78
请按任意键继续. . .
请输入名称: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++中的强大工具之一,但也最为危险。它可以执行对计算机不友好的操作,如使用未经初始化的指针来访问内存或试图释放同一个内存块两次
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++每日一课(四)
- C++每日一课(十九)
- C++每日一课(一)
- C++每日一课(十七)
- C++每日一课(十六)
- C/C++每日一课 >> [原创]细说如何确定一个变量的类型(2005-04-25)
- C++每日一课(八)
- C++每日一课(七)
- C++每日一课(十八)
- C++每日一课(十二)
- C/C++每日一课 >> [原创]指针易犯错误集(初学者适用)(2005-05-08)
- C++每日一课(六)
- C++每日一课(十)
- C++每日一课(十四)
- C++每日一课(二十)
- C/C++每日一课 >> [原创]typedef 的两种用法
- C++每日一课(十一)
- C++每日一课(二)
- C++每日一课(五)