由数组名做参数所引出探讨

目录

一、阅读资料

二、 心得体会

2.1 不“退化”

2.2 退化

三、 代码实践

四、 补充 

4.1 情形1 

4.2 情形2 

4.3 情形2的改正 


一、阅读资料

首先这些资料也值得去了解下:

强力推荐:数组和指针——都是“退化”惹的祸,这篇博客有涉及到编译链接原理。


数组名降级 数组退化为指针 问题

辨析指针常见误区

数组的指针特性——数组名何时不退化成指针?何时退化成指针

extern关键字有无存在必要


二、 心得体会

2.1 不“退化”

仅在以下3种情况中,数组名不会“退化”为指针。

  •  sizeof(数组名);
  •  对数组名进行取地址操作:&arr;
  •  使用字符串初始化数组;如char arr[] ="Wang";

2.2 退化

其他情况下都会“退化”(退化后就具有常性不可改变


一、规律1:(常用)数组名做函数参数时,arr都会退化为&arr[0]指针;

情形1: void foo(int* p[4]):形参p被解释成什么呢?int **p(二级指针)。
解释:p是一个一维指针数组,作形参时发生退化,变成指向数组首元素的指针,由于数组首元素是一级指针,故p就自动转变成二级指针。

情形2: void foo(int p[][5]):形参p被解释成什么呢?int(*p)[5],这个结果仍然符合规律。
解释:p是一个二维数组,作形参时发生退化,变成指向数组首元素的指针,由于数组首元素是一个一维数组,故p就自动转变成指向一维数组的指针,即int(*p)[5]。



   二、规律2: 形参的形式为int *p、int p[]或int p[N]这三种写法都是允许的,但是最终都被编译器解释为int *p。

三、 代码实践

#include<stdio.h>
int MySize(char arr[])	
{
	return sizeof(arr);
}
int main()
{
	char arr[] = { 'W','a','n','g','F','u','L','u','i','\0' };
	const char *p = "WangFuLui";
	char *q = arr;
	printf("sizeof(arr)=%d\n", sizeof(arr));
	printf("sizeof(p)=%d\n", sizeof(p));
	printf("sizeof(q)=%d\n", sizeof(q));
	printf("MySize(q)=%d\n", sizeof(q));
	return 0;
}
运行结果

四、 补充 

C语言中的数据类型

4.1 情形1 

//file1.c

int a;

//file.c

extern int a;

 分析:

  1.  编译: file1.c和file2.c经过编译后,生成file1.o和file2.o文件;
  2.  符号重定位:在file2.o的符号表中,变量a的地址是尚未解析的(也可以说是尚未确定的);file1.o和file2.o经过链接后,file2.o中变量a的地址才被确定下来,假设是0xbf8eafae;
  3.  对地址的解释:file2.o对该地址的使用,是按照声明extern int a;进行的,比如会进行一个a=2的操作,那么相应的伪代码就是:         *((int*)0xbf8eafae)=2。毫无疑问,这是正确使用了file1.o中的变量a。

4.2 情形2 

//file1.c

char s[10];

//file2.c

extern char *s;

分析:

  1.  编译: file1.c和file2.c经过编译后,生成file1.o和file2.o文件; 
  2.  符号重定位: 在file2.o的符号表中,变量s的地址是尚未解析的(也可以说是尚未确定的);file1.o和file2.o经过链接后,file2.o中变量s的地址才被确定下来,假设是0xbf8eafae
  3.  对地址的解释: file2.o对该地址的使用,是按照声明extern char *s;进行的,比如会进行一个*s=2的操作,那么相应的伪代码就是:   *(*((char **)0xbf8eafae)) 。里层*((char **)0xbf8eafae)何解? 将0xbf8eafae为始址的4个字节(32位机)作为一级字符指针,外层和里层统一起来看就相当于file1.o中s[0], s[1], s[2], s[3]拼接成的这个地址指向的内存赋值为2,很明显是不对的,对地址做出了错误的解释。地址是没有意义的,需要将其和类型结合起来看才可以。

4.3 情形2的改正 

注意和情形2作比较,一点小差错,就可能导致程序发生莫名其妙的错误,而且一般还查不出来;特别是程序居然能不报错运行出来但就是不能得到预期的结果。

//file1.c

char s[10];

//file2.c

extern char s[];

分析:

  1.  编译 : file1.c和file2.c经过编译后,生成file1.o和file2.o文件;
  2.  符号重定位: 在file2.o的符号表中,变量s的地址是尚未解析的(也可以说是尚未确定的);file1.o和file2.o经过链接后,file2.o中变量s的地址才被确定下来,假设是0xbf8eafae
  3.  对地址的解释: file2.o对该地址的使用,是按照声明extern char s[];进行的,比如会进行一个*s=2的操作,那么相应的伪代码就是: *((char (*)[])0xbf8eafae)=2;   0xbf8eafae被解释成一个数组地址,那么如何将 0xbf8eafae解释成一个数组的地址?将其强转成指针数组类型即可,编译器就会知道从0xbf8eafae开始向下连续的空间被分配给一个字符数组了(值得注意的是,在file2.o中使用该数组可能会越界,因为无从知道该数组的大小)。数组地址又退化成&s[0](回顾数组名不退化的三种情况),进行解引用效果上相当于令数组下标为0的元素的值变为2。

猜你喜欢

转载自blog.csdn.net/qq_41822235/article/details/81428584