为什么要使用指针
函数的值传递,无法通过调用函数,来修改函数的实参
被调用函数需要提供更多的“返回值”给调用函数
减少值传递时带来的额外开销,提高代码执行效率
指针的初始化、访问
指针的初始化
#include <stdio.h>
#include <stdlib.h>
int main() {
int room = 2;
int *p1 = &room;
int *p2 = &room;
printf("room address: 0x%p\n", &room);
printf("p1 address: 0x%p\n", &p1);
printf("p2 address: 0x%p\n", &p2);
printf("room Number of bytes: %d\n", sizeof(room));
printf("p1 Number of bytes: %d\n", sizeof(p1));
printf("p2 Number of bytes: %d\n", sizeof(p2));
return 0;
}
注意:
32 位系统中,int 整数占 4 个字节,指针同样占 4 个字节
64 位系统中,int 整数占 4 个字节,指针同样占 8 个字节
指针的访问
访问指针
#include <stdio.h>
#include <stdlib.h>
int main() {
int room = 2;
int room1 = 3;
int *p1 = &room;
int *p2 = p1;
int *p3 = p1;
printf("room address: %d\n", &room);
printf("p1 value: %d p2 value: %d\n", p1, p2);
printf("p3 value: %d\n", p3);
p3 = &room1;
// not recommend
printf("p3 value: %d, room1 address: %d\n", p3, &room1);
// recommend
printf("p1=0x%p\n", p1);
printf("p1=0x%x\n", p1);
printf("p1=0x%X\n", p1);
return 0;
}
运行结果:
访问指针所指向的内容
#include <stdio.h>
#include <stdlib.h>
int main() {
int room = 2;
int * girl = &room;
int x = 0;
x = *girl;
printf("x: %d\n", x);
*girl = 4;
printf("room: %d, *girl: %d\n", room, *girl);
return 0;
}
运行结果:
空指针和坏指针
#include <stdio.h>
#include <stdlib.h>
int main() {
int room1 = 666;
int room2 = 888;
int girl;
int *select;
scanf("%d", &girl);
if (girl == 666){
select = &room1;
}else if (girl == 888) {
select = &room2;
}
printf("The chosen room is: %d\n", *select);
return 0;
}
运行结果:
1. 什么是空指针?
2. 空指针,就是值为 0 的指针。(任何程序数据都不会存储在地址为 0 的内存块中,它是被操作系 统预留的内存块。)
int *p = 0;
或者
int *p = NULL; //强烈推荐
2. 空指针的使用
1)指针初始化为空指针
int *select = NULL;
目的就是,避免访问非法数据。
2)指针不再使用时,可以设置为空指针
int *select = &xiao_long_lv;
//和小龙女约会
select = NULL;
3)表示这个指针还没有具体的指向,使用前进行合法性判断
int *p = NULL;
// 。。。。
if § { //p 等同于 p!=NULL
//指针不为空,对指针进行操作
}
3. 坏指针
int *select; //没有初始化
情形一
printf(“选择的房间是: %d\n”, *select);
情形二
select = 100;
printf(“选择的房间是: %d\n”, *select);
渣男、直男、暖男的区别:const
#include <stdio.h>
#include <stdlib.h>
// const pointer
int main() {
int wife = 24;
int girl = 18;
// The first kind of
int * zha_nan = &wife;
*zha_nan = 25;
zha_nan = &girl;
*zha_nan = 19;
printf("girl : %d wife: %d\n", girl, wife);
// The second
int const * zhi_nan = &wife;
printf("zhi_nan Wife's age: %d\n", *zhi_nan);
zhi_nan = &girl;
printf("zhi_nan Girlfriend's age: %d\n", *zhi_nan);
// the third type
int * const nuan_nan = &wife;
*nuan_nan = 26;
printf("nuan_nan Wife's age: %d\n", wife);
// A fourth
const int * const super_nuan_nan = &wife;
return 0;
}
总结:
看 const 离类型(int)近,还是离指针变量名近,离谁近,就修饰谁,谁就 不能变
指针的算术运算
指针的自增运算
#include <stdio.h>
#include <stdlib.h>
int main() {
int ages[] = {21, 15, 18, 14, 23, 28, 10};
int len = sizeof(ages)/sizeof(ages[0]);
// Methods a
for (int i = 0; i < len; i++) {
printf("The age of the %d cadet is: %d\n", i + 1, ages[i]);
}
printf("ages address: 0x%p, The address of the first element : 0x%p\n", ages, &ages[0]);
int *p = ages;
printf("The first element of the array: %d\n", *p);
for (int i = 0; i < len; i++) {
printf("The %d element of the array: %d address: 0x%p\n", i+1,
*p, p);
p++;
}
printf("======\n");
char ch[4] = {'a', 'b', 'c', 'd'};
char *cp = ch;
for (int i = 0; i < 4; i++) {
printf("The %d element of the array: %d address: 0x%p\n", i+1,
*cp, cp);
cp++;
}
return 0;
}
运行结果:
总结:
p++ 的概念是在 p 当前地址的基础上 ,自增 p 对应类型的大小 p = p+ 1*(sizeof(类型))
指针的自减运算
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// as "12345" Reverse into "54321" output
int main() {
char input[128];
int len;
char tmp;
scanf("%s", input, 128);
len = strlen(input);
// Methods a
/*for (int i = 0; i < len/2; i++) {
tmp = input[i];
input[i] = input[len-i-1];
input[len-i] = tmp;
}
for (int i = 0; i < len; i++) {
printf("%c", input[i]);
}
printf("After the reversal: %s\n", input);
*/
// The second way
/* for (int i = 0; i < len; i++) {
printf("%c", input[len-i-1]);
}
printf("\n");
*/
// Third way
char *p = &input[len-1];
for (int i = 0; i < len; i++) {
printf("%c", *p);
p--;
}
printf("\n");
return 0;
}
运行结果:
指针与整数之间的加减运算
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int ages[] = {20, 18, 19, 24, 23, 28, 30, 38,35, 32};
int len = sizeof(ages)/sizeof(ages[0]);
int *p = ages;
printf("The age of the seventh most beautiful woman: %d\n", *(p+6));
printf("*p+6 = : %d\n", *(p+2));
int *p1 = &ages[4];
printf("Relative to the age of the fifth beauty, the 1 before her: %d\n", *(p1-1));
printf("Relative to the age of the fifth beauty, the 3 before her: %d\n", *(p1-3));
return 0;
}
运行结果:
知识点:
(1)指针与整数的运算,指针加减数字表示的意义是指针在数组中位置的移动; 对于整数部分而言,它代表的是一个元素,对于不同的数据类型,其数组的元素占 用的字节是不一样的,
比如指针 + 1,并不是在指针地址的基础之上加 1 个地址,而是在这个指针地址的 基础上加 1 个元素占用的字节数:
如果指针的类型是 char*,那么这个时候 1 代表 1 个字节地址;
如果指针的类型是 int*,那么这个时候 1 代表 4 个字节地址;
如果指针的类型是 float*,那么这个时候 1 代表 4 个字节地址;
如果指针的类型是 double*,那么这个时候 1 代表 8 个字节地址。
(3)通用公式:
数据类型 *p;
p + n 实际指向的地址:p 基地址 + n * sizeof(数据类型)
p - n 实际指向的地址:p 基地址 - n * sizeof(数据类型)
比如
(1)对于 int 类型,比如 p 指向 0x0061FF14,则:
p+1 实际指向的是 0x0061FF18,与 p 指向的内存地址相差 4 个字节;
p+2 实际指向的是 0x0061FF1C,与 p 指向的内存地址相差 8 个字节
(2)对于 char 类型,比如 p 指向 0x0061FF28,则:
p+1 实际指向的是 0x0061FF29,与 p 指向的内存地址相差 1 个字节;
p+1 实际指向的是 0x0061FF2A,与 p 指向的内存地址相差 2 个字节;
李连杰的二级指针
二级指针也是一个普通的指针变量,只是它里面保存的值是另外一个一级指针的地址
定义:
int guizi1 = 888; int *guizi2 = &guizi1; //1 级指针,保存 guizi1 的地址 int **liujian = &guizi2; //2 级指针,保存 guizi2 的地址,guizi2 本身是一个一级指针变量
#include <stdio.h>
#include <stdlib.h>
int main(void){
int guizi2 = 888; //存枪的第 2 个柜子
int *guizi1 = &guizi2; //存第 2 个柜子地址的第一个柜子
int **liujian = &guizi1; //手握第一个柜子地址的刘建
printf("刘建打开第一个柜子,获得第二个柜子的地址:0x%p\n", *liujian);
printf("guizi2 的地址:0x%p\n", &guizi2);
int *tmp;
tmp = *liujian; printf("访问第二个柜子的地址,拿到枪:%d\n", *tmp);
printf("刘建一步到位拿到枪:%d\n", **liujian); //缩写成 **liujian
return 0;
}
二级指针的用途:
- 普通指针可以将变量通过参数“带入”函数内部,但没办法将内部变量“带出”函数
- 二级指针可以不但可以将变量通过参数函数内部,也可以将函数内部变量 “带出”到函 数外部。
#include <stdio.h>
#include <stdlib.h>
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
void boy_home(int **meipo) {
static int boy = 23;
*meipo = &boy;
}
int main() {
int *meipo = NULL;
boy_home(& meipo);
printf("boy: %d\n", *meipo);
return 0;
}
运行结果: