Java教程

指针

本文主要是介绍指针,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

 

取地址运算

1 . 运算符 &

  • scanf(“%d”, &i);⾥的&
  • 获得变量的地址,它的操作数必须是变量,int i; printf(“%x”,&i);
  • 地址的⼤⼩是否与int相同取决于编译器,int i; printf(“%p”,&i);

 

2. &不能取的地址

&不能对没有地址的东⻄取地址

&(a+b)?

&(a++)?

&(++a)?

 

3. 试试这些&

变量的地址

相邻的变量的地址

&的结果的sizeof

数组的地址

数组单元的地址

相邻的数组单元的地址

 

 

 

指针

1. scanf

如果能够将取得的变量的地址传递给⼀个函数, 能否通过这个地址在那个函数内访问这个变量?

  • scanf(“%d”, &i);

scanf()的原型应该是怎样的?我们需要⼀个参数 能保存别的变量的地址,如何表达能够保存地址 的变量?

 

2. 指针

就是保存地址的变量

int i;

int* p = &i;

int* p,q;

int *p,q;

 

3. 指针变量

  • 变量的值是内存的地址
  1. 普通变量的值是实际的值
  2. 指针变量的值是具有实际值的变量的地址

 

 

 

 

 

4. 作为参数的指针

  • void f(int *p);
  • 在被调⽤的时候得到了某个变量的地址:int i=0; f(&i);
  • 在函数⾥⾯可以通过这个指针访问外⾯的这个i

 

 

5. 访问那个地址上的变量*

  • *是⼀个单⺫运算符,⽤来访问指针的值所表⽰的地 址上的变量
  • 可以做右值也可以做左值int k = *p;*p = k+1;

 

 

6. *左值之所以叫左值

  • 是因为出现在赋值号左边的不是变量,⽽是值,是表 达式计算的结果:a[0] = 2;   *p = 3;
  • 是特殊的值,所以叫做左值

 

7. 指针的运算符&*

互相反作⽤

*&yptr -> * (&yptr) -> * (yptr的地 址)-> 得到那个地址上的变量 -> yptr

&*yptr -> &(*yptr) -> &(y) -> 得到y的地 址,也就是yptr -> yptr

 

 

8. 传⼊地址

  • 为什么 ? int i; scanf(“%d”, i);
  • 编译没有报错?

 

9.  指针应⽤场景⼀

交换两个变量的值

void swap(int *pa, int *pb){
    int t = *pa;
    *pa = *pb;
    *pb = t;
}

 

 

10. 指针应⽤场景⼆

  • 函数返回多个值,某些值就只能通过指针返回
  • 传⼊的参数实际上是需要保存带回的结果的变量

 

11. 指针应⽤场景⼆b

  • 函数返回运算的状态,结果通过指针返回
  • 常⽤的套路是让函数返回特殊的不属于有效范围内的 值来表⽰出错:-1或0(在⽂件操作会看到⼤量的例⼦)
  • 但是当任何数值都是有效的可能结果时,就得分开返 回了,后续的语⾔(C++,Java)采⽤了异常机制来解决这 个问题

 

12. 指针最常⻅的错误

  • 定义了指针变量,还没有指向任何变量,就开始使⽤ 指针

 

13. 9.1-3 指针与数组

传⼊函数的数组成了什么?

 

 

  • 函数参数表中的数组实际上是指针
  1. sizeof(a) == sizeof(int*)
  2. 但是可以⽤数组的运算符[]进⾏运算

 

 

14. 数组参数

以下四种函数原型是等价的:

int sum(int *ar, int n);

int sum(int *, int);

int sum(int ar[], int n);

int sum(int [], int);

 

 

15.数组变量是特殊的指针

  • 数组变量本⾝表达地址,所以
  1. int a[10]; int*p=a; // ⽆需⽤&取地址
  2. 但是数组的单元表达的是变量,需要⽤&取地址
  3. a == &a[0]
  • []运算符可以对数组做,也可以对指针做:
  1. p[0] <==> a[0]
  • *运算符可以对指针做,也可以对数组做:
  1. *a = 25;
  • 数组变量是const的指针,所以不能被赋值
  1. int a[] <==> int * const a=….

 

 

 

 

*9.1-4 指针与const

1.指针与const

 

 

 

 

2. 指针是const

  • 表⽰⼀旦得到了某个变量的地址,不能再指向 其他变量
  1. int * const q = &i; // q 是 const
  2. *q = 26; // OK
  3. q++; // ERROR

 

3. 所指是const

  • 表⽰不能通过这个指针去修改那个变量(并不 能使得那个变量成为const)
  1. const int *p = &i;
  2. *p = 26; // ERROR! (*p) 是 const
  3. i = 26; //OK
  4. p = &j; //OK

 

4. 这些是啥意思?

int i;

const int* p1 = &i;

int const* p2 = &i;

int *const p3 = &i;

判断哪个被const了的标志是const在*的前⾯还是后⾯

 

5. 转换

  • 总是可以把⼀个⾮const的值转换成const的

void f(const int* x);

int a = 15; f(&a);// ok

const int b = a;

f(&b); // ok

b = a + 1; // Error!

  • 当要传递的参数的类型⽐地址⼤的时候,这是常⽤的⼿ 段:既能⽤⽐较少的字节数传递值给参数,⼜能避免函 数对外⾯的变量的修改

 

6. const数组

const int a[] = {1,2,3,4,5,6,};

数组变量已经是const的指针了,这⾥的const 表明数组的每个单元都是const int

所以必须通过初始化进⾏赋值

 

 

7. 保护数组值

因为把数组传⼊函数时传递的是地址,所以那个函数 内部可以修改数组的值

为了保护数组不被函数破坏,可以设置参数为const

  • int sum(const int a[], int length);

 

 

 

 

 

 

指针是可计算的

1. 1+1=2?

  • 给⼀个指针加1表⽰要让指针指向下⼀个 变量
  1. int a[10];
  2. int *p = a;
  3. *(p+1) —> a[1]
  • 如果指针不是指向⼀⽚连续分配的空间, 如数组,则这种运算没有意义

 

2. 指针计算

  • 这些算术运算可以对指针做:
  1. 给指针加、减⼀个整数(+, +=, -, -=)
  2. 递增递减(++/—)
  3. 两个指针相减

 

3. *p++

  • 取出p所指的那个数据来,完事之后顺便 把p移到下⼀个位置去
  • *的优先级虽然⾼,但是没有++⾼
  • 常⽤于数组类的连续空间操作
  • 在某些CPU上,这可以直接被翻译成⼀条 汇编指令

 

4. 指针⽐较

  • <, <=, ==, >, >=, != 都可以对指针做
  • ⽐较它们在内存中的地址
  • 数组中的单元的地址肯定是线性递增的

 

5. 0地址

  • 当然你的内存中有0地址,但是0地址通常是 个不能随便碰的地址
  • 所以你的指针不应该具有0值
  • 因此可以⽤0地址来表⽰特殊的事情:
  1. 返回的指针是⽆效的
  2. 指针没有被真正初始化(先初始化为0)
  • NULL是⼀个预定定义的符号,表⽰0地址
  1. 有的编译器不愿意你⽤0来表⽰0地址

 

 

6. 指针的类型

  • ⽆论指向什么类型,所有的指针的⼤⼩都 是⼀样的,因为都是地址
  • 但是指向不同类型的指针是不能直接互相 赋值的
  • 这是为了避免⽤错指针

 

7. 指针的类型转换

  • void* 表⽰不知道指向什么东⻄的指针
  1. 计算时与char*相同(但不相通)
  • 指针也可以转换类型
  1. int *p = &i; void*q = (void*)p;
  • 这并没有改变p所指的变量的类型,⽽是让 后⼈⽤不同的眼光通过p看它所指的变量
  1. 我不再当你是int啦,我认为你就是个void!

 

 

8. ⽤指针来做什么

  • 需要传⼊较⼤的数据时⽤作参数
  • 传⼊数组后对数组做操作
  • 函数返回不⽌⼀个结果
  1. 需要⽤函数来修改不⽌⼀个变量
  • 动态申请的内存...

 

 

 

 

动态内存分配

1.输⼊数据

  • 如果输⼊数据时,先告诉你个数,然后再输⼊,要记 录每个数据
  • C99可以⽤变量做数组定义的⼤⼩,C99之前呢?
  • int *a = (int*)malloc(n*sizeof(int));

 

 

2. malloc

  • #include void* malloc(size_t size);
  • 向malloc申请的空间的⼤⼩是以字节为单位 的
  • 返回的结果是void*,需要类型转换为⾃⼰ 需要的类型
  1. (int*)malloc(n*sizeof(int))

 

 

3.没空间了?

如果申请失败则返回0,或者叫做NULL 

你的系统能给你多⼤的空间?

 

4. free()

  • 把申请得来的空间还给“系统”
  • 申请过的空间,最终都应该要还
  1. 混出来的,迟早都是要还的
  • 只能还申请来的空间的⾸地址 • free(0)?

 

5. 常⻅问题

  • 申请了没free—>⻓时间运⾏内存逐渐下降
  1. 新⼿:忘了
  2. ⽼⼿:找不到合适的free的时机
  • free过了再free
  • 地址变过了,直接去free

 

 

 

函数间传递指针

1. 好的模式

如果程序中要⽤到动态分配的内存,并且 会在函数之间传递,不要让函数申请内存 后返回给调⽤者

因为⼗有⼋九调⽤者会忘了free,或找不 到合适的时机来free

好的模式是让调⽤者⾃⼰申请,传地址进 函数,函数再返回这个地址出来

 

 

 

 

2. 函数返回指针?

  • 返回指针没问题,关键是谁的地址?
  1. 本地变量(包括参数)?函数离开后这些变量就不 存在了,指针所指的是不能⽤的内存
  2. 传⼊的指针? 没问题
  3. 动态申请的内存? 没问题
  4. 全局变量—>以后再解释

 

 

函数返回数组?

  • 如果⼀个函数的返回类型是数组,那么它实际返回 的也是数组的地址
  • 如果这个数组是这个函数的本地变量,那么回到调 ⽤函数那⾥,这个数组就不存在了
  • 所以只能返回
  1. 传⼊的参数:实际就是在调⽤者那⾥
  2. 全局变量或动态分配的内存

 

这篇关于指针的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!