C++解析函数:返回char *p与char p[]类型的区别

一、首先来看一道题目:

#include "stdio.h"
char *get_string_1()
{
    char p[] = "hello world!";
    return p;
}
char *get_string_2()
{
    char *p = "hello world!";
    return p;
}
int main()
{
    char *p;
    p = get_string_1();
    printf("get_string_1:%s\n",p);
    p = get_string_2();
    printf("get_string_2:%s\n",p);
    return 0;
}

输出:
get_string_1:(乱码或者没有输出),linux下没有任何输出
get_string_2:hello world!
为什么会这样?

关键点:

char p[] = "hello world!";//有2份内存拷贝,一份在常量区、一份在栈区(会消失)

char *p = "hello world!";//只有1份内存,就在常量区(不会消失)=》所以可以!

1.”hello world”是一个字符串常量,存放在静态数据区,没错,但是把一个字符串常量赋值给了一个局部变量,该变量存放在栈中。
这样就有两块内容一样得内存,也就是说“char p[]=”hello world!”;这条语句让“hello world!”这个字符串在内存中有两份拷贝,一份在动态分配的栈中,另一份在静态存储区。
当get_string_1函数退出得时候,栈要清空,局部变量内存也被清空了,所以这个时候函数返回得是一个已经释放了得内存地址。

2.因为”hello world!”是一个字符串常量,存放在静态数据区,把该字符串常量存放的静态数据区的首地址赋值给了指针。所以get_string_2函数退出时,该该字符串常量所在内存不会被回收,故能够通过指针顺利无误的访问。

//=======================================================================

二、

数组名的盲点

  • 数组名可以看作是一个常量指针不可以赋值(第一次初始化的时候除外!=》调用的是 构造函数,不是赋值运算符)
  • 数组名“指向”的是内存中的数组首元素的起始位置
  • 在表达式中数组名只能做右值使用
  • 只有在数组名作为sizeof操作符的参数和&运算符的参数时不能看作常量指针
     

1.作为参数类型时没有区别

        像函数传参数的时候,实参可以是char*,形参可以是 char[],比如: 
void fun1(char b[]){ 
printf(“%s”,b); 

int main(){ 
char *a=“HellowWorld”; 
fun1(a); 

反过来,实参可以是char[],形参可以是 char *也是可以的。

2.作为变量类型时有明显区别

一个是在分配一个数组,一个是在分配一个指针,大小就不一样。使用字符串字面量初始化的时候,char[]会复制字面量到本地数组中(包括结尾的\0),而char *会指向这个常量字符串,这个字符串在许多实现中是不可以修改的,使用会修改字符串本身的库函数就会报错。

2.1 char* s1=”abc”; s1是一个指针,s1所指向的地址的内容不可改变的,但是s1指针可以指向其他地址。s1是指向字符串常量的,它存储在里不可被修改。
如下:

    char* s1="abcd";
    s1[2]='z';    //错误:编译时能通过运行的时候会报错(因为:s1指向的是常量"abcd"。而s1[2]='c',字符串常量是不允许被改变的!!)
    s1="xyz";     //可以将指针指向其他内容(不是const类型指针,都可以指向其他内容)
    cout<<s1[2]<<endl;

2.2 char s2[] =”cdef”;是一个数组,s2指向第一个元素所在的位置,一经分配就不能更改。 它的空间是则栈里分配的,可以被重新修改,但是s2不能够再指向其他空间
如下:

    char s2[]="efgh";
    s2="xyz";        //出错:s2不可以再指向其他内容
    cout<<s2[2]<<endl;  // s2中的元素是可以被修改的

2.3 若是将指针指向一个数组,那么这个数组既可以被改变元素值又可以被指向其他字符串。如下:

    char *p=s2;
    p[0]='x';   //可以改变元素值
    p="rty";    //可以指向其他字符串
    cout<<p;

这种既能改变元素值又能重新指向其他字符串的方式与string定义的字符串功能很相似。

注意,可以p = s2,但不可以s2 = p,且数组名可以赋值给指针表示地址,但是却不能赋给数组名,它是一个常量类型,所以不能修改。

char * a=”string1”; 
char b[]=”string2”; 
a=b; //a,b指向同一个区域,注意这里改变了a的指向 
gets(a) //OK 
printf(“%s”,b) //会出现gets(a)时输入的结果 
解释: a的值变成了是字符数组首地址,即&b[0],该地址指向的区域是char *或者说 char[8],习惯上称该类型为字符数组,其实也可以称之为“字符串变量”,区域可读可写。

注意:char *本身是一个字符指针变量,但是它既可以指向字符串常量又可以指向字符串变量指向的类型决定了对应的字符串能不能改变

三、

说明以下问题:

char * a=”string1”;是实现了3个操作: 
1.声明一个char*变量(也就是声明了一个指向char的指针变量)。 
2.在内存中的文字常量区中开辟了一个空间存储字符串常量”string1”。 
3.返回这个区域的地址,作为值,赋给这个字符指针变量a 
最终的结果:指针变量a指向了这一个字符串常量“string1” 
(注意,如果这时候我们再执行:char * c=”string1”;则,c==a,实际上,只会执行上述步骤的1和3,因为这个常量已经在内存中创建)

char b[]=”string2”;则是实现了2个操作: 
1.声明一个char 的数组, 
2.为该数组“赋值”,即将”string2”的每一个字符分别赋值给数组的每一个元素,存储在栈上。 
最终的结果:“数组的值”(注意不是b的值)等于”string2”,而不是b指向一个字符串常量

PS: 
实际上, char * a=”string1”; 的写法是不规范的! 
因为a指向了即字符常量,一旦strcpy(a,”string2”)就糟糕了,试图向只读的内存区域写入,程序会崩溃的!尽管VS下的编译器不会警告,但如果你使用了语法严谨的Linux下的C编译器GCC,或者在windows下使用MinGW编译器就会得到警告。

所以,我们还是应当按照”类型相同赋值”的原则来写代码: const char * a=”string1”; 
保证意外赋值语句不会通过编译。

小结

对于 
const char * a=”string1” 
char b[]=”string2”;

1.a是const char 类型, b是char const类型 
( 或者理解为 (const char)xx 和 char (const xx) )

2.a是一个指针变量,a的值(指向)是可以改变的,但a只能指向(字符串)常量,指向的区域的内容不可改变;

3.b是一个指针常量,b的值(指向)不能变;但b指向的目标(数组b在内存中的区域)的内容是可变的;

4.作为函数的声明的参数的时候,char []是被当做char *来处理的!两种形参声明写法完全等效!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值