C语言学习之七——指针_2_数组

字数 2280阅读 51

!!!注意:因为简书的显示格式缘故,所以“ * ”显示会出现问题,可能有些星号由于疏忽未改动格式,造成没有显示,请多多包含,如有错误,请留言或联系本人更正,谢谢!

  1. 指针和数组
    1.1通过指针得到整个数组:由于数组是由各个具有相同性质的数组元素按照一定顺序排布的集合,而每个数组元素都占一定的内存空间,且由数据结构知识可知,数组元素在内存中的排布是连续的。因此,只要知道数组的首地址,那么整个数组就可以被知晓。由此可知,只需要将指针指向数组首地址,就可以按照地址顺序知道整个数组。

    1.2数组元素的指针:即为指向数组元素的指针(换句话说,就是数组元素的地址)。见例1

//例1,定义一个指向数组元素的指针变量
int a[5] = {1,2,3,4,5}; //定义数组a
int *p; //定义指针变量p
p = &a[2]; //将数组a的第三个元素的地址赋给p(即p中存储数组a的第三个元素的地址)

引用数组元素的方法:1)下标法,如a[i]; 2)指针法,如(a+i)或(p+ i)。(注:i为第i+1个元素)

//例2
/#include<stdio.h>
void main()
{
int a[5] = {1,2,3,4,5};
int *p, i;
p = a; //这里a是数组名,p是指向数组元素的指针变量,注意:数组名即“翻译成数组的第一个元素的地址!因此将数组的第一个元素的地址赋给p,后面的数组元素就可以通过地址+1得到。
//p = &a[0]; //这也是同样道理,因为a[0]的地址即为数组的首地址。
for(i = 0; i < 5; i++)printf("%d ", *(p + i));
printf("\n");
}

例题:假设有一个a数组,整型,有10个元素。要输出各元素的值有三种方法,见例3.
(1) 下标法
(2) 通过数组名计算数组元素地址,找出元素的
值。
(3) 用指针变量指向数组元素。

//例3
/#include<stdio.h>
void main()
{
int a[5] = {1,2,3,4,5};
int *p, i;
for(i = 0; i < 5; i++)printf("第一种方法:%d ", a[i]);
printf("\n");
for(i = 0; i < 5; i++)printf("第二种方法:%d ", *(a + i));
printf("\n");
p = a; //p = &a[0]也是等价的
for(i = 0; i < 5; i++)printf("第三种方法:%d ", *(p + i));
printf("\n");
}
//输出:第一种方法:1 第一种方法:2 第一种方法:3 第一种方法:4 第一种方法:5
第二种方法:1 第二种方法:2 第二种方法:3 第二种方法:4 第二种方法:5
第三种方法:1 第三种方法:2 第三种方法:3 第三种方法:4 第三种方法:5

  1. 用数组名作函数参数
    2.1 f(int arr[], int n) 与 f(int *arr, int n),这两个是等价的,因为在编译时编译器将arr按照指针变量处理(因为,arr中存储的是数组的首地址,在调用函数f的时候,取的是数组arr 的首地址然后进行下面一系列运算)(注:C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。)
    见例4,例5

//例4
/#include<stdio.h>
void main()
{
int a[5] = {1, 2, 3, 4, 5};
int *p, f(int arr[], int n);
f(a, 5);
printf("///////////////////////////////////////////////////\n");
p = a;
f(p, 5);
}

int f(int arr[], int n)
{
int i;
for (i= 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
//输出:
1 2 3 4 5
///////////////////////////////////////////////////
1 2 3 4 5

//例5
/#include<stdio.h>
void main()
{
int a[5] = {1, 2, 3, 4, 5};
int *p, f(int *arr, int n);
f(a, 5);
printf("///////////////////////////////////////////////////\n");
p = a;
f(p, 5);
}

int f(int *arr, int n)
{
int i;
for (i= 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
//输出:
1 2 3 4 5
///////////////////////////////////////////////////
1 2 3 4 5

例题:将数组a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}中10个整数按相反顺序存放
方法一,用将数组a的数组反向插入到另一个空数组中:

/#include<stdio.h>
void main()
{
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, b[10];
int reverse(int a[], int b[]);
int i;
reverse(a, b);
for (i = 0; i <= 9; i++)
{
printf("%d ", b[i]);
}
printf("\n");
}

int reverse(int a[], int b[])
{
int i, j;
for (i = 0, j = 9; i <= 9 && j >= 0; i++, j-- )
{
b[j] = a[i];
}
return b[0];
}
//输出:10 9 8 7 6 5 4 3 2 1

方法二,将数组的末元素地址赋给一个数组变量,然后依照地址数-1的顺序得到反向的数组元素,并依次存入另一个空数组中。

/#include<stdio.h>
void main()
{
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, b[10];
int *p, i;
void reverse(int *p1, int b[]);
p = &a[9];
reverse(p, b);
for (i = 0; i <= 9; i++)
{
printf("%d ", b[i]);
}
printf("\n");
}

void reverse(int *p1, int b[])
{
int i;
for (i = 0; i <= 9; i++)
{
b[i] = *(p1 - i);
}
}
//输出:10 9 8 7 6 5 4 3 2 1

2.2归纳:如果有一个实参数组,想在函数中调用这个实参数组,实参与形参的对应关系有以下4种情况:
1)形参和实参都用数组名,如:

void main()
{
int a[10];
void f(int x[],int n) //函数声明
f(a,10); //函数调用,这里传入的参数都是实参
}

void f(int x[],int n) //函数定义,这里定义的参数都是实参
{
……
}

2)实参用数组名,形参用指针变量。如:

void main()
{
int a[10];
void f (int *a, int n);
f (a, 10);
}

void f (int *a, int n)
{
…...
}

3)实参形参都用指针变量。例如:

void main()
{
int a[10], *p;
p = a;
void f (int *x, int n);
f (p , 10);
}

void f (int *x, int n)
{
......
}

4)实参为指针变量,形参为数组名。如:

void main()
{
int a[10], *p;
p = a;
f(p,10);
}

f (int x[], int n)
{
............
}

例题:对数组中10个整数按由大到小顺序排序
/#include<stdio.h>
void main()
{
int a[10] = {99, 88, 11, 77, 56, 35, 110, 201, 5, 33};
int sort(int a[], int n);
int i;
sort(a, 10);
for(i = 0; i < 10; i++)printf("%d ", a[i]);
printf("\n");
}

int sort(int a[], int n)
{
int i, j, temp;
for(i = 0; i < (n - 1); i++)
for(j = i; j < n; j++)
{
if (a[i] < a[j])
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
//输出:201 110 99 88 77 56 35 33 11 5

  1. 多维数组与指针
    3.1设有一个二位数组a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}},且其首地址为2000,则其内存中的存储方式为:


    二维数组在内存中的存储方式

    指针及下标表示数组的方式

    3.2指向对维数组元素的指针变量:
    把二维数组a分解为一维数组a[0],a[1],a[2]之后,设p为指向二维数组的指针变量。可定义为:int ( * p)[4] (它表示p是一个指针变量,它指向包含4个元素的一维数组。若指向第一个一维数组a[0],其值等于a,a[0],或&a[0][0]等。而p+i则指向一维数组a[i]。)
    由此我们可以得出 * (p+i)+j是二维数组i行j 列的元素的地址,而 * (*(p+i)+j)则是i行j列元素的值。

    二维数组指针变量的一般定义形式:类型说明符(指针变量名)[长度] (“类型说明符”为所指数组的数据类型。“”表示其后的变量是指针类型。“长度”表示二维数组分解为多个一维数组时,一维数组的长度,也就是二维数组的列数。)见例6

//例6
/#include <stdio.h>
void main()
{
int a[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
int (* p)[4];
int i, j;
p = a;
for( i=0; i < 3; i++)
{
for( j=0; j < 4; j++)
{
printf("%2d ", * ( * (p+i)+j)); /这里的i就表示 行,j表示列,+i的时候即根据 ( * p)[4] 中4列的原则,从数组的首地址跳过4 * i个数组元素存储空间,到i行;+j的时候即从i行的起始位置算起跳过j个数组元素存储空间到j列,到此即为要找的数组元素/

}
printf("\n");
}
}

推荐阅读更多精彩内容