指针


取地址运算

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. 全局变量或动态分配的内存