搞清C++中指针、数组、字符串的关系

字符串是存储在内存中连续字节的一系列字符,C++处理字符串的方式有两种:

1、来自C语言的C-风格字符串;

2、基于string类库中的方法。

本文主要讲第一种。

在C++中定义一个字符串的语句,可以是:

char arr[] = "aaa"; 

或者是:

char arr[4] = "aaa"; 

或者是:

char arr[4] = {'a','a','a','\0'};  // 注意,最后一个元素一定要是'\0',否则arr不表示字符串

这三种写法都是等效的。

但怎么表示这个字符串呢,有两种C-风格字符串的表示方式:

1、使用带双引号的字符串表示,比如"aaa";

2、使用指向字符串第一个字符的指针表示,比如arr(arr是数组名,也是指向数组第一个元素的指针,也是指向arr这整个数组的指针,其值实际为"aaa"字符串所在的内存地址)。

但是在一些时候,字符串、指针和数组的关系我们常常搞混,下面通过一些典型问题来展示它们间的关系:

1、字符串-数组:

#include <iostream>
int main()
{
    using namespace std;
    char arr[] = "aaa";
    cout << arr << endl;
    cout << &arr <<endl;
    cout << arr[0] << endl;
    cout << &arr[0] <<endl;
    return 0;
}

输出:

aaa(arr等同于字符串"aaa",是一个常量,所以直接输出"aaa")

0x7ffeefbff5d8(arr其实还是指向arr整个数组地址的指针,也是指向arr中第1个元素地址的指针,所以&arr输出了其指向的地址值)

a(输出arr第一个元素)

aaa(这个有点不好理解,&arr[0]其实是 char* 类型(指向第一个元素的指针)。 cout会把char*当做字符串,从指向处开始处理,一直输出直到\0,所以输出"aaa"。如果对它解引用 * &arr[0] 就得到单个字符char 类型。或者也可以这么理解:

因为arr[1] = *(arr + 1),所以有&arr[1] = arr + 1,所以有&arr[0] = arr,所以输出相同)

代码中的双引号做了三件事:

1)申请了空间(在常量区),存放字符串"abc";

2)在字符串尾加上'/0';

3)返回字符串地址给arr。

2、字符串-指针:

#include <iostream>
int main()
{
    using namespace std;
    char arr[] = "abc";
    char* pt = arr;
    cout << *pt << endl;
    cout << pt << endl;
    cout << *pt + 1 << endl;
    cout << *(pt + 1) << endl;
    cout << arr[1] << endl;
    cout << &arr[0] << endl;
    cout << &arr[1] << endl;
    return 0;
}

输出:

a(因为arr也是一个指向首元素地址的指针,赋值给pt后,*pt就是取首元素的值)

abc(同输出arr)

98(*pt为a,+1后转为整型,即97+1=98)

b(*(pt + 1) 相当于 arr[1])

b(数组第二个元素值)

abc(&arr[0]其实是 char* 类型(指向第一个元素的指针)。 cout会把char*当做字符串,从指向处开始处理一直输出直到\0,所以输出"abc")

bc(&arr[1]也是 char* 类型(指向第二个元素的指针)。 cout会把char*当做字符串,从指向处开始处理一直输出直到\0,所以输出"bc")

3、数组-指针:

1)

#include <iostream>
int main()
{
    using namespace std;
    char* p;
    p = "aaa";
    cout << p << endl;
    return 0;
}

2)

#include <iostream>
int main()
{
    using namespace std;
    char arr[4];
    arr = "aaa";
    cout << arr << endl;
    return 0;
}

为什么1)可以编译通过而2)会报错?

因为字符串常量"aaa"表达式使用的值其实是这个字符所存储的地址(在常量区),而不是这个字符本身。所以,可以把字符串赋值给指向字符的指针p,而不能把字符串赋值给一个字符数组名(因为字符数组名虽然也是一个指针,但它是一个(指针)常量,是不可变的,放在等号左边会报错)。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏领域驱动设计DDD实战进阶

07-TypeScript的For循环

在传统的JavaScript中,关于循环,可以有两种方式,一种是forEach,一种是for。 forEach的用法如下: var sarr=[1,2,3,4]...

36040
来自专栏java一日一条

Java的内存机制

Java 把内存划分成两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时...

11100
来自专栏python3

python-其它有用模块1

re模块是文件处理中必不可少的模块,它主要应用于字符串的查找,定位等等,在使用网络爬虫时,即使没有爬虫框架,re模块配合urllib.request模块也可以完...

9120
来自专栏十月梦想

数据类型强制转换

上次提到过数据类型转换在输出的之前(),括号内加入需要转换的类型,这个是临时的转换,下面介绍一下临时转换和永久性数据类型转换。

24030
来自专栏WD学习记录

js学习2016-8-16

    当一个函数被保存为对象的一个属性时,我们称它为一个方法。如果调用表达式包含一个提取属性的动作,(即我们讲解对象时所说的.操作符),那么它就被当做一个方法...

8210
来自专栏黑泽君的专栏

对象数组的内存图解

对象数组的概述和案例(掌握) (1)对象数组的概述     数组既可以存储基本数据类型,也可以存储引用类型。它存储引用类型的时候的数组就叫对象数组。 (...

16120
来自专栏向治洪

传统多线程之前如何共享数据

几种方式 线程执行代码相同,使用同一Runnable对象,Runnable对象中有共享数据 线程执行代码不同,将共享数据封装在另一对象中(操作数据的方法也在该...

190100
来自专栏古时的风筝

Python中的几种数据类型

大体上把Python中的数据类型分为如下几类: Number(数字) 包括int,long,float,complex St...

231100
来自专栏java一日一条

常见Java面试题 – 第三部分:重载(overloading)与重写(overriding)

这篇文章介绍的常见面试题是关于重载(overloading)方法和重写(overriding)方法的。Q.下面代码片段的输出结果是什么?

8720
来自专栏轮子工厂

常见的java面试的基础问题(一) | 看看你会的有多少?

面向对象思想:面向对象是相对于面向过程而言的,面向过程强调的是功能,面向对象强调的是将功能封装进对象,强调具备功能的对象;

7930

扫码关注云+社区

领取腾讯云代金券