❥关于C++之数组与指针

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❥关于C++之数组与指针相关的知识,希望对你有一定的参考价值。

c-string字符串与指针
char charr[20] = "My favorite color";
char* str = "His favorite book";
char* ps = new char[strlen(tmp) + 1];

对于第1种表示法:常规数组表示法。
对于第2种表示法:指针表示法。(char*是一种复合类型,是指向char的指针。)
对于第3种表示法:动态内存分配。

下面示例,打印charr、str、ps作为指针所指向的元素:

char tmp[80];
char charr[20] = "My favorite color";
const char *str = "His favorite book";
char *ps = new char[strlen(tmp) + 1];
strcpy(ps, "abcdefghijklmn");
cout << *charr << "," << *str << "," << *ps << endl;
cout << *(charr + 1) << "," << *(str + 1) << "," << *(ps + 1);
delete[] ps;
M,H,a
y,i,b

可见,不管是哪种表示法,数组名或指针名即是字符串首字符的地址!

数组名指针特别之处
int arr1[8] = 1, 2, 3;
int arr2[8] = 4, 5, 6;
arr2 = arr1;// 编译器报错:Array type 'int [8]' is not assignable

即,不能将一个数组赋值给另一个数组(对任何类型的数组都是一样)!

为什么呢?
因为:数组名arr1与arr2都是指向首字符地址的指针变量,但数组有它的规则,即:数组名本身永远指向初始化时的首元素地址!
所以,arr2不可能再指向其他地址,所以arr2=arr1;这种赋值去改变arr2指向的行为是不可行的。
假设arr2=arr1;能够通过,那arr2就不能代表原先的初始化数据,数组的概念崩塌了!比如arr2指向变了,那他还能代表以前的数组吗、怎么再通过数组名下标访问元素呢。
*(arr2+N)这种访问元素的方式没任何问题,因为它没有改变arr2始终得指向首元素地址的特征。

不难推断:所有数组名都是一个指针常量——指向不能变,指向地址的内容可改变

int p[8] = 1, 2, 3; 
其中变量p等效于下面的变量p:
int arr[3] = 1, 2, 3;
int* const p = arr; 
指针表示的字符串特别之处

指针表示的字符串,是可以赋值的,如下代码是正常的:

char* ps1 = "abc";// Warning:ISO C++11 does not allow conversion from string literal to 'char *'
char* ps2 = "def";// 警告:C++11标准不允许将字符串字面量转化为“char *”
ps2 = ps1;// OK
ps1[0] = 'X';// 运行时产生未知错误!

char food[5] = "abc";
food = "def";// 编译器报错:Array type 'char [5]' is not assignable

int iar[3] = 1, 2, 3;
int iar2[3] = iar;// Error!但可以逐元素复制。

解决警告,在前面加const关键字:

const char* ps = "xyz";// OK

解决以上food数组赋值,应该使用strcpy()或strncpy()来完成以上任务!

指针表示的c-string,在C++11标准中,要加const关键字,并且此指针是常量指针——指向可变,指向地址内容不可改变。

概念:常量数组、【常量指针】、【指针常量】、常量指针常量

A.常量数组——const typename arr[n]——数组元素不能变。
B.常量指针——const typename* p——指向地址的内容不能变(记忆:与p=“hello”;中p性质一样),指向可变。
C.指针常量——typename* const p——指向不能变(记忆:与type p[N];中p性质一样),指向地址的内容可变。
D.常量指针常量——const typename* const p——指向与指向地址的内容都不能变。

二维数组的指针特点


float rain[5][12]:5行12列。二维数组可以看作是把一维数组当作元素的一维数组。rain[5][12]就可看作是由5个元素的组成的一维数组。

const float rain[YEARS][MONTHS] =

    4.3,4.3,4.3,3.0,2.0,1.2,0.2,0.2,0.4,2.4,3.5,6.6,
    8.5,8.2,1.2,1.6,2.4,0.0,5.2,0.9,0.3,0.9,1.4,7.3,
    9.1,8.5,6.7,4.3,2.1,0.8,0.2,0.2,1.1,2.3,6.1,8.4,
    7.2,9.9,8.4,3.3,1.2,0.8,0.4,0.0,0.6,1.7,4.3,6.2,
    7.6,5.6,3.8,2.8,3.8,0.2,0.0,0.0,0.0,1.3,2.6,5.2
;

第1个元素rain[0] = 4.3, 4.3, 4.3, 3.0, 2.0, 1.2, 0.2, 0.2, 0.4, 2.4, 3.5, 6.6
第5个元素rain[4] = 7.6, 5.6, 3.8, 2.8, 3.8, 0.2, 0.0, 0.0, 0.0, 1.3, 2.6, 5.2
初始化一维数组时的格式如下:

sometype ar1[5] = val1, val2, val3, val4, val5;

对于rain[5][12],只不过val1 = 4.3, 4.3, 4.3, 3.0, 2.0, 1.2, 0.2, 0.2, 0.4, 2.4, 3.5, 6.6
我们知道,对于一维数组,数组变量名即是指向首个元素的指针变量。对于二维数组也一样,数组变量名也是指向首个元素的指针变量。

#include <stdio.h>
#define SIZE 4
int main(void) 
    short dates[SIZE];
    double bills[SIZE];
    printf("%22s %11s\\n", "short", "double");
    for (int i = 0; i < SIZE; i++)
        printf("pointers + %d: %10p %10p\\n", i, dates + i, bills + i);
    return 0;

                 short     double
pointers + 0: f1f19ffa24 f1f19ffa00
pointers + 1: f1f19ffa26 f1f19ffa08
pointers + 2: f1f19ffa28 f1f19ffa10
pointers + 3: f1f19ffa2a f1f19ffa18

由以上打印结果可知:一维数组元素的内容空间地址是连续分配的

int dates[2][2];
cout << &dates[0][0] << "," << &dates[0][1] << "," << &dates[1][0] << "," << &dates[1][1];
0x2d95fff6e0,0x2d95fff6e4,0x2d95fff6e8,0x2d95fff6ec

由以上打印结果可知:二维数组元素的内容空间地址也是连续分配的。下一行与上一行首尾连续。

int dates[2][2];
cout << &dates << "," << &dates[0] << "," << &dates[0][0];
0xa9399ff6b0,0xa9399ff6b0,0xa9399ff6b0

以上结果直观表现在下图:

dates是一个指向一个指针变量(一个指向首行一维数组的指针)的指针变量。
dates[N]是第N行一维数组的地址(而非第N行一维数组首元素的地址)。
dates[M][N]是二维数组第M行第N列的元素。

int dates[2][2] = 
	1, 2,
    3, 4
;
cout << *dates << "," << **dates;
0xdf475ffcb0,1

由以上程序及运行结果可知,一维数组名是指针,二维数组名是一个指针的指针

二维数组指针的“步长”

所谓“步长”,就是地址+1后相对于sizeof(typename)的偏移倍数值。

int dates[3][3] = 
	1, 2, 3,
    4, 5, 6,
    7, 8, 9
;
cout << &dates << "," << &dates[0] << "," << &dates[0][0] << endl;
cout << &dates + 1 << "," << &dates[0] + 1 << "," << &dates[0][0] + 1;
0xdf6a1ff980,0xdf6a1ff980,0xdf6a1ff980
0xdf6a1ff9a4,0xdf6a1ff98c,0xdf6a1ff984

0xdf6a1ff980 + 9×sizeof(int) = 0xdf6a1ff9a4,即步长为9 = 二维数组元素总数。
0xdf6a1ff980 + 3×sizeof(int) = 0xdf6a1ff98c,即步长为3 = 每行的元素数。
0xdf6a1ff980 + 1×sizeof(int) = 0xdf6a1ff9a4,即步长为1 = 每行相邻元素间隔sizeof(int)。
虽然dates与dates[0]、dates[0][0]所指向的地址相同,但它们的步长不同!所以它们不能互相替代使用。

数组名被解释为其第一个元素的地址,而对数组名应用地址运算符时,得到的是整个数组的地址。

由于二维数组其中每个元素都是一个一维数组,所以dates[m]表示的是二维数组第m个元素的指针变量。
dates就是一个指针的指针(是指针dates[0]的指针)。

dates==&dates[0] => *dates==dates[0] => **dates==dates[0][0]
dates+2==&dates[2] => *(dates+2)==dates[2] => **(dates+2)==dates[2][0]
*(dates+2)+1==dates[2]+1 => *(*(dates+2)+1)==dates[2][1]

推出公式:dates[m][n] == *(*(dates+ m) + n)

二维数组指针表示法和用法

typename (*p)[n]typename *p[n]的区别?
因为[]的优先级高于*,所以:
前者:p是一个指针,指向一个包含n个元素的一维数组。它是指针数组。(记忆:首先它是指针,指向的是数组)。
后者:p是一个数组,数组有n个元素,每个元素都是一个指针变量。它是数组指针。(记忆:首先它是数组,数组元素是指针)。

用指针指向一个数组,可以直接用这个指针+下标的方式访问/修改数组元素。

如下示例,用指针访问一维和二维数组:

int ar[6] = 1, 7, 15, 23, 31, 59;
int(*par)[6] = &ar;
cout << "    (*par)[4] = " << (*par)[4] << endl;
cout << "(*(*par + 4)) = " << *(*par + 4) << endl;
cout << "*(par[0] + 4) = " << *(par[0] + 4) << endl;
cout << "    par[0][4] = " << par[0][4] << endl;
puts("*************************");
int zippo[4][2] = 
    1,  2,
    5,  6,
    9,  10,
    13, 14
;
int(*pz)[2] = zippo;
cout << "*(*(pz + 2) + 1) = " << *(*(pz + 2) + 1) << endl;
cout << "    *(pz[2] + 1) = " << *(pz[2] + 1) << endl;
cout << "        pz[2][1] = " << pz[2][1];
    (*par)[4] = 31
(*(*par + 4)) = 31
*(par[0] + 4) = 31
    par[0][4] = 31
*************************
*(*(pz + 2) + 1) = 10
    *(pz[2] + 1) = 10
        pz[2][1] = 10
通过动态内存分配创建数组
int n;
double* p = new double[3];// 分配3个double数据的空间
p[0] = 0.2;
p[1] = 0.5;
p[2] = 0.8;
int ar[n];// 变长数组(C99开始支持)
ar[0] = p[2];
...
delete[] p;
int m = 6;
int n = 5;
int **paa;
paa = new int *[m];// 动态申请二维数组m行
for (int i = 0; i < m; i++)
    paa[i] = new int[n];// 动态申请一维数组n列
......
// 释放二维数组占用的空间
for (int k = 0; k < m; k++)
	delete[] paa[k];
delete[] paa;

以上是关于❥关于C++之数组与指针的主要内容,如果未能解决你的问题,请参考以下文章

《C++ Primer》学习 之 返回数组的引用(返回数组的指针,方法与之相同)

❥关于C++之函数与指针

c++指针数组与二维数组的最大区别

C++对象数组与对象指针

试图实现一个 C++ 库,需要一些关于如何与它交互的指针

❥关于C++之智能指针