C语言指针和数组

今天突然发现了个奇怪的问题,下面这段代码竟然输出的值都是一样的,代码如下:

#include<stdio.h>

void main()
{
	char a[10]="012345678";
	printf("%d\n",&a[0]);
	printf("%d\n",a);
	printf("%d\n",&a);
}

第一个和第二个printf语句输出地址一样的,能很好理解,但第三个竟然与第二个一样,我就迷茫了,后来查了很多资料,才搞清楚为什么。先看下面的一段程序:

#include<stdio.h>

void main()
{
	char a[10]="1";
	printf("a=%d\n",a);
	printf("a+1=%d\n",a+1);
	printf("&a+1=%d\n",&a+1);
	printf("&a+2=%d\n",&a+2);
}

输出结果为:

虽然a&a是同一个地址,但是各自加1,却不同了。

a:是数组首元素的地址。所以加1,是a+sizeof(char)即为a[1]的地址。

&a:是数组的地址。1,则为&a+一个数组的所占的内存空间即&a+10*sizeof(char)

好,我们再来看下二维数组中的情况。

#include<stdio.h>
void main()
{
	int a[3][2]={{1,2},{3,4},{5,6}};
	printf("a=%d\n",a);
	printf("&a=%d\n",&a);
	printf("&a[1]=%d\n",&a[1]);
	printf("a+1=%d\n",a+1);
	printf("*(a+1)=%d\n",*(a+1));
	printf("&a+1=%d\n",&a+1);
}

输出结果:

我们知道二维数组是一维数组的数组,如我们的例子中,a是一个含有3个元素的数组,每个元素分别为a[0],a[1],a[2],而其中每个a[i]又是包含5个元素的数组。这样对于我们上面的输出就很好理解了。a&a地址肯定一样的,从我们上面一维数组的例子中知道a+1就是a[1]元素的地址,而&a+1将移动的是整个二维数组占的内存空间的大小。

对于二维数组,我们可以这样理解:

a[i] : 是第i行的首元素的地址,即a[i][0]的地址。所以a[i]+j就是第i行第j个元素的地址。

a+i : 是第i行的首地址(注意较上面少了“元素”)。此时a+i+j就是第i+j行首地址。我们

 可以通过*来将该行首地址该为该行首元素地址,如*(a+i)则为第i行的首元素的地址。

&a是整个二维数组的首地址,所以&a+1指针将移动整个二维数组占的内存大小。

注意:虽然就单纯地址的数值上来说,&a[0][0]a[0]a&a都一样,但是意义却是不同的。特别注意a[i]a+i,虽然数值上等价,但是意义很不同,尤其体现在+i这样的操作上。

由以上知识我们可以有如下几种方式访问二维数组中的a[i][j]的地址和数据

               地址                                 数据 

(1)    &a[i][j]                              a[i][j]

(2)    a[i]+j                              *a[i]+j 

(3)  *a+i+j                    **a+i+j

二、数组“退化”为指针

C99中原话是这样说的:Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type “array of type” is converted to an expression with type “pointer to type” that points to the initial element of the array object and is not an lvalue.

除了以下三种情况外,数组名都将”退化”为指向数组首元素的指针

(1)、sizeof 运算符操作(sizeof(str)为:数组的长度*sizeof(DataType))

(2)、运算符(&str为真个数组的首地址)

(3)、用字符串初始化数组时 如char str[]=”12345”

举一个例子:

#include<stdio.h>

void test(int a[5])
{
	printf("%d\n",sizeof(a));
}

void main()
{
	int a[5];
	printf("%d\n",sizeof(a));
	test(a);
}

输出结果为:

20

4

第一个输出用sizeof时,a并没有退化为指针,而第二个数组名作为函数参数传递时,就会退化为指针。

来一个二维数组的例子:

#include<stdio.h>

void test(int a[2][3])
{
	printf("%d\n",a);//第0行的首地址
	printf("%d\n",*a); //a[0][0]元素的地址
	printf("%d\n",(a+1)); //第1行的首地址
	printf("%d\n",*(a+1)); //a[1][0]元素的地址
	int *p = (int *)a; //将第0行的首地址转为a[0][0]元素的地址
    //注意二维数组名传进来还是一级指针并不是二级指针
}

int main(int argc, char * argv[])
{

	int a[2][3]={1,2,3,4,5,6};
	test(a);
	return 0;
}

最后int *= (int *)a,必须加强制类型转换,注意的是,我们说数组作为参数,会”退化”指向其第一个元素的指针。对于二维数组,它是一维数组的数组,如我们上面的a[2][3],它是一个只有两个元素的数组,只不过其中的每个元素又是一个包含三个元素的地址。此时a转换为指针,是指向a[0]行的地址,并不是a[0][0]的地址,虽然两者值相同,但意义是不一样的。虽然如直接将a赋值给p,不加强制类型转换,是会报错的。因为p只能指向一个元素的地址,而此时a相当于一行数据的首地址。

本文下载地址:http://www.kuaipan.cn/file/id_63913550065731418.htm








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值