0%

浅析C语言数组与指针

C语言中数组与指针是不可分开而论的,下面先说说数组T a[n]。

1.数组的访问方式有两种:指针形式(a+i)和下标形式a[i]。而编译器对这两种方式的操作解析是一样:把a作为数组首元素的首地址,然后再加上i作为地址偏移,算出新的地址,然后从新的地址中取出值。而对于偏移的计算为isizeof(T)。如果计算过程反过来,也就是i[a]也具有同样的含义。

2.a表示什么?对于上面两种情况可以知道a表示数组首元素的地址。那么a还有其它的意义吗?当然,sizeof(a)=n*sizeof(T)。这里a表示数组的地址(把数组看成一个整体),而不是首元素的地址。实际上除了作为sizeof()的参数外,a都表示数组的首元素的地址。

3.&a表示什么?从2知道a表示数组首元素的地址,则&a表示数组的地址。看下面的代码
1
2
3
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf(“%d, %d”, *(a+1), *(ptr-1));

a+1中a表示数组首元素a[0]的首地址,a+1则为a+sizeof(int)
&a+1中&a表示数组的首地址,地址为a+sizeof(a)相当于a+5
则上面的结果不言而喻:2,5

4. 二维数组扩展
根据前面说的,二维数组可以看成一个一维数组,其中的每个元素是另外一个一维数组。多维数组类似。设二维数组T a[m][n],访问a[i][j]编译器如何解析呢?根据前面说的,先看成一维的:a+isizeof(a[k]),a[k]表示看成一维数组的元素,根据1所述,上式结果为a+i(nsizeof(T)),再考虑到[j],则a[i][j]表示地址a+insizeof(T)+j*sizeof(T)里存储的值。也可以看成(*(a+i)+j)下面看俩个例子

1
2
3
4
5
6
7
8
//Test1
int A[5][5];
int *p = A;
//test2
int A[5][5];
int (*p)[4];
p = a;
//求&p[4][2]-&a[4][2]的值

第一个例子是非法的,因为A是一个二维数组,即“数组的数组”,A会被转换为一个指向数组的指针;而p是一个指向整形变量的指针。赋值运算符左右类型不相容。正确的方法应该是第二例子的方法。
第二个例子的结果按我们上面说的&p[4][2]表示的是&a[0][0]+44sizeof(int)+2sizeof(int),&a[4][2]表示的是&a[0][0]+45*sizeof(int)+2sizeof(int),结果也就是4.

指针

1.我们知道指针的访问形式和数组一样也有上述两种形式。而且对于两种访问形式编译器的动作也是一样的。如下列代码

1
2
char *p = “abcdefg”;
c = p[i]

编译器符号表具有一个p,假设地址为X1。运行时步骤1:取地址R1的内容,假设为R2。步骤2:取得i的值,并将其与R2相加。步骤3:取地址[R2+I]的内容。对比可知,指针的访问比数组的访问多一步,但编译器对其
最后的处理是一样。这个性质在后面很有用。如我们在一个文件中定义了数组int a[10],而我们在另外一个文件中要引用这个数组,那么如果声明为extern int *a。由于编译器对指针和数组的操作解析是不一样的,
所以最后得不到我们想要的结果,所以是非法的。二维指针或者多维指针类似于多维数组。

2.指针变量本身传递给函数了吗?
先看下面一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
void GetMemory(char *p, int num)
{
p = (char *)malloc(num*sizeof(char));
}

int main()
{
char *str = NULL;
GetMemory(str, 10);
strcpy(str, "hello");
free(str);
return 0;
}

问题出在什么地方,经过调试我们发现strcpy的时候str还是为NULL,也就是说GetMemory函数没有作用。回想函数的参数传递,我们知道str并没有被改变,改变的是str的一份拷贝,malloc分配的空间是位于堆中,
并没有返回给str。那么这个问题如何解决呢?
一种方法是使用return将分配的地址返回给str,这在C语言课上swap的例子都被讲烂了。
另一种方法是使用二级指针

1
2
3
4
5
6
7
8
9
10
11
12
13
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num*sizeof(char));
}

int main()
{
char *str = NULL;
GetMemory(&str, 10);
strcpy(str, "hello");
free(str);
return 0;
}

这里我们传递是str的指针,也就是二级指针,其值就是str的值。

3.野指针
“野指针”不是NULL指针,是指向“垃圾”内存(不可用内存)的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if无法判断一个指针是正常指针还是“野指针”。
所以在日常编程中,指针不用了之后置为NULL。

关于指针的还有很多东西可以谈,但是只要我们记住上面这些基本的东西,一步一步分析总可以分析出来。另外关于指针和数组的异同在《C专家编程》中已经有了详尽的阐述,这里就不做无谓的体力活了。这篇本来应该
昨晚发的,anyway, 周末愉快!