C语言学习历程(4)——指针

指针与变量

数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。

指针变量存储了数据的地址,通过指针变量能够获得该地址上的数据,格式为:
*pointer;        //这里的*称为指针运算符,用来取得某个地址上的数据
 
如:
int x = 10;
int y = 20;
int *px = &x;
int *py = &y;
y = *px + 5;   //表示把x的内容加5并赋给y,*px+5相当于(*px)+5
y = ++*px;    //px的内容加上1之后赋给y,++*px相当于++(*px)
y = *px++;    //相当于y=*(px++)
py = px;        //把一个指针的值赋给另一个指针
 

指针与数组

 一、利用数组名遍历数组元素
     数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element)。数组中的所有元素在内存中是连续排列的,整个数组占用的是一块内存。

定义数组时,要给出数组名和数组长度,数组名可以认为是一个指针,它指向数组的第 0 个元素。在C语言中,我们将第 0 个元素的地址称为数组的首地址。
 二、利用指针遍历数组元素
1、指向数组开头的指针
我们也可以定义一个指向数组的指针,例如:
                          int arr[] = { 99, 15, 100, 888, 252 };
                          int *p = arr;
      arr 本身就是一个指针,可以直接赋值给指针变量 p。arr 是数组第 0 个元素的地址,所以int *p = arr;也可以写作int *p = &arr[0];。也就是说,arr、p、&arr[0] 这三种写法都是等价的。但是特别注意的是,“arr 本身就是一个指针”这种表述并不准确,严格来说应该是“arr 被转换成了一个指针”。区别在于:sizeof(arr)和sizeof(p)。 
定义:如果一个指针指向了数组,我们就称它为数组指针(Array Pointer)。
数组指针指向的是数组中的一个具体元素,而不是整个数组,所以数组指针的类型和数组元素的类型有关,上面的例子中,p 指向的数组元素是 int 类型,所以 p 的类型必须也是int *。反过来想,p 并不知道它指向的是一个数组,p 只知道它指向的是一个整数,究竟如何使用 p 取决于程序员的编码。
注意:在求数组的长度时不能使用sizeof(p) / sizeof(int),因为 p 只是一个指向 int 类型的指针,编译器只知道它指向的是一个整数但不知道是一个整数(数组),所以 sizeof(p) 求得的是 p 这个指针变量本身所占用的字节数,而不是整个数组占用的字节数。
 
2、指向数组某个位置的指针
     上节我们讲到,对指针变量进行加法和减法运算时,是根据数据类型的长度来计算的。p+1 指向下一个元素,p-1 指向上一个元素。那么p+i 指向下 i 个元素,p -i 指向上 i 个元素。
三、访问数组元素的两种方案
引入数组指针后,我们就有两种方案来访问数组元素了,一种是使用下标,另外一种是使用指针。
1) 使用下标
也就是采用 arr[i] 的形式访问数组元素。如果 p 是指向数组 arr 的指针,那么也可以使用 p[i] 来访问数组元素,它等价于 arr[i]。
2) 使用指针
也就是使用 *(p+i) 的形式访问数组元素。另外数组名本身也是指针,也可以使用 *(arr+i) 来访问数组元素,它等价于 *(p+i)。 
     不管是数组名还是数组指针,都可以使用上面的两种方式来访问数组元素。不同的是,数组名是常量,它的值不能改变,而数组指针是变量,它的值可以任意改变。也就是说,数组名只能指向数组的开头,而数组指针可以指向数组开头,也可以指向其他元素。

指针与字符串

一、数组存放字符
     C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中,字符数组归根结底还是一个数组,上节讲到的关于指针和数组的规则同样也适用于字符数组。
二、字符指针
     除了字符数组,C语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,
例如:char *str = "hello";     或者:    char *str;str = "hello";
字符串中的所有字符在内存中是连续排列的,str 指向的是字符串的第 0 个字符;我们通常将第 0 个字符的地址称为字符串的首地址。字符串中每个字符的类型都是char,所以 str 的类型也必须是char *。
三、两类存放字符的区别
     它们最根本的区别是在内存中的存储区域不一样,字符数组存储在全局数据区或栈区,第二种形式的字符串存储在常量区。全局数据区和栈区的字符串(也包括其他数据)有读取和写入的权限,而常量区的字符串(也包括其他数据)只有读取权限,没有写入权限。内存权限的不同导致的一个明显结果就是,字符数组在定义后可以读取和修改每个字符,而对于第二种形式的字符串,一旦被定义后就只能读取不能修改,任何对它的赋值都是错误的。另外,字符数组的sieof(str)为数组中元素个数(包括‘\0’),字符指针的sizeof(str)为指针大小。
四、何种情况使用字符数组还是字符串常量
     在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操作,只能使用字符数组,不能使用字符串常量。

指针与函数

为什么用指针或数组名作为函数的参数
1、使用由于函数最多有一个返回值,为了得到更多的函数返回值,用指针变量作函数参数可以将函数外部的地址传递到函数内部,使得在函数内部可以操作函数外部的数据,并且这些数据不会随着函数的结束而被销毁。因此可以得到多个函数的返回值。
2、像数组、字符串、动态分配的内存等都是一系列数据的集合,没有办法通过一个参数全部传入函数内部,只能传递它们的指针,在函数内部通过指针来影响这些数据集合。
注意:1)将数组作为函数参数时会被弱化成指针,因此传递到函数内部的都是数组指针(地址),所以在函数内部无法通过这个指针获得数组长度,必须将数组长度作为函数参数传递到函数内部。
2)用数组做函数参数时,它的参数还可以写成下面的形式:(也都是将数组地址传递过来而已)
int max(int Arr[6], int len);     或者    int max(int Arr[], int len);
 C语言为什么不允许直接传递数组的所有元素,而必须传递数组指针呢?
参数的传递本质上是一次赋值的过程,赋值就是对内存进行拷贝。所谓内存拷贝,是指将一块内存上的数据复制到另一块内存上。这样会严重拖慢程序的效率,因此C语言没有从语法上支持数据集合的直接赋值。
 

二级指针

      指针可以指向一份普通类型的数据,例如 int、double、char 等,也可以指向一份指针类型的数据,例如 int *、double *、char * 等。如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。
假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,将这种关系转换为C语言代码:
int a =100;
int *p1 = &a;
int **p2 = &p1;
     指针变量也是一种变量,也会占用存储空间,也可以使用&获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*。p1 是一级指针,指向普通类型的数据,定义时有一个*;p2 是二级指针,指向一级指针 p1,定义时有两个*。但是在实际开发中会经常使用一级指针和二级指针,几乎用不到高级指针。
 
 
 


 
 

 

猜你喜欢

转载自blog.csdn.net/Neptune__/article/details/81278848