文章目录
为什么使用指针
函数的值传递,无法通过调用函数,来修改函数的实参
C语言中的一切函数调用中,值传递都是“按值传递”的。如果要在函数中修改被传递过来的对象,就必须通过这个对象的指针来完成。
#include<stdio.h>
void add_blood(int Blood){
Blood +=1000;
}
int main(void)
{
int blood=1;
add_blood(blood);
printf("%d\n",blood);
return 0;
}
运行上面的代码你会发现blood的值还是1,并没有加上1000。这是因为函数参数的传递都是值传递,main函数的blood存放在一个地址空间上,而函数中的Blood又是另外一个空间的,并不是main函数中的的地址。
只是把blood的值1赋值给Blood,相当于把函数中Blood的值初始化为1,然后函数在Blood等于1的基础上进行操作。并不会影响到主函数中的blood。
为什么不用全局变量,来避免这个问题
当然聪明的你可能会想到用全局定义某个变量来解决这个问题,能想到这里,说明你是一个善于思考的good boy。
#include<stdio.h>
int blood = 1;
void add_blood() {
blood += 1000;
}
int main(void)
{
add_blood();
printf("%d\n", blood);
return 0;
}
,粗略看起来,上面代码貌似可以完美解决值传递问题,但是,我们都知道,函数的作用是可以提高代码重复利用率,如果需求不只是blood加1000,那么我们是不是要定义很多变量,并且还要将他们加1000.。这岂不是与函数的本意相冲突吗?而且还会造成资源浪费。
被调用函数需要提供更多的“返回值”给调用函数
//加血成功返回 ture ,否则返回 false ;
bool add_blood2(int blood){
if(blood >= 1000) {
return false;
}else {
blood +=1000;
}
return true;
//另外还需要返回最新的血量,不能实现
}
减少值传递时带来的额外开销,提高代码执行效率
#include<stdio.h>
struct Game {
int level;//等级
const char name[15] = "hello world" ;//游戏名
char details[128];//状态描述
};
void text(struct Game game)
{
//打印地址,比较text中game是不是和主函数game一样
printf("text 中game的地址:0x%p\n", &game);
printf("text 中struct game 的大小:%d\n", sizeof(game));
}
int main(void)
{
Game game;
printf("struct game 的大小:%d\n", sizeof(game));
text(game);
printf("main 中game的地址:0x%p\n", &game);
return 0;
}
通过比较main中和text中的game的地址我们可以得到这样的结论,程序运行时有两个Game结构实体。那么有没有什么方法可以让我们的程序只有一个Game实体,从而减少开销。答案是肯定的,那就是指针。
如何使用指针
什么是指针
为了说清楚什么是指针,就必须搞清楚数据在内存中是如何存储的,又是如何读取的。如果在程序中定义了一个变量,在程序编译时,系统会根据程序中定义的变量类型给这个变量分配内存单元。指针就是存储这个内存单元地址的变量。
指针的定义
int room=10;
//定义了一个指针 名称是 p1, 它是一个指针,可以指向一个整数
int* p1;
int* p2, p3;//注意这样定义只有p2是指针
int* p4, * p5;// 应该这样定义
//指针 p1 指向了 room 也就是说: p1 的值就是一个变量room的地址!!!
p 1= &room;
printf("room的地址:0x%p\n", &room);
printf("p存的值:0x%p\n", p1);
指针的访问
访问指针
#include <stdio.h>
int main(void){
int room = 2 ;
//1.访问(读、写)指针变量本身的值,
//和其它普通变量的访问方式相同
int *p1 = &room;
//等价于int *p2 = &room;
int *p2 = p1;
printf("room 的地址: %d\n", &room);
printf("p1 的值: 0x%p p2 的值: 0x%p\n", p1, p2);
return 0;
}
访问指针所指向的内容
#include <stdio.h>
#include <stdlib.h>
int main(void){
int room = 2 ;
int * girl = &room;
int x = 0;
// *是一个特殊的运算符,*girl 表示读取指针 girl 所指向的 变量的值,
// *girl 相当于 room
x = *girl;
printf("x: %d\n", x);
//相当于 room = 4
*girl = 4;
printf("room: %d, *girl: %d\n", room, *girl);
system("pause");
return 0;
}
指针的算数运算
指针的自增运算
#include <stdio.h>
#include <stdlib.h>
int main(void){
int ages[]={21,15,18,14,23,28,10};
int len = sizeof(ages)/sizeof(ages[0]);
//使用数组的方式来访问数组
for( int i=0; i<len; i++){
printf("第%d 个学员的年龄是:%d\n", i+1, ages[i]);
}
//打印数组的地址和第一个成员的地址
printf("ages 的地址: 0x%p , 第一个元素的地址: 0x%p\n", ages, &ages[0]);
//访问第一个元素
int *p = ages;
printf("数组的第一个元素:%d\n", *p);
for(int i=0; i<len; i++){
printf("数组的第%d 个元素:%d 地址:0x%p\n", i+1, *p, p);
p++; //相当于 p = p+ 1*(sizeof(int))
}
char ch[4]={'a','b','c','d'};
char * cp = ch;
for(int i=0; i<4; i++){
printf("数组的第%d 个元素:%c 地址:0x%p\n", i+1, *cp, cp);
cp++; }
system("pause");
return 0;
}
总结: p++ 的概念是在 p 当前地址的基础上 ,自增 p 对应类型的大小 p = p+ 1*(sizeof(类型))
**注:**数组的地址是一段连续的地址。
指针常量与常量指针
1.指针常量:在指针常量中,指针自身的值是一个常量,不能改变,在定义的时候必须初始化,且指向的地址不可改变。
#include<stdio.h>
int main(void)
{
int age = 18;
int height = 180;
int* const p = &age;//指针常量必须初始化
//p = height;//指针指向的地址是固定的,不能发生改变
*p = 18;//但是可以改变指针p指向的地址内所存储的数据
return 0;
}
2.常量指针
在常量指针中,指针所指向的地址中的数据不可发生改变,但是指针指向的地址可以发生改变。
#include<stdio.h>
int main(void)
{
int age = 18;
int height = 180;
const int* p1 = &age;
int const* p2 = &age;//俩种写法都可以表示常量指针
p1 = &height;//指针指向的地址可以改变
age = 20;
//*p1 = 20;//不可以改变
return 0;
}
总结: 看 const 离类型(int)近,还是离指针变量名近,离谁近,就修饰谁,谁就 不能变