宝塔 wordpress ssl青岛网站的优化
web/
2025/9/29 23:35:49/
文章来源:
宝塔 wordpress ssl,青岛网站的优化,视频制作价格明细,电子商务具体是干什么的指针和数组 1. 指针的算术运算1.1 指针加上整数1.2 指针减去整数1.3 两个指针相减1.4 指针比较1.5 指向复合常量的指针 2. 指针用于数组处理3. 用数组名作为指针3.1 数组型实际参数#xff08;改进版#xff09;3.2 用指针作为数组名 4. 指针和多维数组4.1 处理多维数组的元素… 指针和数组 1. 指针的算术运算1.1 指针加上整数1.2 指针减去整数1.3 两个指针相减1.4 指针比较1.5 指向复合常量的指针 2. 指针用于数组处理3. 用数组名作为指针3.1 数组型实际参数改进版3.2 用指针作为数组名 4. 指针和多维数组4.1 处理多维数组的元素4.2 处理多维数组的行4.3 处理多维数组的列4.4 用多维数组名作为指针 5. C99 中的指针和变长数组 本专题介绍 C 语言中指针指向数组元素时的算术运算、关系运算和判等运算通过示范指针处理数组元素的方法揭示指针和数组之间的联系。
参考资料《C 语言程序设计 · 现代方法 第 2 2 2 版》
1. 指针的算术运算
在专题十中我们知道指针可以指向数组元素。例如假设已经声明 a 和 p 如下
int a[10], *p;通过下列写法可以使 p 指向 a[0]
p a[0];其结果可以用图形方式表示为 现在可以通过 p 访问 a[0]。例如可以通过下列写法把值 5 5 5 存入 a[0] 中
*p 5;下面显示的是现在的情况 通过在 p 上执行指针算术运算或者地址算术运算可以访问数组 a 的其他所有元素。C 语言支持 3 3 3 种而且只有 3 3 3 种格式的指针算术运算
指针加上整数指针减去整数两个指针相减
下面仔细研究一下每种运算。下面的所有例子都假设有如下声明
int a[10], *p, *q, i;1.1 指针加上整数
指针 p 加上整数 j 产生指向特定元素的指针这个特定元素是 p 原先指向的元素后的 j 个位置。更确切地说如果 p 指向数组元素 a[i]那么 p j 指向 a[i j]当然前提是 a[i j] 必须存在。
下面的示例说明指针的加法运算插图说明计算中 p 和 q 在不同点的值。
1.2 指针减去整数
如果 p 指向数组元素 a[i]那么 p - j 指向 a[i - j]。例如
1.3 两个指针相减
当两个指针相减时结果为指针之间的距离用数组元素的个数来度量。因此如果 p 指向 a[i] 且 q 指向 a[j]那么 p - q 就等于 i - j。例如 在一个不指向任何数组元素的指针上执行算术运算会导致未定义的行为。此外只有在两个指针指向同一个数组时把它们相减才有意义。
1.4 指针比较
可以用关系运算符、、 和 和判等运算符 和 !进行指针比较。只有在两个指针指向同一数组时用关系运算符进行的指针比较才有意义。比较的结果依赖于数组中两个元素的相对位置。例如在下面的赋值后 p q 的值是 0 0 0而 p q 的值是 1 1 1。
p a[5];
q a[1];1.5 指向复合常量的指针
指针指向由复合字面量见专题八创建的数组中的某个元素是合法的。例如
int *p (int []){3, 0, 3, 4, 1};p 指向一个五元数组的第一个元素这个数组包括 5 5 5 个整数 3 3 3 0 0 0 3 3 3 4 4 4 和 1 1 1。使用复合字面量可以减少一些麻烦我们不再需要先声明一个数组变量然后用指针 p 指向数组的第一个元素。
2. 指针用于数组处理
指针的算术运算允许通过对指针变量进行重复自增来访问数组的元素。下面这个对数组 a 中元素求和的程序段说明了这种方法。在这个示例中指针变量 p 初始指向 a[0]每次执行循环时对 p 进行自增因此 p 先指向 a[1]然后指向 a[2]依此类推。在 p 指向数组 a 的最后一个元素后循环终止。
#define N 10
...
int a[N], sum, *p;
...
sum 0;
for (p a[0]; p a[N]; p)sum *p;下图说明了前 3 3 3 次循环迭代结束时即 p 自增操作前a、sum 和 p 的内容。 for 语句中的条件 p a[N] 值得特别说明一下。尽管元素 a[N] 不存在但是对它使用取地址运算符是合法的。因为循环不会尝试检查 a[N] 的值所以在上述方式下使用 a[N] 是非常安全的。执行循环体时 p 依次等于 a[0], a[1], ..., a[N - 1]但是当 p 等于 a[N] 时循环终止。
当然改用下标可以很容易地写出不使用指针的循环。支持采用指针算术运算的最常见论调是这样做可以节省执行时间。但是这依赖于具体的实现对有些编译器来说实际上依靠下标的循环会产生更好的代码。
* 运算符和 运算符的组合
C 程序员经常在处理数组元素的语句中组合 *间接寻址运算符和 运算符。例如把值存入一个数组元素中然后前进到下一个元素。利用数组下标可以写成 a[i] j;。如果 p 指向数组元素那么相应的语句将会是 *p j;。因为后缀 的优先级高于 *所以编译器把上述语句看成是 *(p) j;。p 的值是 p因此 *(p) 的值将是 *p即 p 当前指向的对象。
当然*p 不是唯一合法的 * 和 的组合。例如可以编写 (*p)这个表达式返回 p 指向的对象的值然后将对象进行自增p 本身是不变化的。
表达式含义*p 或 *(p)自增前表达式的值是 *p以后再自增 p(*p)自增前表达式的值是 *p以后再自增 *p*p 或 *(p)先自增 p自增后表达式的值是 *p*p 或 (*p)先自增 *p自增后表达式的值是 *p
这几种组合中最频繁见到的就是 *p它在循环中是很方便的。对数组 a 的元素求和时可以把
for (p a[0]; p a[N]; p)sum *p;改写成
p a[0];
while (p a[N])sum *p;* 运算符和 -- 运算符有类似的组合方法。在专题九中我们实现了一个栈原始版本的栈依靠名为 top 的整型变量来记录 contents 数组中 “栈顶” 的位置。现在用一个指针变量来替换 top这个指针变量初始指向 contents 数组的第 0 0 0 个元素。
int *top_ptr contents[0];下面是新的 push 函数和 pop 函数
void push(int i)
{if (is_full())stack_overflow();else*top_ptr i;
}int pop(void)
{if (is_empty())stack_underflow();elsereturn *--top_ptr;
}3. 用数组名作为指针
指针的算术运算是数组和指针之间相互关联的一种方法但这不是两者之间唯一的联系。下面是另一种关键的关系可以用数组的名字作为指向数组第一个元素的指针。这种关系简化了指针的算术运算而且使数组和指针更加通用。
例如假设用如下形式声明 a
int a[10];用 a 作为指向数组第一个元素的指针可以修改 a[0]
*a 7; /* stores 7 in a[0] */可以通过指针 a 1 来修改 a[1]
*(a 1) 12; /* stores 12 in a[1] */通常情况下a i 等同于 a[i]两者都表示指向数组 a 中元素 i 的指针并且 *(a i) 等价于 a[i]两者都表示元素 i 本身。换句话说可以把数组的取下标操作看成是指针算术运算的一种形式。
数组名可以用作指针这一事实使得编写遍历数组的循环更加容易。例如下面这个对数组元素求和的循环
for (p a[0]; p a[N]; p)sum *p;可以用 a 替换 a[0]同时用 a N 替换 a[N]
for (p a; p a N; p)sum *p;虽然可以把数组名用作指针但是不能给数组名赋新的值。试图使数组名指向其他地方是错误的
while (*a ! 0)a; /*** WRONG ***/这一限制不会对我们造成什么损失我们可以把 a 复制给一个指针变量然后修改该指针变量
p a;
while (*p ! 0)p;程序 reverse3.c数列反向改进版 /* Reverses a series of numbers (pointer version) */#include stdio.h#define N 10int main(void)
{int a[N], *p;printf(Enter %d numbers: , N);for (p a; p a N; p)scanf(%d, p);printf(In reverse order:);for (p a N - 1; p a; p--)printf( %d, *p);printf(\n);return 0;
}注意scanf 函数的第二个实际参数是 p不是 p。因为 p 指向数组的元素所以它是满足 scanf 函数要求的参数而 p 则是指向指向数组元素的指针的指针。
3.1 数组型实际参数改进版
数组名在传递给函数时总是被视为指针。思考下面的函数这个函数会返回整型数组中最大的元素
int find_largest(int a[], int n)
{int i, max;max a[0];for (i 1; i n; i)if (a[i] max)max a[i];return max;
}假设调用 find_largest 函数如下
largest find_largest(b, N);这个调用会把指向数组 b 第一个元素的指针赋值给 a数组本身并没有被复制。把数组型形式参数看作是指针会产生许多重要的结果。
在给函数传递普通变量时变量的值会被复制任何对相应的形式参数的改变都不会影响到变量。反之因为没有对数组本身进行复制所以作为实际参数的数组是可能被改变的。为了指明数组型形式参数不会被改变可以在其声明中包含单词 const。给函数传递数组所需的时间与数组的大小无关。因为没有对数组进行复制所以传递大数组不会产生不利的结果。如果需要可以把数组型形式参数声明为指针声明 a 是指针就相当于声明它是数组编译器把这两类声明看作是完全一样的。例如可以按如下形式定义 find_largest 函数int find_largest(int *a, int n)
{...
}对于形式参数而言声明为数组跟声明为指针是一样的但是对变量而言声明为数组跟声明为指针是不同的。声明 int a[10]; 会导致编译器预留 10 10 10 个整数的空间但声明 int *a; 只会导致编译器为一个指针变量分配空间。在后一种情况下a 不是数组试图把它当作数组来使用可能会导致极糟的后果。例如赋值 *a 0; 将在 a 指向的地方存储 0 0 0由于我们不知道 a 指向哪里所以对程序的影响是无法预料的。可以给形式参数为数组的函数传递数组的 “片断”所谓片断是指连续的数组元素组成的序列。假设希望用 find_largest 函数来定位数组 b 中某一部分的最大元素比如说元素 b[5], ..., b[14]。调用 find_largest 函数时将传递 b[5] 的地址和数 10 10 10表明希望 find_largest 函数从 b[5] 开始检查 10 10 10 个数组元素largest find_largest(b[5], 10);3.2 用指针作为数组名
C 语言也允许把指针看作数组名进行取下标操作
#define N 100
...
int a[N], i, sum 0, *p a;
...
for (i 0; i N; i)sum p[i];编译器把 p[i] 看作 *(p i)这是指针算术运算非常正规的用法。
4. 指针和多维数组
4.1 处理多维数组的元素
从专题七可知C 语言按行主序存储二维数组换句话说先是 0 0 0 行的元素接着是 1 1 1 行的依此类推。 r r r 行的数组可表示如下 使用指针时可以利用这一布局特点。如果使指针 p 指向二维数组中的第一个元素即 0 0 0 行 0 0 0 列的元素就可以通过重复自增 p 的方法访问数组中的每一个元素。
作为示例一起来看看把二维数组的所有元素初始化为 0 0 0 的问题。假设数组的声明如下
int a[NUM_ROWS][NUM_COLS];显而易见的方法是用嵌套的 for 循环
int row, col;
...
for (row 0; row NUM_ROWS; row)for (col 0; col NUM_COLS; col)a[row][col] 0;如果把 a 看成是一维的整型数组那么就可以把上述两个循环改成一个循环了
int *p;
...
for (p a[0][0]; p a[NUM_ROWS - 1][NUM_COLS - 1]; p)*p 0;循环开始时 p 指向 a[0][0]。对 p 连续自增可以使指针 p 指向 a[0][1]、a[0][2]、a[0][3] 等。当 p 达到 a[0][NUM_COLS - 1]即第 0 0 0 行的最后一个元素时再次对 p 自增将使它指向 a[1][0]也就是第 1 1 1 行的第一个元素。这一过程持续进行直到 p 越过 a[NUM_ROWS - 1][NUM_COLS - 1]数组中的最后一个元素为止。
虽然把二维数组当成一维数组来处理看上去像在搞欺骗但是对大多数 C 语言编译器而言这样做都是合法的。但这样做是否是个好主意则要另当别论。这类方法明显破坏了程序的可读性但是至少对一些老的编译器来说这种方法在效率方面进行了补偿。不过对许多现代的编译器来说这样所获得的速度优势往往极少甚至完全没有。
4.2 处理多维数组的行
为了访问到第 i 行的元素需要初始化指针变量 p 使其指向数组 a 中第 i 行的元素 0 0 0p a[i][0];。对于任意的二维数组 a 来说由于表达式 a[i] 是指向第 i 行中第一个元素元素 0 0 0的指针前面的语句可以简写为 p a[i];。
为了了解原理我们先回顾一下把数组取下标和指针算术运算关联起来的那个神奇公式对于任意数组 a 来说表达式 a[i] 等价于 *(a i)。因此 a[i][0] 等同于 (*(a[i] 0))而后者等价于 *a[i]又因为 和 * 运算符可以抵消也就等同于 a[i]。
下面的循环对数组 a 的第 i 行清零其中用到了这一简化
int a[NUM_ROWS][NUM_COLS], *p, i;
...
for (p a[i]; p a[i] NUM_COLS; p)*p 0;因为 a[i] 是指向数组 a 的第 i 行的指针所以可以把 a[i] 传递给需要用一维数组作为实际参数的函数。换句话说使用一维数组的函数也可以使用二维数组中的一行。因此诸如 find_largest 和 store_zeros 这类函数比我们预期的更加通用。最初设计用来找到一维数组中最大元素的 find_largest 函数现在同样可以用它来确定二维数组 a 中第 i 行的最大元素
largest find_largest(a[i], NUM_COLS);4.3 处理多维数组的列
处理二维数组的一列中的元素就没那么容易了因为数组是按行而不是按列存储的。下面的循环对数组 a 的第 i 列清零
int a[NUM_ROWS][NUM_COLS], (*p)[NUM_COLS], i;
...
for (p a[0]; p a[NUM_ROWS]; p)(*p)[i] 0;这里把 p 声明为指向长度为 NUM_COLS 的整型数组的指针。在 (*p)[NUM_COLS] 中*p 是需要使用括号的如果没有括号编译器将认为 p 是指针数组存放指针的数组而不是数组指针指向数组的指针。表达式 p 把 p 移到下一行的开始位置。在表达式 (*p)[i] 中*p 代表 a 的一整行因此 (*p)[i] 选中了该行第 i 列的那个元素(*p)[i] 中的括号是必要的因为编译器会将 *p[i] 解释为 *(p[i])。
4.4 用多维数组名作为指针
就像一维数组的名字可以用作指针一样无论数组的维数是多少都可以采用任意数组的名字作为指针。思考数组 int a[NUM_ROWS][NUM_COLS];a 不是指向 a[0][0] 的指针而是指向 a[0] 的指针。从 C 语言的观点来看这样是有意义的。C 语言认为 a 不是二维数组而是一维数组且这个一维数组的每个元素又是一维数组。用作指针时a 的类型是 int (*)[NUM_COLS]指向长度为 NUM_COLS 的整型数组的指针。
了解 a 指向的是 a[0][0] 有助于简化处理二维数组元素的循环。例如为了把数组 a 的第 i 列清零可以用
for (p a[0]; p a[NUM_ROWS]; p)(*p)[i] 0;取代
for (p a; p a NUM_ROWS; p)(*p)[i] 0;另一种应用是巧妙地让函数把多维数组看成是一维数组。例如思考如何使用 find_largest 函数找到二维数组 a 中的最大元素。我们把 a数组的地址作为 find_largest 函数的第一个实际参数NUM_ROWS * NUM_COLS数组 a 中的元素总数量作为第二个实际参数
largest find_largest(a, NUM_ROWS * NUM_COLS); /*** WRONG ***/这条语句不能通过编译因为 a 的类型为 int (*)[NUM_COLS] 而 find_largest 函数期望的实际参数类型是 int *。正确的调用是
largest find_largest(a[0], NUM_ROWS * NUM_COLS);a[0] 指向第 0 0 0 行的元素 0 0 0类型为 int *编译器转换以后所以这一次调用将正确地执行。
5. C99 中的指针和变长数组
指针可以指向变长数组中的元素变长数组是 C99 的一个特性。普通的指针变量可以用于指向一维变长数组的元素
void f(int n)
{int a[n], *p;p a;...
}如果变长数组是多维的指针的类型取决于除第一维外每一维的长度。下面是二维的情况
void f(int m, int n)
{int a[m][n], (*p)[n];p a;...
}因为 p 的类型依赖于 n而 n 不是常量所以说 p 具有可改变类型。需要注意的是编译器并非总能确定 p a 这样的赋值语句的合法性。例如下面的代码可以通过编译但只有当 m n m n mn 时才是正确的
int a[m][n], (*p)[m];
p a;如果 m ≠ n m \ne n mn后续对 p 的使用都将导致未定义的行为。
与变长数组一样可改变类型也具有特定的限制其中最重要的限制是可改变类型的声明必须出现在函数体内部或者在函数原型中。
变长数组中的指针算术运算和一般数组中的指针算术运算一样。还是那个对二维数组 a 的一列进行清零操作的例子这次将二维数组 a 声明为变长数组int a[m][n];指向数组 a 中某行的指针可以声明为int (*p)[n];把第 i 列清零的循环如下
for (p a; p a m; p)(*p)[i] 0;
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/84144.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!