返回首页
EN
H7 / SECURITY RESEARCH NOTES

第10章 数组和指针

  • 数组名是数组首元素的地址,假如demo是一个数组,则demo == &demo[0]成立

  • %p为指针十六进制显示的转换说明

  • %zdsizeof返回值(即size_t类型)的转换说明,单位是字节。

  • c语言中,ar[i]*(ar + i)这两个表达式都是等价的,但是有一点,只有当ar是指针变量时,才能使用ar++这样的表达式。

  • 地址之间的差值可以用转换说明%td

指针操作

1.赋值:把地址赋给指针
- 数组名赋值
- 带地址运算符&的变量名赋值
- 另一个指针赋值
2.解引用:*运算符给出指针指向地址上的值
3.取址:&运算符给出变量本身的地址
4.指针与整数相加:可以使用 + 把指针与整数相加,或者整数与指针相加。
- 计算结果 = 整数 × 指针所指向类型的大小(以字节为单位)+ 初始地址
5.递增指针:递增指向数组元素的指针可以让该指针指向数组的下一个元素。
6.指针减去一个整数:可以用 - 从指针减去一个整数,这里指针必须在被减数的位置。
- 计算结果 = 初始地址 - 整数 × 指针所指向类型的大小(以字节为单位)
7.递减指针:递减指向数组元素的指针可以让该指针指向数组的上一个元素。
8.指针求差:可以计算两个指针之间的差值。差值的单位与数组类型的单位相同,例如 int 类型的数组中两个相邻的值的指针相减,那么得出的值为 1,即 1 个int。
9.比较:可以用关系运算符比较两个指针的值,前提是两个指针指向相同类型的对象。

关键字const

# 以下为2021.10.28日的笔记
作用:用于限制一个变量为只读变量。

- 编写一个处理基本类型的函数时,对于数组来说,必须传递指针,这样效率高,但是会面临一个问题就是关于修改数组的问题。解决办法是通过使用指向const的指针,表明该函数不会通过指针来修改数据,如下:
int sum(const int ar[], int n); /* 函数原型 */
int sum(const int ar[], int n) /* 函数定义 */
{
	int total = 0;
	...
	return total
}

- const可以创建const数组、const指针、指向const的指针。
# 说说const指针和指向const的指针
const double locked[4] = {1, 2, 3, 4}
const double * pc = rates; /* 指向const的指针 */
double * const pc = rates; /* const指针 */
const double * const pc = rates; 
- 规律总结:
1.*左边有const意味着是指向const的指针,特点是不能使用指针来更改它所指向的值。
2.*右边有const意味着是const指针,特点是指针本身的值是不可变的。即指针指向的地址是一个固定值,那就不能改变指针所指向的地址。
3.如果const用了两次,则不能使用指针改变指向的值且指针指向的地址也不能变。
4.指向const的指针可以指向const数据和非const数据,但是普通指针不能指向const数据(不然就能通过指针操作const数据了)

# 用途:
- 指向const的指针通常用于函数形参中,表明该函数不会使用指针改变数据。

指针和多维数组

看书看的云里雾里,但是一定要把这个搞懂!世界上更多的是没有耐心的人,所以保持耐心,一定能搞懂!

int zippo[4][2]; /*内含int数组的数组*/

// zippo是数组首元素的地址,即zippo == &zippo[0];
// 而zippo[0]是一个内含两个int对象的数组,所以zippo[0] == &zippo[0][0];
// 所以zippo是一个指向占用两个int大小对象的指针,而zippo[0]是一个指向占用1个int大小对象的指针
// 但是由于zippo和zippo[0]开始于同一个地址,所以zippo == zippo[0]

// 解引用一个指针(即在指针前使用 * 运算符),得到指针所指向的地址上的值。因为zippo[0]是数组首元素zippo[0][0]的地址,所以*(zippo[0]) = zippo[0][0].
// *zippo = zippo[0],因为zippo[0] == &zippo[0][0],所以*zippo = &zippo[0][0]
// **zippo = zippo[0][0]
  • 针对int zippo[4][2],总结关系如下:
# 几类符号:
1.zippo
2.zippo[0]
3.&zippo[0]
4.zippo[0][0]
5.&zippo[0][0]
6.*zippo
7.**zippo

(1) zippo数组名代表二维数组首元素的地址,所以推导出 zippo == &zippo[0],所以 *zippo = zippo[0]

(2) 二维数组首元素是一个包含两个int对象的一维数组,所以zippo[0]相当于一维数组数组名,那么针对一维数组数组名来说,数组名相当于首元素的地址,所以推导出 zippo[0] == &zippo[0][0], 所以 *zippo[0] = zippo[0][0]

结合(1)和(2),可以推导出 **zippo = zippo[0][0]

先背下来吧,zippo == zippo[0] == &zippo[0][0]

- 注意:zippo + 1 得到的值和 zippo[0] + 1 得到的值是不同的,因为zippo是指向一维数组对象的指针,而 zippo[0] 是指向一维数组的单个对象的指针。两个对象占用的内存空间大小不同。

指向多维数组的指针

// pz 指向一个内含两个int类型值的数组
int (* pz)[2]; // 括号的原因是[]的优先级高于*

// 下面是声明一个指针数组
int * pz[2] // 表示该数组含两个指向int类型值的指针

函数和多维数组

# 用到时再说

变长数组

变长数组指在创建数组的时候,可以使用变量指定数组的维度。
# 声明带二维变长数组参数的函数如下:
int sum2d(int rows, int cols, int ar[rows][cols]); # 注意:rows和cols的声明要在ar[rows][cols]之前,因为ar[rows][cols]用到了它。
# 但是,该程序要求编译器支持变长数组这个特性