指针杂谈
指针注意事项
-
指针必须声明,并且初始化
-
函数参数列表中的数组实际上是指针!!!所以一般需要传递数组长度,而不能使用sizeof(array) / sizeof(int)
-
*sizeof(array) == sizeof(int )
-
但是可以使用数组的运算符[ ]来进行运算
-
数组变量本身表达地址,所以
-
int a[10]; int *p = a;//无需使用&取地址
-
但是数组的单元表达的是变量,需要用&取地址
-
a == &a[0]
-
-
[]运算符也可以对数组做,也可以对指针做:
-
p[0] <==> a[0]
-
-
*运算符可以对指针做,也可以对数组做:
-
*a = 25;
-
-
数组变量是const的指针,所以不能被赋值
-
int a[] <==> int * const a=...
-
指针与const(适用于C99)
指针本身可以是const,其指向的值也可以是const
指针是const
表示一旦得到了某个变量的地址,不能再指向其他变量
int *const q = &i;//q是const
*q = 26;//OK
q++; //ERROR
指针指向的值是const
表示不能通过这个指针去修改那个变量(并不能使那个变量成为const),p可以指向其他变量,i也可以重新赋值为其他。不变的是:不能通过*p去做赋值!!!
const int *p = &i;
*p = 26; //ERROR! (*p)是const
i = 26; //OK
p = &j;//OK
练习
int i;
const int* p1 = &i; //是指不能通过p1去修改值---->通过指针不可修改
int const* p2 = &i; //同上
int *const p3 = &i; //p3不能再去指向其他变量---->指针不可修改
/*方法:判断哪个被const了的标志是const在*的前面还是后面?
const在前面,说明它所指向的东西不能被修改
const在后面,说明指针不能被修改
*/
技巧
保护数组值
因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值,为了保护数组内部不被函数破坏,可以设置为const
int sum(const a[], int length);
指针运算
指针相加
char a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
char *p = a;
printf("p = %p
", p);
printf("p+1 = %p
", p+1);
int b[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *q = b;
printf("q = %p
", q);
printf("q+1 = %p
", q+1);
指针+1的意思不是简单的地址值+1,而是指针指向下一个单元!!!
这也解释了为什么数组运算可以和指针一样,*p 就相当于a[0],*(p+1)就相当于a[1]
指针相减
指针相减计算结果不是他们之间的距离之差,而是他们地址之差/sizeof(类型)
特殊形式
*p++
- 取出p所指的数据,然后将p指针移到下一单元去
- *的优先级虽然高,但是没有++高
- 常用于数组类的连续空间操作
- 在某些CPU上,这可以直接被翻译为一条汇编指令
指针比较
- < <= == >= > !=都可以对指针做
- 比较它们在内存中的地址
- 数组中的单元的地址肯定都是线性递增的
0地址
- 内存中肯定存在0地址,但是通常0地址是一个不能随便碰的地址
- 所以地址在实际中不应该具有0值
- 因此可以使用0地址来表示特殊的事情:
- 返回的指针是无效的
- 指针没有被真正初始化(先初始化为0)
- NULL是一个预定定义的符号,表示0地址
- 有的编译器不愿意你用0来表示0地址
指针类型
- 无论指向什么类型,所有的指针的大小都是一样的,都是地址
- 但是指向不同类型的指针是不能直接互相赋值的,因为类型不同
- 以上是为了避免用错指针
指针的类型转换
-
void*表示不知道指向什么类型的指针
- 计算的时候与char*相同(但不相通)
-
指针也可以转换类型
-
int *p = &i; void *q = (void*)p;
-
这转换并没有改变p所指的变量的类型,i仍然是整型,而是让人们用不同的眼光通过p看它所指向的变量(我不再认为你是int类型,而是void)
-
指针用处
- 需要传入较大的数据时用作参数
- 传入数组后对数组做操作
- 函数返回不止一个结果
- 需要用函数来修改不止一个变量
- 动态内存的申请...
动态内存分配
在C99出来之后,可以使用变量来定义数组的大小,例如:
int num;
scanf("%d", &num);
int arr[num];
在C99之前,是需要动态申请内存的,例如:
int *a = (int*)malloc(n*sizeof(int)); //因为malloc返回的是void*,所以对于a来说还需要强转一下
...
free();
//动态申请之后,在程序结束时候,要free()一下,释放内存!
注意:
-
需要导入头文件<stdlib.h>
-
向malloc申请的空间的大小是以字节为单位的
-
返回的结果是void*,需要类型转换为自己需要的类型
-
(int*)malloc(n*sizeof(int))
-
free()
- 把申请得来的空间还给系统
- 申请过的空间都需要还
- 只能还申请来的空间的首地址,如果地址变过了,则会出错!
因为系统内存空间是有限的,如果系统在某个时间内存满了,此时malloc函数向系统申请内存空间,会怎么样呢?
- 如果申请失败则返回0,或者NULL
- 怎么知道系统能分配给你多大空间呢?