C语言学习
指针
-
运算符&
- eg:
scanf("%d",&i);
- 获取变量的地址,它的操作数必须是变量
int i;printf("%x",&i);
- 地址的打算小是否与int取决于编译器
int i;printf("%p",&i);
取地址用 %p
- &不能对没有地址的东西取地址
&(a+b)
&(a++)
&(++a)
- 相邻变量的地址,地址差一个变量类型字节大小,即相邻的变量是紧密存储的,C语言内存分配是从上向下分配,自顶向下。
- 对数组取地址
结果为:
- eg:
-
指针就是保存地址的变量
int i;int *p=&i;
- 指针变量
- 变量的值就是内存的地址
- 普通变量的值是实际的值
- 指针变量的值是具有实际值的变量的地址
- 变量的值就是内存的地址
- 作为参数的指针
void f(int *p);
- 在被调用时,得到了某个变量的地址:
int i=0;f(&i)
- 在函数里面可以通过这个指针访问外面的i,即可以在函数内改变i的值
*
是一个单目运算符,用来访问指针的值所表示的地址上的变量- 可以做右值也可以做左值
int k=*p;*p=k+1;
*p
当作整体其实就是一个值
- 在被调用时,得到了某个变量的地址:
- 指针变量
-
指针的运算符
&*
- 相互反作用
*& yptr -> *(&yptr) -> *(yptr的地址) -> 得到那个地址上的变量 -> yptr
&* yptr -> &(*yptr) -> &(y) -> 得到y的地址,也就是yptr -> yptr
- 相互反作用
-
指针的应用
-
交换两个变量的值交换两个变量的值
-
函数返回多个值,某些值只能通过指针返回
- 传入的参数实际上是需要保存带回的结果的变量
- 结果为
min=1,max=55
- 传入的参数实际上是需要保存带回的结果的变量
-
函数需要返回状态和值,值通过指针返回
- 函数
return
状态,结果通过指针返回状态
- 函数
-
-
指针常见错误
- 定义了指针变量,还没有指向任何变量,就开始使用指针
int *p;*p=12;
会出错
- 定义了指针变量,还没有指向任何变量,就开始使用指针
-
指针与数组
- 传入函数的数组其实是指针
函数传参的int a[] = int *a
- 数组变量是特殊的指针
- 数组变量本身表达地址
int a[10]; int *p = a;
无需采用&取地址a == &a[0];
但数组的单元表达的是变量,需要用&取地址
[]
运算符可以对数组做,也可以对指针做p[0] <==> a[0]
*
运算符可以对数组做,也可以对指针做*a = 25;
- 数组变量是const的指针,所以不能被赋值
- 数组变量本身表达地址
- 传入函数的数组其实是指针
-
指针与const
- 指针是const
- 一但得到某个变量的地址,不能再指向其他变量
int *const q = &i;//q是const
*q = 26;//OK
q++;//ERROR
- 一但得到某个变量的地址,不能再指向其他变量
- 所指是const
- 不能通过指针去修改那个变量(并不能使那个变量成为const)
const int *p = &i;
*p = 26;//ERROR (*p是const)
i = 26;//OK
p = &j;//OK
- 不能通过指针去修改那个变量(并不能使那个变量成为const)
const int *p1 = &i; <==> int const *p2 = &i;
均为所指为const,即*p
为constint *const p3 = &i;
指针为const,不能被修改- const数组
const int a[] = {1,2,3,5,8};
- 数组变量已经是const指针,这里const指数组内每个元素都是const int
- 所以必须在初始化时赋值,否则就不能赋值了
- 保护数组值
- 因为数组传入函数时,传的是地址,所以函数内部可以修改数组的值
- 为了保护数组不被函数修改破坏,可以设置参数为const
int sum(const int a[],int len);
- 指针是const
-
指针运算
- 对指针+1,其实是在地址上加一个数据类型的sizeof,即指向下一个元素
*p --> a[0];*(p+1) --> a[1]
- 只有连续的元素(数组),这样+1才是有意义的
- 包括
-,++,--
*p++
- 取出p所指的数据来,完事之后顺便把p移到下一个位置
*
的优先级虽然高,但没有++
优先级高- 常用于数组类的连续空间操作(遍历)
- 对指针+1,其实是在地址上加一个数据类型的sizeof,即指向下一个元素
-
指针比较
<,>,<=,>=,==,!=
都可以对指针做- 比较它们在内存中的地址
- 数组中的单元的地址肯定是线性递增的
-
不同类型指针
- 不论指向什么类型,所有指针大小都相同,因为都是地址
- 但指向不同类型的指针是不能直接相互赋值的
-
指针的类型转换
void*
表示不知道指向什么东西的指针- 计算时与
char*
相同(但不相通)
- 计算时与
- 指针也可以转换类型
int *p = &i;void *q = (void*)p;
- 并没有改变p所指的变量类型,而是让后人用不同眼光通过p看到它所指的变量,不再是int,而是void,p去看i是int,q去看i是void
-
动态申请内存
int *a = (int *)malloc(n*sizeof(int));
- 要使用malloc需要引入
#include <stdlib.h>
- 用完需要
free(a)
- 申请的都要还,并且最终都要还
- 只能还申请来的空间首地址
- 常见问题
- 申请了没free,导致内存逐渐下降
- 忘了
- 找不到合适时机进行free
- 已经free,继续free
- 地址变过了,直接去free
- 申请了没free,导致内存逐渐下降
- 向malloc申请的空间大小是以字节为单位的
- 返回结果是
void*
,需要类型转换为自己需要的类型(int*)malloc(n*sizeof(int))
- 若系统没空间了,会返回0/NULL
补充:32位架构:int -> 4字节 地址 -> 4字节(sizeof(&i)
)
64位架构:int -> 4字节 地址 -> 8字节(sizeof(&i)
)
- 要使用malloc需要引入