C语言入门(十八)数组和指针

数组和指针 

前言:

指针和数组有着密切的关系,任何能由数组下标完成的操作也都可用指针来实现,但程序中使用指针可使代码更紧凑、更灵活。

一、指向数组元素的指针

我们定义一个整型数组和一个指向整型的指针变量:

int a[10], *p;

和前面介绍过的方法相同,可以使整型指针p指向数组中任何一个元素,假定给出赋值运算

p=&a[0];

此时,p指向数组中的第0号元素,即a[0],指针变量p中包含了数组元素a[0]的地址,由于数组元素在内存中是连续存放的,因此,我们就可以通过指针变量p及其有关运算间接访问数组中的任何一个元素。

Turbo C中,数组名是数组的第0号元素的地址,因此下面两个语句是等价的

p=&a[0];

p=a;

根据地址运算规则,a+1为a[1]的地址,a+i就为a[i]的地址。

下面我们用指针给出数组元素的地址和内容的几种表示形式:

(1). p+i和a+i均表示a[i]的地址, 或者讲,它们均指向数组第i号元素, 即指向a[i]。

(2). *(p+i)和*(a+i)都表示p+i和a+i所指对象的内容,即为a[i]。

(3). 指向数组元素的指针, 也可以表示成数组的形式,也就是说,它允许指针变量带下标, 如p[i]与*(p+i)等价。

假若: p=a+5;

则p[2]就相当于*(p+2), 由于p指向a[5], 所以p[2]就相当于a[7]。而p[-3]就相当于*(p-3), 它表示a[2]。

二、指向二维数组的指针 

1.二维数组元素的地址

为了说明问题, 我们定义以下二维数组:

int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};

a为二维数组名,此数组有3行4列, 共12个元素。但也可这样来理解,数组a由三个元素组成:a[0],a[1],a[2]。而每个元素又是一个一维数组, 且都含有4个元素(相当于4列),例如,a[0]所代表的一维数组所包含的 4 个元素为a[0][0], a[0][1], a[0][2], a[0][3]。如图所示:

______ _______________

a---| a[0] | ____ | 0 | 1 | 2 | 3 |

|______| |___|___|___|___|

| a[1] | ____ | 4 | 5 | 6 | 7 |

|______| |___|___|___|___|

| a[2] | ____ | 8 | 9 | 10| 11|

|______| |___|___|___|___|

但从二维数组的角度来看,a代表二维数组的首地址,当然也可看成是二维数组第0行的首地址。a+1就代表第1行的首地址,a+2就代表第2行的首地址。如果此二维数组的首地址为1000,由于第0行有4个整型元素,所以a+1为1008,a+2也就为1016。如图所示

_______________

(1000) ____ | 0 | 1 | 2 | 3 |

|___|___|___|___|

(1008) ____ | 4 | 5 | 6 | 7 |

|___|___|___|___|

(1016) ____ | 8 | 9 | 10| 11|

|___|___|___|___|

既然我们把a[0],a[1],a[2]看成是一维数组名,可以认为它们分别代表它们所对应的数组的首地址,也就是讲,a[0]代表第 0 行中第 0 列元素的地址,即&a[0][0], a[1]是第1行中第0列元素的地址,即&a[1][0],根据地址运算规则,a[0]+1即代表第0行第1列元素的地址,即&a[0][1],一般而言,a[i]+j即代表第i行第j列元素的地址, 即&a[i][j]。

另外,在二维数组中,我们还可用指针的形式来表示各元素的地址。如前所述,a[0]与*(a+0)等价,a[1]与*(a+1)等价,因此a[i]+j就与*(a+i)+j等价,它表示数组元素a[i][j]的地址。

因此,二维数组元素a[i][j]可表示成*(a[i]+j)或*(*(a+i)+j),它们都与a[i][j]等价,或者还可写成(*(a+i))[j]。

另外, 要补充说明一下, 果你编写一个程序输出打印a和*a,你可发现它们的值是相同的,这是为什么呢? 我们可这样来理解:

首先,为了说明问题,我们把二维数组人为地看成由三个数组元素a[0],a[1],a[2]组成,将a[0],a[1],a[2]看成是数组名它们又分别是由4个元素组成的一维数组。因此,a表示数组第0行的地址, 而*a即为a[0], 它是数组名, 当然还是地址,它就是数组第0 行第0 列元素的地址。

2.指向一个由n个元素所组成的数组指针

在Turbo C中, 可定义如下的指针变量:

int (*p)[3];

指针p为指向一个由3个元素所组成的整型数组指针。在定义中,圆括号是不能少的, 否则它是指针数组, 这将在后面介绍。这种数组的指针不同于前面介绍的整型指针,当整型指针指向一个整型数组的元素时,进行指针(地址)加1运算,表示指向数组的下一个元素,此时地址值增加了2(因为放大因子为2),而如上所定义的指向一个由3个元素组成的数组指针,进行地址加1运算时,其地址值增加了6(放大因子为2x3=6),

这种数组指针在Turbo C中用得较少,但在处理二维数组时, 还是很方便的。例如:

int a[3][4], (*p)[4];

p=a;

开始时p指向二维数组第0行,当进行p+1运算时,根据地址运算规则,此时放大因子为4x2=8,所以此时正好指向二维数组的第1行。和二维数组元素地址计算的规则一样,*p+1指向a[0][1],*(p+i)+j则指向数组元素a[i][j]。

例:

int a[3][4]={
{1,3,5,7},
{9,11,13,15},
{17,19,21,23}
};
main()
{
int i,(*b)[4];
b=a+1; /* b指向二维数组的第1行, 此时*b[0]是a[1][0] */
for(i=1;i<=4;b=b[0]+2,i++) /* 修改b的指向, 每次增加2 */
printf(%d\t,*b[0]);
printf(\n);
for(i=0; i<3; i++)
{
b=a+i; /* 修改b的指向,每次跳过二维数组的一行 */
printf(%d\t,*(b[i]+1));
}
printf (\n);
}

程序运行结果如下:

9 13 17 21

3 11 19

三、字符指针 

我们已经知道,字符串常量是由双引号括起来的字符序列,例如:

a string

就是一个字符串常量,该字符串中因为字符a后面还有一个空格字符,所以它由8个字符序列组成。在程序中如出现字符串常量C编译程序就给字符串常量按排一存贮区域,这个区域是静态的,在整个程序运行的过程中始终占用, 平时所讲的字符串常量的长度是指该字符串的字符个数, 但在按排存贮区域时, C 编译程序还自动给该字符串序列的末尾加上一个空字符‘\0‘,用来标志字符串的结束,因此一个字符串常量所占的存贮区域的字节数总比它的字符个数多一个字节。

Turbo C中操作一个字符串常量的方法有:

(1).把字符串常量存放在一个字符数组之中, 例如:

char s[]=a string;

数组s共有9个元素所组成,其中s[8]中的内容是‘\0‘。实际上,在字符数组定义的过程中,编译程序直接把字符串复写到数组中,即对数组s初始化。

(2).用字符指针指向字符串,然后通过字符指针来访问字符串存贮区域。当字符串常量在表达式中出现时,

根据数组的类型转换规则,它被转换成字符指针。因此,若我们定义了一字符指针cp:

char *cp;

于是可用:

cp=a string;

使cp指向字符串常量中的第0号字符a, 如图所示。

___________________________________

CP ----- | a | | s | t | r | i | n | g | \0|

|___|___|___|___|___|___|___|___|___|

以后我们可通过cp来访问这一存贮区域, 如*cp或cp[0]就是字符a,而cp[i]或*(cp+i)就相当于字符串的第i号字符,但企图通过指针来修改字符串常量的行为是没有意义的。

四、指针数组 

因为指针是变量,因此可设想用指向同一数据类型的指针来构成一个数组, 这就是指针数组。数组中的每个元素都是指针变量,根据数组的定义,指针数组中每个元素都为指向同一数据类型的指针。指针数组的定义格式为:

类型标识 *数组名[整型常量表达式];

例如:

int *a[10];

定义了一个指针数组,数组中的每个元素都是指向整型量的指针,该数组由10个元素组成,即a[0],a[1],a[2], ..., a[9],它们均为指针变量。a为该指针数组名,和数组一样,a是常量,不能对它进行增量运算。a为指针数组元素a[0]的地址,a+i为a[i]的地址,*a就是a[0],*(a+i)就是a[i]。

为什么要定义和使用指针数组呢?主要是由于指针数组对处理字符串提供了更大的方便和灵活,使用二维数组对处理长度不等的正文效率低,而指针数组由于其中每个元素都为指针变量,因此通过地址运算来操作正文行是十分方便的。

指针数组和一般数组一样,允许指针数组在定义时初始化,但由于指针数组的每个元素是指针变量,它只能存放地址,所以对指向字符串的指针数组在说明赋初值时,是把存放字符串的首地址赋给指针数组的对应元素,

例如下面是一个书写函数month_name(n),函数返回一个指向包含第n月名字的字符指针(关于函数指针和指针函数,下一节将专门介绍)。

例: 打印1月至12月的月名:

char *month_name(int n)
{
static char *name[]={
Illegal month,
January,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
};
return((n<1||n>12)?name[0]:name[n]);
} 

main()
{
int i;
for(i=0; i<13; i++)
printf(%s\n, month_name(i));
}

对于指针这一节,一定要多练习一些题。指针是一个很重要的概念,必须多接触实际的问题才能掌握它。

时间: 2024-10-21 01:05:22

C语言入门(十八)数组和指针的相关文章

[WebGL入门]十八,利用索引缓存来绘图

注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正. 本次的demo的运行结果 对应复杂的模型 上次,通过操作模型坐标变换矩阵,实现了多个模型的移动,旋转和放大缩小.但是,渲染的依然是简单的三角形,是个构造及其简单的模型.但是实际中,用WebGL来绘制一个简单的三角形的机会是很少见的.至少是个四角多边形吧,通常会是更复杂的模型.伴随着模型的复杂化

《你必须知道的495个C语言问题》笔记--数组和指针

一.如何动态分配多维数组? 1.分配一个指针数组,然后把每个指针初始化为动态分配的行 代码如下: int **array = (int **)malloc(ROW * sizeof(int*)); int i = 0; for(i=0; i<ROW; i++){ array[i] = (int *)malloc(COL * sizeof(int)); } 2.让数组的内容连续,但在后来重新分配行. 代码如下: int **array = (int**)malloc(ROW * sizeof(in

C语言其实不简单:数组与指针

之前在写C的时候,没怎么留意数组,就这么定义一个,然后颠来倒去的使用就行了.不过后来碰到了点问题,解决后决定写这么一篇博客,数组离不开指针,索性就放一起好了. 现在我定义了一个数组:int cc[10]; 围绕这个数组有好几种指针:cc, cc+1, &cc[0], &cc, &cc+1等等.你知道它们都是什么含义吗?试试运行以下带代码: #include <stdio.h> int main() {     int cc[10];     printf("%

C语言入门基础之数组——数学和编程的完美结合(图)

C语言入门基础之数组--数学和编程的完美结合(图)姐姐又来更新了,刚刚过完周末,是不是玩的很开心啊,放松后我们来学习新知识吧!现在我们要讲数组啦,编程往往和数学紧密联系着,想必在我们高中有学习过集合吧.那么在编程中我们怎么表示集合呢?刚学编程的小可爱可能学到变量时候发现,如果我需要用很多变量存储同意作用的值,需要大量的变量.现在让小姐姐给你介绍下二维数组吧,小可爱可要认真学了哦!记得给小姐姐点赞,?( ′???` )比心大家都玩抖音吧,我们每个人都有一个抖音号,我们还会关注一些小姐姐的抖音号,那

C++ Primer快速入门之六:数组和指针

更新:勘误,delete [] 猪 我们知道,C语言以及早期的面向结构的语言几乎都支持数组定义,比如整形数组int 女神[2],表示有俩数: 女神[0], 女神[1],她们都是整数.C++ 语言为了效率和兼容考虑,也支持这个.当然了,毕竟是别人家的孩子(标准),并不提供类似于 vector 和迭代器类型那些神奇的能力.与 vector的区别在于,数组的长度是固定的.另外呢,指 针还是那个指针,没有变化. ? 我们编程时候,尽量使用 vector,避免使用数组和指针.有了vector和她的iter

C++ Primer高速入门之六:数组和指针

更新:勘误,delete [] 猪 我们知道,C语言以及早期的面向结构的语言差点儿都支持数组定义.比方整形数组int 女神[2].表示有俩数: 女神[0], 女神[1].她们都是整数. C++ 语言为了效率和兼容考虑,也支持这个. 当然了,毕竟是别人家的孩子(标准).并不提供类似于 vector 和迭代器类型那些奇妙的能力.与 vector的差别在于.数组的长度是固定的.另外呢,指 针还是那个指针,没有变化. ? 我们编程时候.尽量使用 vector,避免使用数组和指针.有了vector和她的i

C语言--&gt;(十二)高级指针

知识点: 1.指针数组 指针数组 指向数组的指针 2.返回指针的函数 3.指向函数的指针 block 4.空类型指针 (通用类型指针) 5.二级指针 ==============================指针数组 一个数组,若其元素均为指针类型数据,则该元素称指针数组,也就是说指针数组中每一个元素都存放一个地址,相当于一个指针变量. 1.什么是指针数组(指针的集合) 1)存储指针的数组 2)所有元素均为指针的数组 2.本质 1)指针数组本质仍然为一个数组 2)每一个存储元素均为指向相同类型

C语言--指向多维数组的指针和指针数组

#include <stdio.h> //void show(char *p[]); void show(char s[][10]); int main(){ char s[3][10]={"123","abc","xyz"}; char *p[10]; //指针数组要循环复制 p[1] = s[1]; char (*ps)[]; ps = s; } #include <stdio.h> #include <stri

程序猿之---C语言细节30(数组和指针操作测试题,你能全答对吗??)

主要内容:数组和指针操作测试 #include <stdio.h> int main() { int a[5] = {1,2,3,4,5}; /* 你能答对最后一条语句的输出吗?*/ int *ptr1 = (int *)(&a+1); int *ptr2 = (int *)((int)a+1);  printf("&a = %d\n",(&a)); printf("(&a+1) = %d\n",(&a+1));

十八、函数指针高级(函数指针数组)

1.int (*p[3])(int , int ) = {0}; 可改为: typedef int(*PFUN)(int , int); PFUN p[3] = {0}; p[0] = maxValue; p[1] = sum;