原文地址:http://blog.csdn.net/porscheyin/article/details/3461417
今天在网上意外看到这篇文章,原文博主对指针做了非常好的介绍,看完真是进步了不少。下面是看了文章之后的收获和验证、思考。
1.数组名实际上就是一个常量指针,即指针自身的值不能改变。比如int ia[10]; ia++;是不对的。
数组名作为函数参数时:C语言标准规定作为形参的数组声明转换为指针。在声明函数形参的特定情况下,编译器会把数组形式改写成指向数组第一个元素的指针。
void array_test(int ia[100])
{
double da[10];
printf("%d",sizeof(ia)); //得到的只是一个指针的大小
printf("%d",sizeof(da)); //得到的是10*sizeof(double)
ia++; //编译正确,ia已经被自动转换为指向数组首元素的指针了
//da++; //编译错误,数组名da是常指针
}
2.指针数组:一个数组,它的所有元素都是指针。int *p[100]
3.指向数组的指针:当对一个一维数组的数组名进行&操作时,返回的是一个指向数组的指针。二维数组就是每个元素都是一个一维数组的一维数组。指向数组的指针主要就是用来对二维数组进行操作。int (*p)[100]
long i = 0x11111111;
void *p1 = &i;
void *p2 = (void *)i;
void *p3 = i; //错误
&的实质:当对一个T类型对象进行&操作时,返回的是一个|“指向T的指针”类型的常量。
指针中只能存放地址,不能将一个非0值整型常量表达式或者其他非地址类型的数据赋给一个指针。在大多数计算机中,内存地址 确实是以无符号整型数来表示的,而且多以16进制表示,但我们在C语言中不能用更整型数去表示地址,只能用指针常量来表示,因为它是被用来赋给一个指针的。
上述代码中p1变量将存i的地址,p2变量存的是0x11111111这个地址,当然,可能这个地址对应的内存并没有存什么数据。
4.函数指针:指向函数的指针。
void (*f)( void);
void test(void);
f = test;
f = &test;
f是一个指向不接受参数且不返回任何值的函数的指针。此处test和&test都可以用来初始化函数指针。因为C语言规定函数名会被转换为指向这个函数的指针。f = test中的test会被自动转换为&test;而f = &test则已经显示使用了&test,所以tese就不会再发生转换了。
5.指针函数:返回指针的函数,如void *f(void)。
void (*signal(int sig,void (*func)(int siga)))(int siga)等价于
typedef void (*p_sig)(int);
p_sig signal(int sig,p_sig func);
也就是signal是一个函数,这个函数接受两个参数int和p_sig,返回一个函数指针,这个函数指针指向的是参数为int,返回值为void。
6.函数指针数组:一个数组中存的元素都是函数指针。
typedef void (*PF)();
PF file_options[] = {};
等价于 void (* file_options[])()。
7.字符串数组:
char ca[] = “abcdefg”;
char *cp = “abcdefg”;
含义不同。前者”abcdefg”是用来初始化数组ca的元素,后者”abcdefg”是一个真正的字符串常量,会被转化为由一个指针所指向的字符数组,并将这个指针常量赋值给cp。
用来初始化字符数组的字符串,编译器会在栈中为字符数组分配空间,然后把字符串中的所有字符复制到数组中,也就是此时数组中的元素是可以改变的。如 ca[0] = ‘b’是正确的。
而用来初始化字符指针的字符串常量会被编译器安排到 只读数据存储区,但也是按字符数组的形式来存储的,也就是说我们可以通过字符指针来读取字符串常量但是不能去修改它。比如 cp[0] = ‘b’是错误的。
另外,标准C允许编译器为两个包含相同字符的字符串常量使用相同的存储地址。也就是说:
char cb[] = “abcdefg”;
char *cp2 = “abcdefg”;
此时ca != cb因为这是两个不同的字符数组,在栈中有各自的空间。
而cp == cp2因为两个字符指针分别被初始化为指向相同的区域,该区域包含相同字符的字符串常量。
8.常量指针:指针本身的值不能改变。如int *const c_p。
指针常量:一个指针类型的常量。如(int *)。
指向常量的指针:一个指针,指向的是一个const对象。如 const int *p_to_const;p_to_const本身的值是可以改变的,也就是这个变量所存的地址值是可以 换成其他的,但是不能通过对p_to_const解引用来改变所指的对象的值。
- C语言中对于指针的赋值操作(包括实参与形参之间的传递)应该满足两个条件之一:
(1)两个操作数都是指向有限定符或都是指向无限定符的类型相兼容的指针;
(2)左边指针所指向的类型具有右边指针所指向的类型的全部限定符。
注:在文章的最后讲C语言的值传递时,用了这个例子:
#include <stdio.h>
void pointer_plus(char *p)
{
p += 3;
}
int main( )
{
char *a = "abcd";
pointer_plus(a);
printf("%c/n", *a);
return 0;
}
这个例子很有意思,根据之前讲的用字符串常量初始化指针的知识,并且结合gdb的结果我们可以看到:
“abcd”是处于只读数据存储区的,首地址也就是字符’a的地址’0x4035d4,a这个指针变量存的就是这个地址,a+1表示这个地址变量偏移1个单位,所以就是’b’的地址0x4035d5,在函数pointer_plus里,通过C语言只有传值调用机制,程序将拷贝一份赋给p,所以此时指针变量p的初始值是’a’的地址0x4035d4,通过p+=3之后,此时变量p存的地址将会是0x4035d7,也就是’d’的地址了。但是又因为值传递,从此a和p就没有关系了。所以p这个变量有什么改变并不会影响到a这个变量。所以函数返回后变量a所存放的仍然是’a’的地址0x4035d4。因此输出字符a。
另外,我们还可以看到,&p和&(p+1)的值是一样的,都是0x7fffffffe378,这是指针变量 p本身的地址。可能会疑惑为什么&(p+1)值也是这样?其实是因为p+1表示p这个变量存放的值是0x4035d5,但是此时&作用的还是p这个变量,所以得到的仍然是p本身的地址。而&p+1则不同,这是先用&取出p本身的地址,然后在这个值上进行了地址偏移。而指针变量本身占几个字节跟语言无关,而是跟系统有关。本机是64位,所以是占8个字节,也就解释了0x7fffffffe378偏移1个单位后是0x7fffffffe380的原因。
10.free函数
free(p)的作用是通过指针p把p所指向的内存空间释放掉,并没有把p释放掉,所谓释放掉就是将这块内存中的对象销毁,并把这块内存交还给系统留作他用。调用该函数之后指针p的值仍是那块内存的首地址,倘若此时这块内存又被指派用于存储其他的值,那么对p进行解引用就可以访问这个当前值,但如果这块内存的状态是不确定的,那对p解引用就可能会出现运行时错误。所以为了安全起见,在free一个指针后,将这个指针设置为NULL或零指针常量。