C/C++ 关于数组和指针的总结

摘要:
可以将指向子数组起始位置的指针传递给函数,以便将数组的一部分传递给函数。到目前为止,最常用的指针数组是存储不同长度的字符串。

1、数组的声明形如a[d],其中a是数组的名字,d是数组的维度,编译的时候数组的维度应该是已知的,所以维度d必须是一个常量。如果要定义一个不知道元素个数的以为数组,那么请使用vector容器;

unsigned cnt = 42;          //不是常量表达式
constexpr unsigned sz = 42; //常量表达式
int arr[10];                //含有10个整数的数组
int *parr[sz];              //含有42个整型指针的数组
string bad[cnt];            //错误:cnt不是常量表达式
string strs[get_size()];    //当get_size()返回值是constexpr时正确

2、一元运算符*和&的优先级比算术运算符的优先级高,因此有:

y = *ip + 1; //把*ip指向的对象的值取出并加1,然后把结果赋值给y
*ip += 1; //将*ip指向的对象的值加1
//等同于
++*ip
//或者
(*ip)++    //此处的圆括号是必须的,否则表达式将对ip加1而不是对ip指向的对象的值加1,因为类似*和++这样的一元运算符遵循从右至左的结合顺序</span>
3、指针与数组的纠缠不清

(1)声明

int a[10]; // 定义了一个长度为10的数组a,这十个整数存储在相邻的内存区域中
int *pa; //定义一个指向整型对象的指针
pa = &a[0]; //将指针指向数组a的第0个元素
int x = *pa; //将数组a[0]的内容复制到变量x中
(2)指针运算:“指针加1”就意味着pa+1指向pa所指向的对象的下一个对象;如果pa指向a[0],那么*(pa+1)引用的是数组元素a[1]的内容,pa+i是数组元素a[i]的地址。

(3)数组名和指针的相同与不同

数组名:a[10],a代表的是该数组的第一个元素的地址,所以pa=&a[0]等价于pa=a。

相同:一个通过数组和下标实现的表达式可以等价地通过指针和偏移量实现

不同:指针是一个变量,因此,语句pa=a和pa++都是合法的,但是数组名不是变量,因此类似a=pa和a++都是非法的。

(4)当把数组名传递给一个函数时,实际上传递的是该数组第一个元素的地址。即将数组作为函数参数传递的时候,会将之转换为一个指针。

在函数定义中,形式参数char s[]和char *s是等价的,我们更习惯于后一种形式。

(5)可以将指向子数组起始位置的指针传递给函数,这样就将数组的一部分传递给了函数。例如,如果a是一个数组,那么f(&a[2])和f(a+2)都将起始于a[2]的子数组的地址传递给函数f。对于函数f来说,它并不关心所引用的是否只是一个更大数组的部分元素。
(6)在定义指针的同时一定要初始化,对指针有意义的初始值是NULL或者是表示地址的表达式,这个表达式必须是在此前已经定义的具有适当类型的数据的地址。

(7)在C语言中,0永远不是有效的数据地址。指向不同数组的元素之间的算术或者比较运算就没有定义,这里有个特例,指针的算术运算中可使用数组最后一个元素的下一个元素的地址。

(8)所有的指针运算,都会自动考虑它所指向的对象的长度,并由此决定p+1以后,p会向前移动几个字节。
4、二维数组的数组名、行指针、指针数组,代码是最有力的说明

(1)在C语言中,二维数组实际上是一种特殊的一维数组,它的每个元素也是一个一维数组

(2)将二维数组作为参数传递给函数,那么在函数的参数声明中必须指明数组的列数。数组的行数没有太大关系。比方说有二维数组daytab[2][13],将这个二维数组传递给函数时,f(int daytab[][13]){……}或者f(int (*daytab)[13]){……},这两种声明方式都可以,后一种更加表明其含义——“参数daytab是一个指针,它指向具有13个整型元素的一维数组”

(3)指针数组和二维数组

int a[10][20];

int *b[10];

从语法角度看,a[3][4]和b[3][4]都是对一个int对象的合法引用。

a是一个真正的二维数组,分配了200个int类型长度的存储空间,并通过常规的矩阵下标计算公式“20*row + col”计算得到a[row][col]的位置。 

b,仅仅分配了10个指针,并且没有对它们初始化,如果要跟a[10][20]达到同样的效果,则要在堆上分配200个int类型长度的整数空间,以及栈上10个指针的存储空间

但是:指针数组的一个重要的优点在于,数组的每一行长度可以不同。到目前为止,指针数组最频繁的用处是存放具有不同长度的字符串。

#include <iostream>
using namespace std;

int main()
{
	int a[2][2]={1,2,3,4};

	//二维数组的本质是数组的数组,所以a+1,这里的步长就会走过那个1维数组的长度,一维数组就是这个二维数组a中的对象
	cout << *(*(a+1)) << endl; 
	//a[0]代表二维数组的第一个元素,即第一行的名字,对它加1,步长是行内元素的大小
	cout << *(a[0]+1) << endl; 
	//a[1]代表二维数组的第二个元素,即第二行的名字
	cout << *(a[1]+1) << endl;

	//a[0]的类型是int [2],表示含有两个变量的整型数组;
	//p的类型是int (*)[2],表示指向“含有两个整型变量的数组”的指针
	int (*p)[2] = &a[0]; 
	cout << *(*p+1) << endl;

	// q的类型是int *[2],表示含有两个指向整型变量的指针的数组,即数组中元素的类型是整型指针
	int *q[2];
	q[0] = &a[0][0];
	q[1] = &a[1][1];
	cout << *q[0] << "	" << *q[1] <<endl;
	return 0;
}


免责声明:文章转载自《C/C++ 关于数组和指针的总结》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇C# 使用GDI制作垂直进度条(由下往上).NET CORE 对接天翼云 OOS下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Borland C++ 语法摘要

常用抽象数据类型:包括TList(链表)类、AnsiString类、Set(集合)类、DynamicArray(动态数组)类和TStream(流)类。 TList类实现了线性链表,可以存储任意类的对象。虽然它是链表,但是它实际上是一个存放指针的数组,可以通过其Items属性象访问一个数组那样实现对List对象的每一个元素的访问。 主要属性:Capaci...

变量的结构赋值

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解耦; 1 数组的解耦赋值 以前,为变量赋值,只能直接指定值。 let a = 1; let b = 2; let c = 3; ES6允许写成下面这样。 let [a, b, c] = [1, 2, 3]; 上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。 这种写法属于...

java8使用parallelStream并行流造成数据丢失或下标越界异常解决方案

描述 我们先看一段使用了并行流的代码 @Test public void testStream() { List<Integer> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { li...

oracle 嵌套表 老猫

一、嵌套表的定义:     嵌套表是表中之表。一个嵌套表是某些行的集合,它在主表中表示为其中的一列。对主表中的每一条记录,嵌套表可以包含多个行。在某种意义上,它是在一个表中存储一对多关系的一种方法。考查一个包含部门信息的表,在任何时间内每个部门会有很多项目正在实施。在一个严格的关系模型中,将需要建立两个独立的表department和project.   ...

跟我学算法-pca(降维)

pca是一种黑箱子式的降维方式,通过映射,希望投影后的数据尽可能的分散, 因此要保证映射后的方差尽可能大,下一个映射的方向与当前映射方向正交 pca的步骤: 第一步: 首先要对当前数据(去均值)求协方差矩阵,协方差矩阵= 数据*数据的转置/(m-1) m表示的列数,对角线上表示的是方差,其他位置表示的是协方差 第二步:需要通过矩阵对角化,使得协方差为0,只...

微信小程序之条件判断

前文: 今天踩了一下午的坑,但是确实很简单的问题。 我说一下需求:扫描商品的二维码,从而判断,同一个二维码不可多次扫描; 点击扫一扫 会在灰色区域展示 扫描的商品信息,比如商品名称,商品码等,但是我们的需求是一物一码,即使是同一个商品也是不同的商品码。 错误示例: 最开始我的想法是做判断,因为我会在相对应的js文件中定义一个 productList:[...