指针与数组的思维导图笔记
用了两天时间认真的学习了一下c语言指针与数组的恩怨纠葛,真的是让我心力交瘁!下面直接上干货!
一、指针和一维数组
1、指针指向一维数组:
int a[10], *pa;
pa = a; //指针指向数组头,相当于a[0];
pa = &a[3]; //指针指向数组的第3个元素
2、指针做++和- -运算
和数组名a不同,pa的值可以被修改,因此可以做++和- -运算(跳过4个字节),指向下一个元素或前一个元素。配合指针运算符*,可以写出非常简练的表达式或语句。比如两个字符串(分别存储在字符数组str1和str2中)的拷贝,可以简化成如下的形式:
char *pstr1 = str1;
char *pstr2 = str2;
while(*pstr1++ = *pstr2++) ;
另外一种写法:
void MyStrcpy(char *detStr, char *srcStr)
{
while((*detStr++ = *srcStr++) != ‘\0’)
{
}
}
3、指针做关系运算
因为数组元素是连续存放的,所以指向一维数组的指针变量也可以做关系运算。关系运算主要用在循环语句的条件中,比如:
for( pa = a; pa<a+5; pa++ )
printf(“%d”, *pa);
还可以写作:
int i;
for(i=0;i<n;i++,p++)
{
printf("%d", *p);
}
二、指针和二维数组
重点是理解行指针和列指针的概念:对于二维数组a,数组名a是行指针,a+i则跳过i行,为了访问行上的元素,需要把行指针转换为列指针,方法是加*,即 *(a+i),这个地址在数值上等于a+i,但它是列指针,它加j后在列上跳过j个元素,即* (a+i)+j指向第i行第j列的元素,也就是说*(a+i)+j == &a[i][j];那么为了访问元素a[i][j](也就是取值),还需要再做一次指针运算,即,*(*(a+i)+j),它等价于a[i][j]。因为从一维数组我们知道,*(a+i)等价于a[i],所以 *(*(a+i)+j)也可以写作*(a[i]+j)。二维数组的每一行本来就可以看作是一个一维数组,数组名是a[i],所以*(a[i]+j)就是a[i]这个一维数组中的第j个元素。
数值上,它们是相等的: a == a[0] == &a[0][0] == *a。
我来简单的总结一下上面的内容:
*(a + i)+j == &a[i][j]——这是址;
*(*(a+i)+j) == a[i]+j——这是值;
还有一个需要掌握的是行指针变量 int (*p)[4],
它的优势是可以将二维数组的一行传递给某个函数。
既然有行指针就有列指针,列指针指向的是数据类型为二维数组的元素类型,因此他与同类型简单变量的指针的定义方法是一样的:int *p;
好吧,简单来说,一摞扑克牌一共是13*4=52张,就相当于我们将这摞牌分成了13行4列。
第一种摆放扑克的方法:要查找扑克牌中的一张牌的话,我们要做的就是一张一张的去找这张牌,其实就好比将这副牌看成了一位数组,那么就可以使用二维数组的列指针来寻址这些数组元素;所以列指针的定义方法和简单的变量的指针的定义方法是一样的。
从数组的第零行第零列寻址到第i行第j列则需要跳过i*n+j(其中n为一行元素的个数)因此p+i*n+j代表数组的第i行第j列,即&(p+i*n+j),*(p+i*n+j) === p[i*n+j] === a[i][j];这里的p是指向数组的指针;
第二种摆放扑克的方法就是将一副牌分成13摞,先查找这张牌在那一摞,然后在这一摞中一张一张的查找,相当于使用二维数组的行指针来寻址数组元素;
例:二维数组存储学生的成绩,每行存储一个学生,求指定学号的学生的平均成绩。
# include"stdio.h"
float ave(int(*pi)[3],int n) //定义行指针变量pi,准备接收行指针
{
int j;
float sum=0,aver;
for(j=0;j<n;j++)
{
sum+= *(*pi+j); // 在该例中*(*pi+j)等于 *(*(score+2)+j)或者 *(score[2]+j)
}
aver= sum/n;
return aver;
}
int main()
{
int score [3][3]={{1,2,3},{4,5,6},{7,8,9}};
printf("ave= %.2f\n",ave(score+2,3)); //传递行指针score+2给pi
return 0;
}
指针数组
每个元素都是一个指针值的数组。注意区别指针数组和上面定义的行指针变量(有点像函数指针和返回指针值的函数):
int * p[4]; int (*p)[4]; (前者是指针数组,后者是行指针变量)
int *fun( ) int (*p)( ) (前者是返回指针值的函数,后者是函数指针)
指针数组的每一个元素存储一个地址值,每个元素都可以当作同类型的指针变量一样去使用。使用前必须初始化。
int * p[4]; int a[4];
for(i=0;i<4; i++)
p[i] =&a[i];
指针数组主要用于字符串的处理。下面的例子中用字符指针数组指向若干个字符串,在对字符串进行排序的时候,不需要挪动字符串本身,而只需要挪动指针数组元素即可,也就是说改变指向关系即可。这提高了程序的效率。
#include "stdio.h"
#include "string.h"
#define N 150
#define MAX_LEN 10
void sortString(char *p[], int n);
int main()
{
int i, n;
char name[N][MAX_LEN]; //定义二维数组;
char *pstr[N]; //定义指针数组;
printf("How many countries?");
scanf("%d", &n);
getchar(); //读走缓冲区的回车符;
printf("Input their name:\n");
for(i=0;i<n;i++)
{
pstr[i] = name[i]; //指针数组的第i行指向二维数组的第i行;
gets(pstr[i]); //输入第i个字符串到指针数组的内存;
}
sortString(pstr, n);
printf("Sorted result:\n");
for(i=0;i<n;i++)
{
puts(pstr[i]);
}
return 0;
}
/*用指针数组做函数参数,用索引排序代替物理排序;*/
void sortString(char *p[], int n)
{
int i, j;
char *temp = NULL; //因为交换的是字符串的地址值,故temp定义为指针变量,为防止指针变量的地址出问题,所以将其定义为NULL;
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
{
if(strcmp(p[j], p[i]) < 0) //采用字符串函数strcmp来比较两个字符串的字典顺序;
{
temp = p[i];
p[i] = p[j];
p[j] = temp;
}
}
}
}
指针数组的另一个用途就是带参数的main函数:int main(int argc, char * argv[ ]),用命令行方式运行程序时,可以给main函数传递参数,参数和程序名都是字符串,字符串的个数保存在变量argc中,字符串的首地址保存在argv指针数组中。
例:从命令行输入两个整数,计算并输出它们的和:
#include “stdio.h”
#include “stdlib.h”
int main(int argc, char*argv[])
{
int a, b;
a= atoi(argv[1]);
b= atoi(argv[2]);
printf("a+b= %d\n",a+b);
}
命令行输入:add 45 67
运行结果:a+b = 112
注意:上面的输入中,字符串“add”的地址存入了argv[0],“45”的地址存入了argv[1],“67”的地址存入了argv[2]。