运算符 &
- scanf(“%d”,&i);里的&
获得变量的地址,它的操作数必须是变量
- int i; printf(“%x”,&i);
地址的大小是否与int相同取决于编译器 |
- inti; printf(“%p”,&i);
scanf
如果能够将取得的变量的地址传递给一个函数, 能否通过这个地址在那个函数内访问这个变量?
- scanf(“%d”,&i);
- scanf()的原型应该是怎样的?我们需要一个参数 能保存别的变量的地址,如何表达能够保存地址 的变量?
指针
- 就是保存地址的变量
int i;
int*p=&i;
int*p,q;
int*p,q;
指针变量
变量的值是内存的地址
- 普通变量的值是实际的值
- 指针变量的值是具有实际值的变量的地址
作为参数的指针
- void f(int *p);
在被调用的时候得到了某个变量的地址:
- int i=0;f(&i);
- 在函数里面可以通过这个指针访问外面的这个i
访问那个地址上的变量
- *是一个单目运算符,用来访问指针的值所表示的地 址上的变量
可以做右值也可以做左值 。
- int k= *p;
- *p=k+1;
左值之所以叫左值
是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果:
- a[0]=2;
- *p=3;
- 是特殊的值,所以叫做左值
指针的应用场景
场景一
- 交换两个变量的值
void swap(int *pa, int *pb){
int t=*pa;
*pa=*pb;
*pb=t;
}
场景二
- 函数返回多个值,某些值就只能通过指针返回 ·
- 传入的参数实际上是需要保存带回的结果的变量
场景二B
- 函数返回运算的状态,结果通过指针返回
- 常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:
- -1或0(在文件操作会看到大量的例子)
- 但是当任何数值都是有效的可能结果时,就得分开返回了
- 后续的语言(C++,Java)采用了异常机制来解决这个问题
指针常见错误
- 定义了指针变量,还没有指向任何变量,就开始使用指针
指针与数组
数组变量是特殊的指针
数组变量本身表达地址,所以
- int a[10];int*p=a;//无需用&取地址
- 但是数组的单元表达的是变量,需要用&取地址
- a==&a[0]
- []运算符可以对数组做,也可以对指针做:
- p[0]<==>a[0]
- *运算符可以对指针做,也可以对数组做
- *a=25;
数组变量是const的指针,所以不能被赋值
- int a[]< :==> int*const a
指针与const(只适用于C99)
指针是const
表示一旦得到了某个变量的地址,不能再指向 其他变量 ·
- int*constq=&i;//q是const
- *q=26;1/ OK
- q++; // ERROR
所指是const
表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const) )
- const int *p=&i;
- p=26;//ERROR!(p)是const
- i=26;//OK
- P=&j;//OK
这些是啥意思?
int i;
const int* p1 = &i;
int const* p2= &i;
int *const p3= &i;
判断哪个被const了的标志是const在*的前面还是后面。
转换
- 总是可以把一个非const的值转换成const的
void f(const int*_x);
int a=15;
f(&a);// ok
const'int b = a;
f(&b);// ok
b=a'+1;// Error!
- 当要传递的参数的类型比地址大的时候,.这是常用的手 段:既能用比较少的字节数传递值给参数,又能避免函 数对外面的变量的修改
const数组
- const int a[]={l,2,3,4,5,6,};
- 数组变量已经是const的指针了,这里的const 表明数组的每个单元都是constint
- 所以必须通过初始化进行赋值
保护数组值
- 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
- 为了保护数组不被函数破坏,可以设置参数为const
- int sum(const int a[],int length);
指针运算
- 给一个指针加I表示要让指针指向下一个变量
- int a[10]; intp=a; (p+I)—> a[l]
- 如果指针不是指向一片连续分配的空间, 如数组,则这种运算没有意义
*p++
- 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
- *的优先级虽然高,但是没有++高
- 常用于数组类的连续空间操作
- 在某些CPU上,这可以直接被翻译成一条汇编指令
指针比较
- <,<=,==,>,>=,!= 都可以对指针做
- 比较它们在内存中的地址
- 数组中的单元的地址肯定是线性递增的
0地址
- 当然你的内存中有0地址,但是0地址通常是 个不能随便碰的地址
- 所以你的指针不应该具有0值
因此可以用0地址来表示特殊的事情:
- 返回的指针是无效的
- 指针没有被真正初始化(先初始化为0)
NULL是一个预定定义的符号,表示0地址
- 有的编译器不愿意你用0来表示0地址
指针的类型
- 无论指向什么类型,所有的指针的大小都 是一样的,因为都是地址
- 但是指向不同类型的指针是不能直接互相 赋值的
- 这是为了避免用错指针
指针的类型转换
void* 表示不知道指向什么东西的指针
- 计算时与char相同(但不相通)
指针也可以转换类型
- int p=&i; voidq=(void*)p
这并没有改变p所指的变量的类型,而是让 后人用不同的眼光通过p看它所指的变量
- 我不再当你是int啦,我认为你就是个void!
用指针来做什么
- 需要传入较大的数据时用作参数
传入数组后对数组做操作函数返回不止一个结果
- 需要用函数来修改不止一个变量
- 动态申请的内存...