Jialong's Blog

Do things I love, and seek happiness.

0%

C语言指针

一、指针的一些概念

1. 指针的类型

  • 把指针声明语句中的指针名字去掉,剩下的就是指针本身的类型

2. 指针所指向的类型

  • 把指针声明语句中的指针名字和名字前的指针声明符号*去掉,剩下的就是指针指向的类型

3. 指针的值——指针所指向的内存区或地址

  • 指针的值是指针本身存储的数值,该值被编译器当作一个地址(在32位程序中,所有类型的指针的值都是一个32位的整数,因为32位程序的内存地址都是32位长。)
  • 指针所指的内存区就是从指针的值所代表的内存地址开始,长度为sizeof(指针指向的类型)的一篇内存区。
  • 我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区
  • 同理,我们说一个指针指向了某个内存区,相当于说该指针的值是该内存区域的首地址

4. 指针本身所占据的内存区

  • sizeof(指针的类型)测试一下就知道了

二、指针的算术运算

**0.**指针可以加上或者减去一个整数,这种运算的意义并不是平常的数值的加减。

  • example 1
1
2
3
char a[20] = {0};
int *ptr = (int *)a;
ptr++;

指针ptr的类型为int*,它指向的类型为int,被初始化为指向整型变量a。

在第三句中,指针ptr被加上了1,编译器是这样进行处理的:

把指针ptr的值加上sizeof(int),在32位程序中是被加上了4,因为32位程序中int占4字节。

由于地址是用字节作为单位,所以ptr指向的地址由原来的a的地址向高的地方增加了4个地址。

char类型的长度是一个字节,所以原来的ptr是指向数组a从第0个单元开始的4个字节,现在指向了从第4个单元开始的4个字节

  • example 2

1.**可以使用一个循环一个指针**遍历一个数组。

1
2
3
4
5
6
7
int array[20] = {0};
int *ptr = array;
for (i = 0; i < 20; i++)
{
(*ptr)++;
ptr++;
}

该例子中将整形数组中各个单元的值都加1。同时每次循环都将指针加1个单元,所以每次循环都能访问数组的下一个单元。

  • example 4
1
2
3
char a[20] = "You_are_a_girl";
int *ptr = (int *) a;
ptr += 5;

该例子中,指针ptr被加上了5个单元,编译器这样处理:

将char的值加上sizeof(int) * 5,在32位程序中即乘了4*5=20。

指针的值代表了该指针以这个值为地址指向了某个内存区,所以指针ptr所指向的地址向高地址的方向移动了20个字节

然而ptr加上5(即地址增加20个字节后)已经指向了数组a的合法范围之外,虽然应用时会出现问题,但在语法上没有问题。


**2.**总结

一个指针ptrold加或减一个整数n后,结果是一个新的指针ptrnew,两个指针的类型相同,指向的类型也相同。

ptrnew的值比ptrold的值增加或者减少了n * sizeof(int)个字节,即ptrnew指向的内存区比ptrold指向的内存区向高或低地址的方向移动了n * sizeof(int)个字节。

指针进行加减:两个指针不能进行加法操作,这是非法操作,结果毫无意义。两个类型相同的指针可以进行减法运算,一般在数组上应用。

三、运算符&和*

0. &是取地址运算符,*是间接运算符。

&a 运算的结果是一个指针,指针的类型是 *a,指针指向的类型是a的类型,指针指向的地址是a的地址。

*p的类型是p指向的类型,所占用的地址是p指向的地址。

四、指针表达式

0. 一个表达式的结果如果叫指针,那么这个表达式就叫指针表达式。

  • example
1
2
3
4
5
6
7
8
int a, b;
int array[10];
int *pa;
pa = &a;// &a是指针表达式
int **ptr = &pa;
*ptr = &b;
pa = array;
pa++;

1. 指针表达式的结果是一个指针,所以也具有指针的4个要素:

指针的类型

指针指向的类型

指针指向的内存区

指针自身占据的内存

2. 一个指针表达式的结果指针已经具有了自身占据的内存的时候,这个指针表达式就是一个左值,否则就不是一个左值。

五、指针和数组的关系

0. 数组的数组名可以看作是一个指针

example

1
2
3
int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, value;
value = array[0]; //or: value = *array;
value = array[3]; //or: value = *(array + 3);

总结:

声明一个数组TYPE array[n],则这个数组名有两重含义:

代表整个数组,类型是TYPE[n]。

是一个常量指针,类型是TYPE*,该指针指向的类型是TYPE,该指针的内存区就是数组的第0号单元,且该指针自己占有单独的内存区,且该指针的值不能修改,array++是错误的写法,这不代表array[1]。

六、指针和结构类型的关系

结构类型还未学习,之后补充

七、指针和函数的关系

0. 可以把一个指针声明成一个指向函数的指针。

  • example
1
2
3
4
int fun1 (char*, int);
int (*pfun1)(char*, int);
pfun1 = fun1;
int a = (*pfun1)("abcdefg", 7);//通过函数指针调用函数

可以把指针作为函数的形参。在函数表达式中,可以用指针表达式来作为实参。

八、指针类型转换

初始化一个指针或者给一个指针赋值时,赋值号左边时一个指针,右边是一个指针表达式。绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和表达式所指向的类型是一样的。

1
2
3
float f = 12.3;
float *fptr = &f;
int *p;

如果我们想让指针p指向实数f,我们应该怎样操作?

是“ p = &f; ”吗?

不对,因为左右两侧的指针和指针所指向的类型不同。所以直接赋值不行。

为了实现这个目的,需要使用强制类型转换:

p = (int*) &f;

如果一个指针p,需要把它指向的类型改为”TYPE”,语法格式为:(TYPE*)p;

这样的类型转换得到一个新指针,类型为TYPE*,指向的地址就是原指针指向的地址。原来p的一切属性都没有被修改。

九、指针的安全问题