9.1.1取地址运算
sizeof
是一个运算符,给出某个类型或变量在内存中所占据的节字数 sizeof(int) sizeof(i)
运算符&
scanf("%d",&i);里的&
获得变量的地址,它的操作数必须是变量
int i;printf("%x",&i);
地址大小是否与int相同取决于编译器
int i;printf("%d",&i);
&不能对没有地址的东西取地址
&(a+b)? &(a++)? &(++a)?取地址符的右边必须有一个明确的变量,才可以取
相邻的位置数组之间的差距是4!(&a==a==a[0])
9.1.2指针:指针变量就是记录地址的变量
scanf
如果能够取得的变量的地址给一个函数,能否通过这个地址在那个函数内访问这个变量?
scanf("%d",&i);
scanf()的原型应该是什么样子?我们需要一个参数能保存别的变量地址,如何表达能够保存地址的变量?
指针
就是保存地址的变量
int i;
int* p=&i;*表示p是指针 指向int 将i的地址交给p(p里面的值就是i里面的地址,就叫做p指向i)
int * p,q;*靠近谁都表示p是指针 q是普通的int类型变量(*加给了p使得p成了指针)
int *p,q;
指针变量
变量的值是内存的地址
普通变量的值是实际的值
指针变量的值是具有实际值的变量地址
作为参数的指针
void f(int *p)
在被调用的时候得到某个变量的地址:
int i=0;f(&i);
在函数里面可以通过这个指针访问外面的这个i
访问那个地址上的变量*
*是一个单目运算符,用来访问指针的值所表示的地址上的变量
可以做右值也可以做左值
int k=*p;
*p=k+1;
传入地址
为什么
int i;scanf(“%d”,i);
编译没有报错?
9.1.3指针的使用
指针使用场景1:交换两个变量的值(swap函数)
void swap(int *pa,int *pb)
{
int t=*pa;
*pa=*pb;
*pb=t;
}
指针使用场景2a
函数返回多个值,某些值就只能通过指针返回
传入的参数实际上是需要保存带回的结果的变量
指针使用场景2b
函数返回运算的状态,结果通过指针返回
常用的套路是让函数返回特殊的不属于有效范围内的值来表述出错
-1或0(在文件操作会看到大量的例子)
但是当任何数值都是有效的可能结果时,就得分开返回了
指针最常见的错误
定义了指针变量,还没有指向任何变量,就开始使用指针
9.1.4指针与数组
传入的函数的数组成了什么?
函数参数表中的数组实际上是指针
sizeof(a)==sizeof(int*)
但是可以用数组的运算符[]进行运算
数组参数
以下四种函数原型是等价的:
int sum(int*ar,int n);
int sum (int*,int);
int sum(int ar[ ],int n);
int sum(int [ ],int);
数组变量是特殊的指针
数组变量本身表达地址,所以int a[10];int*p=a;//无需用&取地址
但是数组的单位表达的数量,需要用&取地址
a==&a[0]
[ ]运算符可以对数组做,也可以对指针做:
p[0]<==>a[0]
*运算符可以对指针做,也可以对数组做
数组变量是const的指针 所以不能被赋 值
9.1.5
指针是const
表示一旦到了某个变量的地址,不能再指向其他变量
int *const q=&i://q是const,q指向i的事实不能改变,q不能指向别人不能改变。
*q=26;//OK;
q++; //ERROR
所指是const
表示不能通过这个指针去修改变量(并不能使得那个变量成为const)
const int*p=&i;
*p=26;//ERRORR!(*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[]={1,2,3,4,5,6}
数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
所以必须通过初始化进行赋值
保护数组值
int sum(const int a[],int length);
9.2.1指针运算
算术运算这些可以对指针做:
给指针加,减一个整数(+,+=,-,-=)
递增递减(++/——)
两个指针相减
*p++
取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
*的优先级虽然高,但是没有++高
常用于数组类的连续空间操作
在某些CPU上,这可以直接被翻译成一条汇编语言
指针比较
<,<=,==,>,>=,!=都可以对指针做
比较它们在内存中的地址
数组中的单元的地址肯定是线性递增的
0地址
当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
所以你的指针不应该具有0
因此可以用0地址来表示特殊的事情:
返回的指针是无效的
指针没有被真正初始化(先初始化为0)
NULL是一个预定定义的符号,表示0地址
有的编译器不愿意你用0地址来表示0地址
指针的类型
无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
但是指向不同类型的指针是不能直接相互赋值的
这是为了避免用错指针
指针的类型转换
void*表示不知道指向什么东西的指针
计算时与char*相同(但不相通)
指针也可以转换类型
int *p=&i;void*q=(void*)p;
这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看他所指的变量
我不在当你是int啦,我认为你是个void!
用指针来做什么
需要传入较大的数据时用作参数
传入数组后对数组做操作
函数返回不止一个结果
需要用函数来修改不止一个变量
动态申请的内涵.......