指针和数组的区别:
(1)数据:指针保存数据的地址;数组保存数据的值
(2)访问:指针间接访问,首先取得指针的内容,把它作为地址,根据地址提取数据;数组直接访问数据
(3)用途:指针通常用于动态的数据结构,数组通常用于存储数目固定且数据类型相同的元素
(4)空间:指针使用malloc和free申请和释放空间,数组隐式分配和删除
(5)数据名:指针通常指向匿名数据,数组自身即为数据名
另外:数组和指针都可以在他们定义中用字符串常量进行初始化,看起来一样,但是底层的机制不一样:
指针:定义指针的时候编译器并不为指针所指向的对象分配内存空间,它只是分配指针本身的空间(32位机上通常为4个字节),除非在定义指针的时候同时赋值给一个字符串常量进行初始化,由于字符串常量存放于文本段,所以不允许通过指针修改字符串常量。
数组:定义数组的时候会根据下标分配内存,如果没有写明下标,会根据初始化的字符串常量的长度分配内存,可以通过数组下标修改数组的值。
#include <stdio.h>
int main()
{
char b[] = "abc";
printf("b[0] = %d\n", b[0]);
printf("++b[0] = %d\n", ++b[0]);
char *a = "abc";
printf("a[0] = %d\n", *a);
printf("++a[0] = %d\n", ++(*a));
return 0;
}
测试结果如下:
cheny.le@cheny-ThinkPad-T420:~$ ./a.out
b[0] = 97
++b[0] = 98
a[0] = 97
段错误 (核心已转储)
可以看到验证了不能通过指针修改字符串常量的值,但是可以通过数组去修改,具体原因如下:
#include <stdio.h>
int main()
{
char b[] = "bbb";
char *a = "def";
printf("&a = %p\n", &a);
printf("&b = %p\n", &b);
printf("a = %p\n", a);
printf("b = %p\n", b);
return 0;
}
运行结果:
cheny.le@cheny-ThinkPad-T420:~$ ./a.out
&a = 0x7fff93153108
&b = 0x7fff93153110
a = 0x40068c
b = 0x7fff93153110
从10和11行可以看到,”bbb“和”def“存储的位置是有区别的,”bbb“存储在栈区,栈区的数据是可以修改的,”def“存储在静态数据区(也就是代码段),数据是只读的
从7和8行可以看到,变量a和变量b都是存储在栈区的,另外b和&b的值是相等的,可以说明数组的数组名是一个指向第一个元素的指针,所以数组名的值跟第一个元素的地址是相同的
指针和数组的关联:
在KRC里面有这么一句话:‘’指针和数组是相同的仅当他们作为函数定义的形式参数“。
(1)”表达式中的数组名“就是指针
(2)c语言把数组下标作为指针的偏移量
(3)”作为函数参数的数组名“等同于指针
这里需要顺带的解释下形参和实参的区别:
形参:是一个变量,在函数定义或函数声明的原型中定义,又称”形式参数“, 例如:swap(int *a, int * b);
实参:在实际调用一个参数时所传递给函数的值,又称”实际参数“,例如swap(i, j);
数组和指针可交换性总结:
(1)用a[i]这样的形式对数组访问总是被编译器”改写“或者解释为向*(a+i)这样的指针访问
(2)指针始终就是指针,他绝对不可能改写成数组,你可以用下标的形式访问数组,一般都是指针作为函数的参数时,而且你知道实际传递给函数的是一个数组
(3)在特定上下文中,也就是他作为函数的参数(也只有这种情况),一个数组的声明可以看作是一个指针,作为函数参数的数组(就是在一个函数的调用中)始终会被编译器修改成指向数组的第一个元素的指针
(4)当把一个数组定义成函数的参数的时候,可以选择把他定义成数组,也可以定义成指针,不管选择哪种方法,在函数内部实际上获得的都是一个指针。
(5)在其他所有情况下,定义和声明必须匹配,如果定义了一个数组,在其他文件中对他进行声明的时候也必须把他声明为数组,指针也是如此。
给出一下测试的例子:
#include <stdio.h>
void p(char *a)
{
printf("&a = %p\n", a);
printf("&a[0] = %p\n", &a[0]);
printf("&a[1] = %p\n", &a[1]);
}
int main()
{
char a[] = "abc";
p(a);
return 0;
}
测试结果如下:
cheny.le@cheny-ThinkPad-T420:~$ ./a.out
&a = 0x7fff42747fe0
&a[0] = 0x7fff42747fe0
&a[1] = 0x7fff42747fe1
这个可以看到形参a是一个指针,值是0x7fff42747fe0,而数组第一个元素的地址就是0x7fff42747fe0,这个说明了总结(3)
#include <stdio.h>
void p(char a[])
{
printf("&a = %p\n", a);
printf("&a[0] = %p\n", &a[0]);
printf("&a[1] = %p\n", &a[1]);
}
int main()
{
char a[] = "abc";
p(a);
return 0;
}
测试结果如下:
cheny.le@cheny-ThinkPad-T420:~$ ./a.out
&a = 0x7fff73a4dca0
&a[0] = 0x7fff73a4dca0
&a[1] = 0x7fff73a4dca1
这段代码跟前一段代码的差别仅仅就是形参由指针变成了数组,但是得到的结果是一样的,a的值还是实参数组的第一个元素的地址,这个说明了总结(4)