【数组】
C语言中数组名表示该数组的起始地址,即给数组本身对应的值就是一个地址,而数组中的值就是从起始地址开始的不同的地址内的值。
如:char c[9]; //定义时的数组char c[5]中的c和运算时的c是一个含义,都是数组首地址
scanf("%s",c);
printf("%d",c); // printf(%s,c)
输入:china 输出:2686675。输出的是字符数组首地址。
若换为后面一个,输出china,输出china。字符数组按照%s格式化输出时会自动输入/输出直到‘/0’,
【数组作为函数参数】
首先我们比较下变量作函数参数和数组作函数参数的区别
如:
void main(){ int a,b; sum(a,b); } sum(int x,int y){ int sum; sum=a+b; }
实参变量类型为int,形参变量类型也为int,实参由自己的地址,形参也有而且和实参不同,实参赋值给形参,形参在函数中进行运算,运算的过程和结果与实参无关,在整个过程中实参和形参是独立开的,执行完函数后形参变了,实参无变化。
再来看下数组作为变量函数
如:
#include <stdio.h> void main () { void sum(int p[]); int a[5]={0,1,2,3,4}; sum(a); } void sum(int p[5]){ // (int p[]) // (int *p) int i; for(i=0;i<5;i++){ printf("%d,\n",p[i]); } }
我们知道数组名代表着数组首地址,所以实参类型为地址,同时我们也知道实参和形参间的沟通是通过赋值进行的,那 什么类型的变量中存放的地址?指针变量,所以形参的类型就是指针变量,实际上C对形参数组的大小是不做检查的,就是说[]之间写什么都行就算是写p[0]、p[1]都可以。另外,在形参数组中,可以用int *p替换int p[],因为数组只是指针变量的一种表示,我们直接写指针变量当然可以~。
[数组可以看做是一种特殊的指针变量]
一般的指针变量中存放的是另一个数据的地址,而数组中存放的是它自己的首地址;
一般的指针变量有自己的地址,而数组的地址就是它的首地址;
一般的指针变量中存放别人的地址,而数组的指针变量存放自己的首地址;
一般的指针变量是可以随着地址变化而变化的,而数组不灭,地址(=首地址)不变。
指针变量作为数组(函数形参)存在时,看起来是是数组(常用),其实是指针变量(通用);
指针变量无法转换成数组
[指针变量和数组的诗]
指针变量指向别人,数组指向自己
指针变量有它的载体且不同于它指向的东西,数组变量的载体就是它自己;
指针变量依靠别人活着,数组依靠自己活着;
指针变量随着别人变化而变化,数组坚守自己的内心;
指针变量和数组在一起聊得很好,就像是和数组一类人,但是回到家,指针变量依然是指针变量;
指针变量很羡慕数组,可它永远成不了数组。
[为什么指针变量可以表示数组形参要用数组表示]
int *p初始时等价于int a[],这是因为a初始时等价于一个不变的指针变量。虽然我们写void sum(int p[]),但它不真正是个数组,它是指针变量!数组地址是不能变的,写成数组是为了好理解,我们也可以理解为这是一个地址可以变化的数组。(奇葩)
[数组和指针变量关系]
其实已经很清楚了,数组和指针变量都是人,但不是一类人...在数组作形参时,指针变量伪装成数组,本质还是指针变量。
[数组形参可以变么]
在实参中,数组名是指针常量,不可变;赋值后形参初值还是数组首地址,但是它是可以变化的指针变量!
[最重要的]
变量作参数和数组作参数:形参变量和实参变量是独立的内存空间,形参数组和实参数组共用一段内存单元。
【指针和指针变量】
指针变量=地址变量,里面存储的是指针,所以说指针变量的值是指针(地址);而指针是变量的地址。*加在变量之前代表该变量指向的变量。
如:int *pointer;int i;
定义指针变量在变量名前需加上*,而左端的int是定义指针变量必须指定的“基类型”即此指针变量可以指向的变量的类型。这里基类型为int,就必须使得指针变量中的指针指向的地址的内存空间存放的是int类型的数据。指针变量类型不限。
在上式中,pointer是指针变量,*pointer是整形变量即pointer指针变量中的指针指向的地址的变量类型是整形变量,*pointer的值就是pointer指向变量的值,pointer指针变量的值是指针地址。
指针变量的赋值:<1>指针变量中只能存放地址<2>赋值给指针变量的变量地址只能是和指针变量指向变量类型一致的变量地址。
int *pointer;*pointer=3;
前者是定义整形变量,后者是把3赋值给pointer指向的变量。
【指针变量作实参调用函数】
<1>通过调用函数使得实参变量的值发生了直接变化,而在main函数中不通过返回值可以使用这些改变了的值
<2>指针变量做实参执行[调用函数]的过程中,只能通过改变指针变量所指的值的数据,而不能改变指针变量的值(地址),因为实参和形参同时指向某个值,所以它们两个指针变量中的值必然相同,我们想改变必然同时需要改变这两个值,可C中实参形参间的数据传递是单向的即实参中的值是无法改变的,所以我们只能改变实参和形参所指向的地址的值。
【指针和普通数组】
<1>指针与数组
C语言中,数组名代表着数组的起始地址(而不是整个数组)它是一个指针常量,只能指向起始地址,无法改变。所以
int a[10];
<1> int *p; <2> int *p=a; <3> int *p =a[0]; //定义时的*p和运算时的p一个含义
p=&a[0]; / p=a;
第二行的三种方法等价,第三行两个语句等价,
<2>指针运算
指针可以执行的运算:+ - += a++ ++a -= a-- --a --
指针加减的含义
:如:int a[10]; int *p=a; p=p+1;
p=p+1的含义是p指向的地址由a[0]转换为a[1],这个过程中p的地址增加了4个字节(VC中的int类型所占空间)
我们假设i代表着数组a中的第i个元素,此时 a[i]=*(p+i)=*(a+i);
数组中地址之差/数组类型所占空间(vc-int -4)=两个地址间的元素个数。
综上我们可以看出,计算机输出a[i]的时候需要先把a[i]转换成*(a+i),活取内部数值,比如我们循环输出一个数组中的数制
int *p,i,a[10]={0,1,2,3,4,5,6,7,8,9}
for(i=0;i<10;i++)
printf("*d",a[i]);
每一个for循环都会活取当前a的地址,加上当前的i值,根据得到的结果去对应地址中取出数值,但是如果我们这么做
int *p,i,a[10]={0,1,2,3,4,5,6,7,8,9}
for(p=a;p<(a+10);p++)
printf("*d",*p);
这么做指针变量直接指向数组中数值,除了刚进入for循环地址的赋值,其他情况不需要考虑地址的问题,效率增加。
同时我们可以想到:a[i]=*(a+i)那么我们也可以写p[i];p[i]的含义是*(p当前地址+i),因为p是变量,所以这个容易出错。
【指针和多维数组】
<1>多维数组元素的地址
在二维数组a[i][j]中,a代表首地址,这个首地址只是[行]首地址,不是[列]首地址,比如:
x x x x
x x x x
x x x x
我们就假设它是3*4的二位,每行代表一维,现在我们把每行看成一个大的元素,那么我们现在得到一个“一维数组”,把这个一维数组横着放,这个数组首地址是a,所以a是列首地址,那么a=a[0],a=a[1],a+2=a[2]就分别代表着第一第二第三个大元素的[行]首地址了。
注:a和a[0],a+1和a[1]..仅仅是[地址开始的值相同],a,a+1,a+2代表着二位数组,是指向行的,意思就是我加1,行加1,我不加,行为0;而a[0],a[0]+0,a[0]+1,a[1],a[1]+0,a[1]+1这些都是指向列的,就是说我加1,列加1,我不加,列为0;
<2>重要语法:
<21>在指向行的指针前加“*”就转换成指向列的指针,变得具体,如a[i][j]=*(a[i]+j)=*(*(a+i)+j)。
我们从右往左看,a+i是行指针,*(a+i)是加0的列指针,*(a+i)是加i的列指针,有了行指针和列指针,去除指针指向的内存地址中的数,注意里面的是[行->列]转换指针,外面的是标准的转换成取出指向单元内容符号。
*(*(a+i)+j)中里面的*(a+i)意义就是取出a+i指针变量所指向的地址中的[范围]具体值,二维-一维.
<22>在指向列指针前面加“&”就转换成了指向行的指针,变得抽象,比如&(&a[1][2])=&(a[1])=&(*(a+1))=a+1,
&(&a[1][2])中的外面的一层&是取地址,里面的一层是把当前的[范围]具体值转换成一个抽象值。
注意:[行->列]转换a+0加上*转换成a[0],a[0]加上*转换成a[0][0],这个过程是a变化的过程,a,a[0],a[0][0]三个含义完全不同,首先1/2是行/列指针,它们地址的值相同,但含义一个是第一个行地址,一个是第一个行地址中的第一个列地址;然后和第三个比较,第三个的含义是第一个行地址中的第一个列地址的数值,这个不可知。
【字符指针变量和字符数组】
输出字符串的两种方式
<1> char string[]="I love China";
printf("%s",string);
注:字符数组按照字符串格式化%s可以完全输入/输出字符串直到‘/0’。
这种情况下:string中存放的是第一个字符的内存地址,可以对字符串数组重新赋值。
<2> char *string="I love China";
printf("%s",string);
注:定义字符指针变量,把多个字符赋值给无名数组,把数组首地址传给字符指针。
这种情况下:string指针变量作用同上,而指针指向的是字符串常量,不能改变。注意,这个时候字符串是常量不可变,但指针变量可以变化。
字符指针变量和字符数组的区别
<1>字符数组由若干元素构成,而每个元素存放一个字符,字符数组的数组名和字符指针变量作用类似。
<2>赋值方式:
字符数组只能在定义的时候整体赋值,而在赋值语句中需要对各个元素赋值,
而字符指针变量可以在任意情况下使用char *a="I love China";直接赋值。
注:字符指针变量只能赋初值,即只能赋值一次。
<3>能否直接赋值
如:int str[10];
scanf("%s",str);
这样是可以的,因为在编译的时候给数组分配内存单元,这个内存单元是没用过的,而函数名就是这个空白内存单元的首地址,首地址可知,输入数据的时候会以首地址为始写入。
如:int *str;
scanf("%s",str);
这样写勉强可以通过编译,但是有问题,str指针变量虽然分配了内存单元,但是str里面的地址未知,而输入的字符串正是以str里面的地址为首地址的,这种做法容易造成冲突。
而:int *str="I love china"
这种情况是先把字符串分配给字符数组,再把字符数组首地址传给指针变量,指针变量有内存单元和指向的地址,比较安全。
<4>指针变量可变,数组名不可变。
<5>对于可变的指针变量,如str+5的值,可以用str[5]进行表示,因为str[5]=*(str+5);
<6>字符数组元素可再赋值,字符指针变量指向字符串不可改变。
//今天学习了 数组&指针/普通&多维数组/字符指针&字符数组