本文主要是介绍指针,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
取地址运算
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()的原型应该是怎样的?我们需要⼀个参数 能保存别的变量的地址,如何表达能够保存地址 的变量?
2. 指针
就是保存地址的变量
int i;
int* p = &i;
int* p,q;
int *p,q;
3. 指针变量
- 普通变量的值是实际的值
- 指针变量的值是具有实际值的变量的地址
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 指针与数组
传⼊函数的数组成了什么?
- sizeof(a) == sizeof(int*)
- 但是可以⽤数组的运算符[]进⾏运算
14. 数组参数
以下四种函数原型是等价的:
int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
15.数组变量是特殊的指针
- int a[10]; int*p=a; // ⽆需⽤&取地址
- 但是数组的单元表达的是变量,需要⽤&取地址
- a == &a[0]
- p[0] <==> a[0]
- *a = 25;
- int a[] <==> int * const a=….
*9.1-4 指针与const
1.指针与const
2. 指针是const
- 表⽰⼀旦得到了某个变量的地址,不能再指向 其他变量
- int * const q = &i; // q 是 const
- *q = 26; // OK
- q++; // ERROR
3. 所指是const
- 表⽰不能通过这个指针去修改那个变量(并不 能使得那个变量成为const)
- const int *p = &i;
- *p = 26; // ERROR! (*p) 是 const
- i = 26; //OK
- p = &j; //OK
4. 这些是啥意思?
int i;
const int* p1 = &i;
int const* p2 = &i;
int *const p3 = &i;
判断哪个被const了的标志是const在*的前⾯还是后⾯
5. 转换
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?
- int a[10];
- int *p = a;
- *(p+1) —> a[1]
- 如果指针不是指向⼀⽚连续分配的空间, 如数组,则这种运算没有意义
2. 指针计算
- 给指针加、减⼀个整数(+, +=, -, -=)
- 递增递减(++/—)
- 两个指针相减
3. *p++
- 取出p所指的那个数据来,完事之后顺便 把p移到下⼀个位置去
- *的优先级虽然⾼,但是没有++⾼
- 常⽤于数组类的连续空间操作
- 在某些CPU上,这可以直接被翻译成⼀条 汇编指令
4. 指针⽐较
- <, <=, ==, >, >=, != 都可以对指针做
- ⽐较它们在内存中的地址
- 数组中的单元的地址肯定是线性递增的
5. 0地址
- 当然你的内存中有0地址,但是0地址通常是 个不能随便碰的地址
- 所以你的指针不应该具有0值
- 因此可以⽤0地址来表⽰特殊的事情:
- 返回的指针是⽆效的
- 指针没有被真正初始化(先初始化为0)
- 有的编译器不愿意你⽤0来表⽰0地址
6. 指针的类型
- ⽆论指向什么类型,所有的指针的⼤⼩都 是⼀样的,因为都是地址
- 但是指向不同类型的指针是不能直接互相 赋值的
- 这是为了避免⽤错指针
7. 指针的类型转换
- 计算时与char*相同(但不相通)
- int *p = &i; void*q = (void*)p;
- 这并没有改变p所指的变量的类型,⽽是让 后⼈⽤不同的眼光通过p看它所指的变量
- 我不再当你是int啦,我认为你就是个void!
8. ⽤指针来做什么
- 需要传⼊较⼤的数据时⽤作参数
- 传⼊数组后对数组做操作
- 函数返回不⽌⼀个结果
- 需要⽤函数来修改不⽌⼀个变量
动态内存分配
1.输⼊数据
- 如果输⼊数据时,先告诉你个数,然后再输⼊,要记 录每个数据
- C99可以⽤变量做数组定义的⼤⼩,C99之前呢?
- int *a = (int*)malloc(n*sizeof(int));
2. malloc
- #include void* malloc(size_t size);
- 向malloc申请的空间的⼤⼩是以字节为单位 的
- 返回的结果是void*,需要类型转换为⾃⼰ 需要的类型
- (int*)malloc(n*sizeof(int))
3.没空间了?
如果申请失败则返回0,或者叫做NULL
你的系统能给你多⼤的空间?
4. free()
- 把申请得来的空间还给“系统”
- 申请过的空间,最终都应该要还
- 混出来的,迟早都是要还的
5. 常⻅问题
- 新⼿:忘了
- ⽼⼿:找不到合适的free的时机
- free过了再free
- 地址变过了,直接去free
函数间传递指针
1. 好的模式
如果程序中要⽤到动态分配的内存,并且 会在函数之间传递,不要让函数申请内存 后返回给调⽤者
因为⼗有⼋九调⽤者会忘了free,或找不 到合适的时机来free
好的模式是让调⽤者⾃⼰申请,传地址进 函数,函数再返回这个地址出来
2. 函数返回指针?
- 本地变量(包括参数)?函数离开后这些变量就不 存在了,指针所指的是不能⽤的内存
- 传⼊的指针? 没问题
- 动态申请的内存? 没问题
- 全局变量—>以后再解释
函数返回数组?
- 如果⼀个函数的返回类型是数组,那么它实际返回 的也是数组的地址
- 如果这个数组是这个函数的本地变量,那么回到调 ⽤函数那⾥,这个数组就不存在了
- 所以只能返回
- 传⼊的参数:实际就是在调⽤者那⾥
- 全局变量或动态分配的内存
这篇关于指针的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!