❥关于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++之数组与指针的主要内容,如果未能解决你的问题,请参考以下文章