在c语言的学习中,指针始终是一个非常重要的知识点,同时它也是一个难理解的知识点。相比于其他的语言,比如java(好吧,只学过java) xxx等,指针也是c语言的一个重要特点。通过指针,c语言程序可以直接访问内存地址(或者称为虚拟地址空间更准确,对已实实在在的物理内存,虚拟地址空间是一个比较抽象概念,大概的意思是只要知道虚拟内存地址,通过虚拟内存与物理内存的映射关系,电脑自己会找到对已的物理内存然后把数据存进去,具体可参考操作系统书籍中内存这一部分。一般的32bit电脑的最大寻址空间是 232 2 32 =
4G
,而64bit的电脑最大寻址空间是 264 2 64 =xxG
,好吧,太大了不会算,反正希望能有用到这么大内存的一天。这也是为什么物理内存大于4G的电脑装32bit的系统会无法识别多出的那部分),而很多高级语言如java是没有地址访问这一个说法的(当然java中的引用和c中的指针很类似),这也是为什么很多底层开发有会用到c语言的语言之一吧。在c语言中指针的种类也不少,比如数组指针、指针数组 和 函数指针、指针函数等,乍一听过还感觉差不多,其实是失之毫厘差之千里,千万不要搞混淆了,make a long story short。
指针,就是一个内存地址,在程序进行编译时,系统会给各个变量分配内存单元而该变量的指针就是这个内存单元的地址,其形式为0x00000000
共8位16进制数也就是对应32位地址空间。对应int型的指针a,其形式为int * a,表示指针a指向的内存单元中存储的是一个int型的数据。同理,要是一个char型的指针b,其形式为int * b,表示指针b指向的内存单元中存储的是一个char型的数据。除了这些指针外,还有一种void类型的指针,它表示指向一个抽象的类型的数据,使用的时候需要进行强制转换。使用指针可以提高程序的效率以及实现动态存储分配,指针的使用如下:
#include
void main()
{
int data_a,data_b;
int * point_a, * point_b;
data_a=0;data_b=1;
point_a=&data_a;/*通过符号&来获得变量的地址*/
point_b=&data_b;
printf("通过指针point_a来获得a的值为:%d\n",* point_a);/*通过符号 * 来获得指针的指向对象的内容*/
printf("通过指针point_b来获得b的值为:%d\n",* point_b);
}
执行后的结果为:
通过指针point_a来获得a的值为:0
通过指针point_b来获得b的值为:1
除了指向基本数据类型的指针外,还有一些其他类型的指针,比如指向某些结构体类型的指针,不过它和指向基本数据类型的指针大同小异(有点类似于java语言中对象的引用类型)这里就不在复习,接下来看看下面这些类型指针:
数组实际上是同类型元素有序的一个集合,可以通过数组名及对应的下标定位元素,每个数组元素在内存中占用了存储单元,所以都有相对地址。指针表示指向某个数据元素的地址,所以也是可以用指针来指向数组元素的,如把int数组a中第0个元素地址赋值给p:
int * p;
int a[]={23,53,4,64,3,6,2,4};
p=&a[0];
在c语言中规定数组名表示数组中第一个元素的地址,即a可以表示a[0]的地址,所以也可以这样写:
int * p;
int a[]={23,53,4,64,3,6,2,4};
p=a;
除了一维数组外,还有多维数组,多维数组的实质就是数组的数组,而多维数组的指针相对于一维数组指针会复杂点。
如一个二维数组int arr_a[3][4]表示3个int arr_b[4],而一个int arr_b[4]表示有4个int型元素,所以数组arr_a有12个元素。
数组名arr_a表示指向3个int arr_b[4]元素中的第一个元素地址,而该元素则又是一个int数组,所以arr_a也表示指向int数组的第一个元素(虽然说arr_a指向的地址和arr_a[0]指向的地址是一样的,但是个人感觉从某种意义上说它们并不代表同一个概念。arr_a是具有3个int arr_b[4]元素的数组名,表示指向第一个int arr_b[4]元素的地址,而arr_a[0]表示第一个int arr_b[4]元素的数组名,指向4个int型元素的第一个元素)。
#include
void main()
{
int arr_a[3][4]={{1,2},{0,5,8,9},{1}};
printf("%p and %p\n",arr_a,&arr_a);/* 奇怪的发现arr_a和*&arr_a结果发现是一样的 */
printf("the address of a[0] is %p: %p ,and the value of arr_a[0][0]is :%d\n",arr_a,*arr_a,**arr_a);
printf("the value of a[1][1] is %d:\n ",arr_a[1][0]);
printf("the address of a[1][0] is %p:\n ",&arr_a[1][0]);
}
运行结果:
0x7fff4156aef0 and 0x7fff4156aef0
the address of a[0] is 0x7fff4156aef0: 0x7fff4156aef0 ,and the value of arr_a[0][0]is :1
the value of a[1][1] is 0:
the address of a[1][0] is 0x7fff4156af00:
从上面的代码中可以看出很多奇怪的现象,比如arr_a、&arr_a以及arr_a的值都一样,好吧只能说c语言真的博大精深,而这还只是二维数组,想想多维数组,确实够难理解的。所以只好用最笨的方法记住这些。( *1.多维数组即数组的数组 2.数组名表示数组中第一个元素的地址 )
对应字段 | 含义 |
---|---|
arr_a | 二维数组名,指向首行元素地址 |
**arr_a,arr_a[0][0] | 表示0行0列元素的值 |
*arr_a,arr_a[0] | 表示0行0列地址 |
*(arr_a+1)+2,arr_r[1]+2 | 都表示第二行第3列元素地址 |
*(arr_a+1),arr_a[1] | 都表示第二行0列元素地址 |
arr_a+1,&arr_a[1] | 都表示第二行地址 |
在c语言中,有2种方式访问一个字符串:
#include
void main()
{
char string[]="hello World";
printf("%s\n",string);
}
输出结果:
hello World
#include
void main()
{
char * string ="helloworld!";
printf("%s\n",string);
}
输出结果:
helloWorld!
字符数组和字符指针变量都可以用来操作字符串,但是2者之间并不相同。
1.字符数组是由多个元素组成,每个元素中存入一个字符,以’\0’为结束。
2.字符数组和字符指针变量赋值方式不一样,字符数组只能通过对各个元素赋值,而字符指针可以通过如下方式赋值:
char *a;
a="helloworld!";
3.定义一个字符数组,在编译时就会为它分配一个固定的地址并确定它的大小,在以后的操作中,该数组大小无法改变。而字符指针表示指向一个字符变量的地址,是可以改变的,如果一个字符指针没有进行赋值,则该指针是随机的指向一个地址(这存在非常大的隐患)。
数组指针,可以简单的把它理解成普通的指针(数组指针不是数组,它是一个指针),而与普通的指针不同之处在于它指向的对象是个数组。
其声明为:
int (*point_arr)[];
即相当于有一个数组对象arr
int arr[]={2,5,8,6,7};
指针point_arr指向数组对象arr的地址,又因为数组名arr可以表示数组的首地址,所以数值上arr==point_arr,但是所表示的意义不一样。point_arr表示指向的是整个数组对象,如果对它取值*point_arr
,得到的是数组首地址(point_arr
和*point_arr
值相同,含义是不一样的)。
#include
int main()
{
int i;
int (* point_arr)[];
int arr[5]={2,5,8,6,7};
point_arr=arr;
printf("%p and the value of first element is %d ,the valueof point is %p\n",point_arr,**point_arr,*(point_arr));
return 0;
}
0x7fffb1ebc710 and the value of first element is 2 ,the valueof point is 0x7fffb1ebc710
指针是可以进行运算的,如将指向int数组a中第一个元素的指针p进行p+1操作,则p+1表示指向数组a中的第二个元素(这里不是说把数组a第一个元素的地址加1)。同理,如果指针p指向一个float数组b,p+1同样表示数组中p指向元素的下一个元素,它代表的地址操作为p指向的地址+1*float的字节数。
#include
void main()
{
int a[10];
int *p ,i;
for(i=0;i<10;i++)
{
a[i]=(i+5)%10;
}
printf("\n");
for(p=a;p<(a+10);p++)
{
printf("%d",*p);
}
printf("\n");
}
#include
#include
#define N 5
#define M 8
void fun(char (* ss)[M])
{
char *ps[N],*tp;int i,j,k;
for(i=0;ifor(i=0;i1;i++)/*same as select sort*/
{
k=i;
for(j=i+1;jif(strlen(ps[k])<strlen(ps[j]))
k=j;
}
tp=ps[i];
ps[i]=ps[k];
ps[k]=tp;
}
printf("\n The string after sorting by length :\n\n");
for(i=0;iputs(ps[i]);
}
}
main()
{
char ch[N][M]={"red","green","bule","yellow","black"};
int i;
printf("\nThe original string\n\n");
for(i=0;iputs(ch[i]);
}
printf("\n");
fun(ch);
}
运行结果:
The original string
red
green
bule
yellow
black
The string after sorting by length :
yellow
green
black
bule
red