【C语言基础】11 指针与数组

【C语言基础】11 指针与数组,第1张

一、指针的算术运算

 指针指向数组元素时,C语言允许对指针进行算术运算(加&减)。由此,可用指针代替数组下标来处理数组。

C语言支持的3种指针算术运算:

  • 指针加上整数;
  • 指针减去整数;
  • 两指针相减。(不能相加)
指针能与整数加减的原理是该整数会根据指针类型进行缩放,类似int与doule类型想加减。
1、指针加上整数

 指针 p 加上整数 j 后,指向 p 的对象后的第 j 个位置,即:

pa[i]
p + ja[i+j]

以上成立的前提是:

  • ij均为整型;
  • a[i]a[i+j]均存在。

2、指针减去整数

 指针 p 减去整数 j 后,指向 p 的对象前的第 j 个位置,即:

pa[i]
p - ja[i-j]

以上成立的前提是:

  • ij均为整型;
  • a[i]a[i-j]均存在。

注意:不允许整数减去指针。


3、两指针相减

 两指针相减,结果为指针之间的距离(数组元素对象的标号之差)。即:

pa[i]
qa[j]

p - q = i-j

举例:

int a[] = {0, 1, 2, 3, 4, };
	
	int i, *p = &a[5], *q = &a[1];
	i = p - q;
	
	printf("    p = %p\n", p);
	printf("    q = %p\n\n", q);
	
	printf("%%d :       i = %d\n", i);
	printf("%%d :    p-q = %d\n", p-q); // %d输出 
	printf("%%X :    p-q = %X\n", p-q); // %X输出 
	printf("%%p :    p-q = %p\n", p-q); // %p输出 

执行结果:

注意:

  • 在不指向任何数组元素的指针进行算术运算会导致未定义行为。
  • 两指针指向同一个数组,指针相减才有意义。

4、指针比较

指针比较:><>=<===!=

只有指向同一数组时,指针比较才有意义。

p = &a[5];
q = &a[1];

对于以上两指针,p<=q 的值是0,p>=q 的值是1。


5、指向复合字面量的指针

复合字面量:指定内容元素的没有名字的数组。(见 【07 函数】)

举例:

int *p = (int []){1, 2, 3, 4, 5};  //p指向数组的第一个元素

此语句等价于:

int a[] = {1, 2, 3, 4, 5};
int *p = &a[0];

二、指针处理数组 1、使用指针 依序访问数组元素
#define N 10
...
int a[N], sum, *p;
...
sum = 0;
for(p=&a[0]; p<&a[N]; p++){
	sum += *p;
}
  • 使用数组的地址来控制循环;
  • a[N]不存在,&a[N]是合法的。程序不会检查a[N]的值。当p = &a[N]时,循环结束。

2、*运算符与++运算符的组合 (1)举例

  举例1: 把值存入数组元素后,转到下一个元素

a[i++] = j;   //方法1

*p++ = j;     //方法2

说明:

  • i++表达式的值是自增之前的值;
  • 后辍++运算符的优先级高于*运算符。等价于*(p++) = j;

自增表达式含义举例:

表达式含义
*p++*(p++)自增前表达式的值是*p,之后再自增p
(*p)++自增前表达式的值是p,之后再自增p
*++p*(++p)先自增p,自增后表达式的值是*p
++*p++(*p)先自增p,自增后表达式的值是p
(2)应用

*p++的应用最常见。

for(p=&a[0]; p<&a[N]; p++){
	sum += *p;
}

等价于:

p = &a[0];
while(p<&a[N]){
	sum += *p++;
}

三、数组名作为指针 1、概念
使用数组名作为指向数组的第一个元素的指针,且该指针不需要单独声明。
int a[10];
*a = 7;           // a[0]=7; 不需要单独声明
*(a+1) = 12;      // a[1]=12

说明:

  • 对C编译器而言,a[i]与*(a+i)等价,a+i 与 & a[i] 等价
  • 把数组的取下标 *** 作视为指针的算术运算。

2、数组作为函数参数 (1)形式参数

形参参数声明中,*a 与 a[ ] 等同。

int find_largest(int a[], int n)
{
	...
}

//上下两种函数的声明是相同的,都是将指针作为形式参数

int find_largest(int *a, int n)
{
	...
}

注意:当作为形式参数的数组为const时,对应的指针也需要加const

int find_largest(const int a[], int n)
{
	int *p = a;   //错误写法
	const int *p = a;  //正确写法
}
(2)实际参数
数组名在传递给函数时,也总是被视为指针(被转换为指针)。

注意:数组名在需要时被编译器视为指针,但本身并不是指针,例如sizeof(a)计算的是数组总长度,而非指针类型的长度。

//函数定义
int find_largest(int a[], int n)
{
	...
}
//函数调用,传入数组
find_largest(b, N);     //数组名b作为实际参数传入

结果是把 &b[0] 赋给a,数组并未被赋值。

数组型形式参数视作指针的优缺点:

  • 缺点
    • 与变量不同,传入函数后,数组本身没有被复制,因而作为实际参数的数组可能被修改。为了避免被修改,可在函数声明中使用const。如:int find_largest(const int a[], int)保护作为实际参数的数组。
  • 优点
    • 给函数传递数组的时间与数组大小无关;
    • 可以把数组型实际参数声明为指针。例如:int find_largest(int *a, int n),在函数参数表中,声明形式参数a为指针相当于声明它是数组。
    • 可以只传递数组的一部分,可以不用全部传入。如largest = find_largest(&b[5], 10);函数直接从b[5]开始检查此后的10个元素,而不必检查全部。

3、指针作为数组名
int a[5], i, sum = 0;
int *p = a;         //p指向a[0]
for(i=0; i<5; i++){
	sum += p[i];           //指针p被视为数组名a,p[i]视为*(p+i)
}
4、应用 (1)遍历数组

数组元素求和:


int a[N];
for(p = a; p < a+N; p++){
	sum += *p;
}

注意:

  • 数组名作为指针时,不得修改该指针的值(非法)。
  • 例如int a[]; a++;是错误代码。可以将a复制给另一个非数组名p的指针再修改p的值。

数组元素逆序输出:

for(p=a+N-1; p>a; p--){
	printf(" %d", *p);   //只逆序输出,未逆序存储
}
printf("\n");
(2)数组元素逆序存储

见此文第二章第2节【C语言补充】数组元素逆序(反向)


四、指针与多维数组 1、处理多维数组的元素
处理方法:对于二维数组而言,可以将二维数组当作一维数组来处理。

r 行数组的存储方式:

举例: 对二维数组所有元素初始化为0

int a[row][col];
int *p;

//正确写法1:
for(p=&a[0][0]; p<= &a[row-1][col-1]; p++){
	*p = 0;
}
//正确写法2:
for(p=a; p<= &a[row-1][col-1]; p++){
	*p = 0;
}

//错误写法1:元素越界,不存子啊a[row][]和a[][col]
for(p=a; p<= &a[row][col]; p++){
	*p = 0;
}
//错误写法2:原因见本章第4节
for(p = a; p < a+ROW*COL; p++){
		*p = 1;
	}

注意: 与一维数组不同,不存在a[ROW][]a[][COL],因此不能写成p < a[ROW][COL]


2、处理多维数组的行 (1)概念
p = &a[i][0];         //访问第i行第1个元素

或简写为:

p = a[i];             //访问第i行第一个元素

说明:

  • 对于任意数组a而言,a[i]等价于*(a+i)。则&(a[i][0])等价于&( *(a[i]+0) ),又等价于&( *a[i] ),等价于a[i]
  • 即,对于数组,a是指向数组a的第一个元素的指针。对于二维数组a[i]是指向第i行的第一个元素的指针。
(2)应用:对第 i 行清零
//对数组第i行清零
int a[ROW][COL], i;
int *p;
...
for(p=a[i]; p<a[i]+COL; p++){
	*p = 0;
}

注意:对于二维数组a[ROW][COL]a[0]指向第0行的首个元素。


3、处理多维数组的列
//对数组第j列清零
int a[ROW][COL], j;
int (*p)[COL];          //p声明为指向该数组整行的指针
...
for(p=&a[0]; p< &a[ROW] ; p++){
	(*p)[j] = 0;        //每行的第j个元素赋0
}

说明:

  • *p[COL]等价于*(p[COL])是指针数组,而(*p)[COL]是指向数组(长度为COL)的指针。
  • p++;将指针移到下一行的开头。
  • (*p)[i]代表a的一整行。

4、用多维数组作为指针 (1)概念
  • 对于一维数组,a是指向a[0]的指针。
  • 对于二维数组,a是指向a[0](第0行),而a[0]才是指向a[0][0]的指针。

  由行主序存储原则,C语言视其为一维数组(长度为行数),数组的每个元素也是一维数组(长度为列数)。

  • 对于int a[n],a的类型是int *(指向整型变量的指针);
  • 对于int a[ROW][COL],a的类型是int (*)[COL](指向长度为COL的整型数组的指针,也即指向数组某一行的指针)。
(2)应用:将二维数组第 j 列清零
//方法1:
for(p=&a[0]; p< &a[ROW] ; p++){
	(*p)[j] = 0;        //每行的第j个元素赋0
}

//方法2(简化):
for(p=a; p< a + ROW ; p++){
	(*p)[j] = 0;        //每行的第j个元素赋0
}
(3)多维数组作为函数实际参数

对于找数组最大值的函数find_largest(int a[], int n)

错误写法:

largest = find_largest(a, ROW*COL);        //a不是int *类型,与参数表要求不符
largest = find_largest(a[0][0], ROW*COL);  //a[0][0]不是int *类型

正确写法:

largest = find_largest(a[0], ROW*COL);   //a[0]指向第0行的首个元素

五、指针与变长数组 1、一维变长数组
void f(int n)
{
	int a[], *p;
	p = a;
	...
}

p指向一维数组中的元素

2、二维变长数组
void f(int m, int n)
{
	int a[m][n], (*p)[n];
	p = a;
	...
}
  • 对于多维变长数组,指针类型取决于除第一维之外的其他每一维的长度。
  • 因为p依赖于n,而n是变量,则p具有变量修改类型。
  • 变量修改类型的声明必须出现在函数体内部或函数原型中。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/914927.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存