C语言的精华就是熟练的对指针进行运用
指针就是就是指向变量的地址,指针变量中存储着地址,而指针本身也会被分配一片内存空间进行其地址的存储。在32os下其所占字节为4,也就是32位,而64os下所占字节为8,也就是64位。熟练的运用指针对地址进行操作可以解决许多问题。
指针的定义
定义指针变量的格式为:存储类型 数据类型 * 指针变量名。存储类型:指针变量名在内存空间中开辟的位置;数据类型:指针所指向的数据类型;数据类型*:指针的数据类型;指针变量名:分配连续空间的名字。指针就是内存地址,指针变量中存储的就是地址,可以通过对地址进行操作实现地址中值发生改变。同时指针变量也可以进行赋值,改变其指针的指向。
取值符和取址符
*在C语言中有许多的用途,可以作为运算符参与运算,在定义指针的时候作为标识符,在linux下的命令窗口中作为通配符进行使用,同时还可以作为取址符,如:

指针变量p中存储的是a的地址,而利用取值符对p取*后,就可以得到对应地址中的值,p指向的是a的地址,所以取值后显示的是变量a的值。利用取址符对a进行&后,就得到了a的地址。也就是p=&a,两边同时取*后得到*p=a。
案例
1、试着写出指针数组中四个指针元素的值
int main()
{
char *str[4] = {"welcome","to"," I ew","beijing"};
char **p = str+1;
str[0] = (*p++)+1;
str[1]= *(p+1); .
str[2]= p[1]+3; .
str[3] = p[0] + (str[2]- str[1]);
}

str[0] = (*p++)+1;
数组名str等价于数组首元素的地址,而str+1表示数组中第二个元素的地址&str[1],因此指针p指向数组str中的第二个元素str[1],而str[1]本身也是指针,指向字符串“to”的首字符t,因此指针p是一个指向指针的指针。*p指向字符串“to”的首字符t。此外 *p++ 中的 *p没有括号并且是后++,因此赋值语句相当于执行了str[0] = *p + 1后再执行p++,而 *p+1指向字符串“to”的第二个字符o,因此str[0]指向了字符o,p++后p指向指针数租str的第三个元素str[2]。
str[1] = * (p + 1);
str[2] = p[1] + 3;
此时,p指向str[2],p + 1指向str[3],也就是p + 1 = &str[3],解引用可得*(p+1)= str[3],因此str[1] = str[3]。由于str[3]指向字符串“Beijing”的首字母B,因此str[1]也指向同样的位置。在分析str[2]时,首先应该明确p[1]等价于*(p+1),这是访问数组元素的两种方式。根据对str[1]的分析可知,*(p+1)指向字符串“Beijing”的首字符B,也就是p[1]指向字符串“Beijing”的首字符B,p[1]+3就指向字符串“Beijing”的第四个字符j,因此str[2]就指向字符串“Beijing”的第四个字符j。
str[3] = p[0] + (str[2] - str[1]);
表达式str[2]-str[1]表示两个指针之间元素的个数,str[2]指向字符串“Beijing”的第四个字符j,而str[1]指向字符串“Beijing”的首字符B,相差3个字符,因此str[2]-str[1]=3。此外,p[0]等价于*(p+0),也就是*p,而 *p=str[2],因此p[0]同样指向字符串“Beijing”的第4个字符j,而p[0]+3就指向字符串“Beijing”的第7个字符g。
指针可以和许多东西进行结合,如一维数组,二维数组,函数 。
指针对数组的一些操作
指针操作 |
数组操作 |
说明 |
array |
&array[0] |
数组首地址 |
*array |
array[0] |
数组的首元素 |
array + i |
&array[i] |
数组第i个元素的地址 |
*(array + i) |
array[i] |
数组的第i个元素 |
*array+ b |
array[0] + b |
数组首元素的值加b |
*(array+i)+b |
array[i] + b |
数组第i个元素的值加b |
*array++ (当前指向第i个元素) |
array[i++] |
先取得第i个元素的值,i再加1 |
*++array(当前指向第i个元素) |
array[++i] |
先将i加1,再取得第i个元素的值 |
*array--(当前指向第i个元素) |
array[i--] |
先取得第i个元素的值,i再减1 |
*--array(当前指向第i个元素) |
array[--i] |
先将i减1,再取得第i个元素的值 |
子函数和指针的结合
利用函数可以对功能进行封装,实现模块化。而大部分函数需要进行输入参数的传递,这就涉及到实参和形参。通过将值传入的方式将参数进行输入的方式为形参,在子函数执行完成后其空间就会被释放。这种传递方式就相当于把传入的值进行就拷贝,无论在子函数里面怎么进行操作,都不会引起主函数参数发生改变。

如图,销毁顺序表子函数,想要通过在函数内对函数外申请的空间进行释放的话就要将指向顺序表首地址的地址传入函数内。顺序表首地址为一级指针,传入指针的地址,那么就要定义二级指针进行接收。
假如光传入指针变量的话,就只是一种拷贝,在函数体内又定义了一个指针指向顺序表,这样同时有两个指针指向顺序表。函数体内空间当然可以释放空间和指针置空。但是主函数的指针在空间完成释放后失去了指向,本来好好地指向顺序表的首地址,但是这片空间没了,那指向谁呢?这就成了野指针。野指针是非常危险的,它不像空指针一样,可以被判断,它指向随机,所以要尽量避免野指针的出现。
所以,参数传入的是顺序表首地址的地址,也就是二级指针,二级指针存的是一级指针的地址,通过取值符*得到顺序表的首地址,对首地址进行操作,就实现了对主函数内的指针进行操作,从而引起实参的改变。这样在函数体内部操作的指针与主函数内指针为同一个。