一. 理解指针和数组的关键是理解编译器对程序中的C做了一些什么。下面主是讲关于编译器对c程序的处理的规则。
1. 关于数组:
1.1 数组名即地址名。
1.2 不分配数组名的栈空间(即数组名不能为左值,只能为右值,不能给数组名赋值),这一点必须与指针变量相区分。
1.3 数组的分配占用栈空间。
1.4 数组做参数传递。很明确的一点,传递数组只能通过数组地址(当然,如果你把每个数组的元素做参数一一传递,好吧…..),在c 当中,对于一维数组形参表示可以为:
int printArray(int a[],int n1); 对于二维数组形参表示可以为:int printArray(int a[],int n1,int n2)或者intprintArray(int a[][3],int n1); 当然不管几维数组,都可以通过指向数组的指针来做形参: int printArray(int *a,int n1,int n2,int n3 …);
相应的将实参正确对应,即可将数组传递。
2. 关于指针
1. 地址传递的实质也是值传递,不过这个值是一个地址(地址也是一个值),编译器只有统一值传递。
2. 在c语言中,指针非常灵活,与其他后来的编程语言相比,感觉用c编程好比在用汇编在写程序(感觉真好 (: >) )。关于这一点的编程的优缺点就非常明显了。
3. 还是要讲在c中,对于数据的地址(代码也可看做数据),都是一样的,在32位下,地址都是占用4字节。基于这一点,32位地址变量可以相互赋值,同时指针的加减是通过指针类型告诉编译器的而进行地址的不同的加减(这一点让我联想到了类中重载和多态的概念,当然跟这里毫无关系,除了第一个c 编译器是用汇编写的,现在的用的gcc是用c语言写的,如果用c++ java写一个c语言的编译器,在这里用一下多态不错啊~)。
接着上面,通过指针访问程序32位下虚拟地址(通过内核分段和分页,有4GB大小虚拟地址被一个进程独享,当然对unix和linux系统中,要除去虚拟地址高地址的1Gb的内核占用,至于windows这里只能mark一下)。
c 语言的编译器可以允许指针指向用户空间的3GB虚拟地址的任何一个位置,不会提示任何错误,最多来个warning,当然这里对右值指针强制类型转换一下连warning也就没有了。当程序运行起来,不正确的指针使用会出现各种段错误(段越界、权限问题、读不可读的段、写代码段等等,这里跟内核的知识联系密切,这里只能mark一下)。当然这里还是讲的是32位下保护模式的编程,对于64位的平展模式还是望尘莫及 ,这里还是mark一下。总之,这些错误的处理是内核要做的事了。
二. 以一个实例来说明上面的内容。具体代码参阅arrayPointer.c
基本思路是:定义一个二维数组int a[2][3]并进行初始化; 定义一个指针数组
int *p[2], 一个数组指针int (*q)[3]。
1.1 将数组的地址赋给*p (可以是a,&a,*a,a[0],&a[0],&a[0][0]);
1.2 将数组的地址偏移3个数据元素赋给*(p +1) = (int *)(a +1); 其他五种不再写了;
1.3 通过上面两个步骤对当前的栈的内容应有清晰的了解。
1.4 通过p指针将数组a的元素正确的输出;p[i][j] *(*(p + i) + j)
*(p[i] + j) (*(p +i))[j] **p + i*3 +j
2.1 对q进行赋值(可以是a,&a,*a,a[0],&a[0],&a[0][0]);
2.2 能过q数组指针对数组a中的元素正确的打印出来: q[i][j] *(*(q + i) + j)
*(q[i] + j) (*(q +i))[j] **q + i*3 +j
3.1 通过数组a将 p指针指向的数组进行赋值,将*p赋给q ,通过q指针打印数组a的元素这里有一个强制类型转换的知识讲一下:typedef int (*arrayP)[3];
int (*q)[3] = (arrayP)(*p);
3.2 通过数组a将q指针进行赋值,通过q指针初始化P指针数组,通过p将数组a的元素打印出来。
通过以上三组实组,加深对数组和指针的理解。对于c编译器来说,指针真是想指哪就指那,对于c程序员应该保证指针变量指向有意义。
具体代码 :arrayPointer.c
#include<stdio.h>
typedef int (*arrayP)[3];
int main(void){
int a[2][3] = {1,2,3,4,5,6};
int *p[2] = {(int *)a,(int *)(a + 1)};
//int *p[2] = {&a[0][0],&a[1][0]};
//int *p[2] = {a[0],a[1]};
int i,j;
for(i=0;i<2;i++){
for(j = 0;j < 3;j++){
printf("%d",p[i][j]);
printf("%d",*(*(p + i) + j));
printf("%d",*(p[i] + j));
printf("%d",(*(p + i))[j]);
}
}
//int (*q)[3] = a;
//*******************************************************//
//int (*q)[3];
//q = a;
//q = &a;
//q = *a;
// q = a[0];
//q = &a[0];
// q = &a[0][0];
//***********************************************************//
printf("\n********************************\n");
printf("%p\n",&a[0][0]);
printf("%d\n",*a[0]);
printf("\n********************************\n");
printf("%p\n",*(p + 1));
printf("%p",&a[1][0]);
printf("\n********************************\n");
printf("%p\n",a);
printf("%p\n",*a);
printf("%d\n",**a);
printf("\n********************************\n");
//*******************************************************//
// int (*q)[3] = a;
int (*q)[3] = (arrayP)(*p);
// int (*q)[3] = &a;
// int (*q)[3] = *a;
// int (*q)[3] = a[0];
// int (*q)[3] = &a[0];
// int (*q)[3] = &a[0][0];
for(i=0;i<2;i++){
for(j = 0;j < 3;j++){
printf("%d",q[i][j]);
printf("%d",*(*(q + i) + j));
printf("%d",*(q[i] + j));
printf("%d",(*(q + i))[j]);
}
}
int aa[4] = {1,2,3,4};
int *c = (aa + 1);
int *c2 = (int *)((int)aa + 1);
printf("\n%x\t%x\n",*c,*c2);
return 0;
}
说明:以上内容均为原创,转载请标明来源或联系作者。