重要
printf("%d",i);
scanf("%d",&i);

说某个变量的地址时,讲的都是它的
起始地址
直接访问:
访问地址拿到宝藏
间接访问:
把地址看作藏宝图
通过指针变量 - 找到地址(藏宝图) - 再拿到宝藏
即多了一次跳转
/*基类型 *指针变量名;*/
int *i_pointer;//定义了一个指针变量,i_pointer是指针变量名
一个变量的地址称为该变量的
“指针”。
如果有一个变量专门用来存放另一变量的地址(即指针),那么称它为“指针变量”。
本章中编写的程序都是64位应用程序,寻址范围为64位即8字节,所以对于本章来说sizeof(i_pointer)=8。如果编写的程序是32位,那么寻址范围就是4字节(考研中往往会强调程序是32位的程序)。
通过该操作符我们可以获取一个变量的地址值。
通过该操作符我们可以得到一个地址对应的数据。
#include
int main()
{
//&符号是取地址,指针变量的初始化一定是某个变量取地址
int i = 5;
//指针变量的初始化是某个变量取地址来赋值,不能随机写个数
//指针变量的基类型与所指向的变量的基类型相同
int* p = &i;
printf("i = %d\n", i);//直接访问
printf("p = %d\n", *p);//间接访问
return 0;
}
注意!!
(1)指针变量前面的“*”表示该变量为指针型变量。
例如,float *pointer_1;
- 1
注意指针变量名是pointer_1,而不是*pointer_1。
(2)在定义指针变量时必须指定其类型。
需要注意的是,指针变量的基类型与所指向的变量的基类型相同。例如,下面的赋值是错误的:float a; int *pointer_1; pointer_1 = &a; //毫无意义而且会出错
- 1
- 2
- 3
(3)如果已执行了语句pointer_1 = &a;
- 1
那么
&*pointer_1的含义是什么呢?(犯傻)
“&”和“*”两个运算符的优先级别相同,但要按自右向左的方向结合。因
此,&*pointer_1与*&a相同,都表示变量a的地址,也就是pointer_1。
*&a的含义是什么呢?
首先进行&a运算,得到a的地址,再进行*运算。*&a和*pointer_1的作用是一样的,它们都等价于变量a,即*&a 与a等价。
(4)为什么要让*和指针变量连着
C语言本质上是一种自由形式的语言,这很容易诱使我们把“*”写在靠近类型的一侧,如int* a这个声明与int *a具有相同的意思,而且看上去更清晰,a被声明成类型为int*的指针。
但是,这并不是一个好习惯,因为类似int* a,b,c的语句会使人们很自然地认为这条语句把所有三个变量声明为指向整型的指针,但事实上并非如此,“*”实际上是*a的一部分,只对a标识符起作用,但其余两个变量只是普通的整型变量。
要声明三个指针变量,正确的语句如下:int *a, *b, *c;
- 1
指针的使用场景通常只有两个,即传递与偏移
程序启动起来就是进程
#include
//在子函数中去改变主函数中某个变量的值
void change(int j)//j是形参
{
j = 5;
}
int main()
{
int i = 10;
printf("before change i = %d\n", i);
change(i);//C语言的函数调用是值传递,实参赋值给形参,i是实参
//j = i - 把i赋值给了j
printf("after change i = %d\n", i);
return 0;
}
运行结果:

change后
i
i
i的值没有改变。
C语言的函数调用是值传递,实参赋值给形参, i i i是实参, j j j是形参。
运行change -j = i-把i赋值给了j
然后j = 5- j的值改变了
所以 i i i的值不会改变!
j = i,然后执行j = 5【例1.1】的原理图:

变量
i
i
i在
m
a
i
n
main
main函数中;
变量
j
j
j在
c
h
a
n
g
e
change
change函数中。
任何时候都是实参赋值给形参
#include
//在子函数中去改变主函数中某个变量的值
void change(int *j)//j是形参
{
*j = 5;//*j等价于变量i,只是间接访问
//间接访问得到变量i
}
//指针的传递
int main()
{
int i = 10;
printf("before change i = %d\n", i);
change(&i);//传递变量i的地址 - j = &i
printf("after change i = %d\n", i);
return 0;
}
运行结果:
程序执行后,
i
i
i的值变成了5。
将变量
i
i
i的地址传递给change函数时,实际效果是j=&i,依然是值传递,只是这时
j
j
j是一个指针变量,内部存储的是变量
i
i
i的地址,所以通过*j就间接访问到了与变量
i
i
i相同的区域,通过*j=5就实现了对变量
i
i
i的值的改变。
指针即地址,就像是找到了一栋楼,这栋楼的楼号是B,那么往前就是A,往后就是C,所以应用指针的另一个场景就是对其进行加减,但对指针进行乘除是没有意义的,就像家庭地址乘以5没有意义那样。在工作中,我们把对指针的加减称为指针的偏移,加就是向后偏移,减就是向前偏移。
实现数组的正序输出和逆序输出:
#include
//指针的偏移使用场景,也就是对指针的加减
#define N 5//定义一个符号常量
int main()
{
int a[N] = { 1,2,3,4,5 };//数组名内存储了数组的起始地址,a中存储的就是一个地址值
int *p;//定义指针变量p
p = a;//保证等号两边的数值类型一致 - p中存储数组起始地址
int i;
for (i = 0; i < N; i++)//正序输出
{
//printf("%3d", a[i]);//等价下一句
printf("%3d", *(p + i));//*(p + 0)拿到a[0];*(p + 1)拿到a[1]……
}
printf("\n-----------------\n");//分隔
p = &a[4];//对整型变量取地址 - 指针变量p指向了数组的最后一个元素
for (i = 0; i < N; i++)//逆序输出
{
printf("%3d", *(p - i));
}
printf("\n");
return 0;
}
数组名内存储了数组的起始地址,a中存储的就是一个地址值
运行结果:

*p可以得到元素a[0]。*(p+1)就可以得到元素a[1]。直接写
*dvoid change(char *d)
- 1
#include
//指针与一维数组的传递
//数组名作为实参传递给子函数是,是弱化为指针的
void change(char *d)//数组名存储的是一个指针值,形参写指针变量,不写数组变量
{
*d = 'H';
d[1] = 'E';//等价于*(d+1) = 'E';
*(d + 2) = 'L';
}
int main()
{
char c[10] = "hello";
change(c);
puts(c);
return 0;
}
运行结果:
#include
#include //malloc需要使用的头文件
#include
int main()
{
int size;//size代表我们要申请多大字节的空间
char* p;//void*类型的指针是不能偏移的,因此自己不会去定义无类型指针
scanf("%d", &size);//输入要申请的空间大小
//注意指针本身大小,和其指向的空间大小,是两码事,不能和前面的变量类比去理解!
//malloc返回的void*代表无类型指针
//使用malloc动态申请堆空间
p = (char*)malloc(size); //强制类型转换与p的类型一致
//p[0] = 'H';
//p[1] = 'O';
//p[2] = 'W';
//p[3] = '\0';
strcpy(p, "malloc success");
puts(p);
//不用时要free!释放申请的空间!
//释放申请的空间时,给的地址,必须是最初malloc返回给我们的地址
free(p); //free时必须使用malloc申请时返回的指针值,不能进行任何偏移
printf("free success\n");
return 0;
}

堆是动态的,但是堆的效率比栈低得多。
记住即可
#include
#include
#include
//堆和栈的差异
//函数栈空间释放后,函数内的所有局部变量消失
//低级错误
char* print_stack()//栈空间stack
{
char c[100] = "I am print_stack func";//栈空间在子函数结束后被释放了
char *p;
p = c;
puts(p);//可以打印出I am print_stack func
return p;
}
//堆空间不会因为函数执行结束而释放
char* print_malloc()//堆空间heap
{
char* p = (char*)malloc(100);//堆空间在整个进程中一直有效,不因为函数结束而消亡
strcpy(p, "I am print malloc func");
puts(p);
return p;
}
int main()
{
char *p;
p = print_stack();//数据放在栈空间
puts(p);//子函数中造的数据到主函数中打印出乱码
//是因为栈空间已经被释放掉了
p = print_malloc();//数据放在堆空间
puts(p);
free(p);//只有free时,堆空间才会释放
return 0;
}
printf("%d",i);
scanf("%d",&i);
/*基类型 *指针变量名;*/
int *i_pointer;//定义了一个指针变量,i_pointer是指针变量名
通过该操作符我们可以获取一个变量的地址值。
通过该操作符我们可以得到一个地址对应的数据。