指针
地址: 存储单元的编号,每个存储单元都有唯一的地址
内存地址:内存中的存储单元的编号,
在地址所标识的存储单元中存放数据
指针:地址(内存单元的编号或地址)
内存单元的指针(地址)和内存单元的内容(数据)是两个不同的概念
指针的好处:
1、为函数提供修改调用变量的灵活手段
2、让函数有多个返回值
3、可以改善某些子程序的效率(在数据传输时,如果数据块较大(比如说数据缓冲区或较大的结构),这时使 用指针传递地址而不死实际数据,即可提高传输效率,又节省内存)
4、为动态数据结构(如二叉树、链表)提供支持
变量的存取方式
1、直接存取:变量名的赋值或取值
2、间接存取:通过指针或地址间接操作完成
指针变量
int a = 4; 获取a的地址:&a
指针变量:存放指针(地址)的变量
注意:
严格意义上,指针是一个地址,是一个常量
指针变量时存放一个地址,是一个变量
定义指针变量:类型说明符 *变量名
其中*表示一个指针变量,变量名即为指针变量名,类型说明符表示指针所指向的变量的数据类型
int *p; //定义了一个指针变量,变量名时p
//int表示,p只能存放一个int类型变量的地址
指针变量初始化及引用
1、定义的同时初始化
完全初始化:int *p = &a;//用a的地址初始化 p这个指针变量
//另一种描述:p指向了a
部分初始化:int *p = &a, *q;
q = &a;
2、先定义后初始化
不推荐,推荐1
3、不知道该指向谁,初始化为NULL
int *p = NULL; //NULL是空(0)
int *p = 0; //空
野指针:未进行初始化的指针,存放的是一个垃圾值。如果垃圾值是系统的地址,就有可能造成系统崩溃!
应避免操作野指针
*的两种用法
1、定义指针
2、使用 * 可以获取或改变指针指向的存储单元的值
int a = 4;
int *p = &a;
*指针变量:获取指针变量只想内存空间的内容
int value = *p; //value == 4 获取
*p = 100; //a == 100 改变
//
用函数实现两个值交换,指针实现
//
必须传的是指针或地址
int
a =
4
, b =
5
;
swap (&a, &b);
swap (&a, &b);
printf("a = %d, b = %d\n", a, b);
//交换值函数
void
swap(
int
*x,
int
*y) {
int i;
i = *x; //*x:获取X指向内存单元的值
*x = *y; //获取y指向内存单元的值,改变x指向内存单元的值
*y = i; //改变y指向内存单元的值
//只是
形参变量的地址进行了交换,并没有改变值,这种写法依旧不能改变
// int *temp;
// temp = x;
// x = y;
// int *temp;
// temp = x;
// x = y;
// y = temp;
}
指针常见的应用场景
1、在被调函数里,通过指针可以修改主调函数变量的值
2、让函数有多个返回值(利用指针可以修改值,返回多个值)
//
计算
+ - * /
int add = 0 , jian = 0 , cheng = 0 ;
float chu = 0.0f ;
caculator ( 12 , 4 , &add, &jian, &cheng, &chu);
printf ( "add = %d\n" , add);
printf ( "jian = %d\n" , jian);
printf ( "cheng = %d\n" , cheng);
int add = 0 , jian = 0 , cheng = 0 ;
float chu = 0.0f ;
caculator ( 12 , 4 , &add, &jian, &cheng, &chu);
printf ( "add = %d\n" , add);
printf ( "jian = %d\n" , jian);
printf ( "cheng = %d\n" , cheng);
printf("chu = %.2f\n", chu);
//
加减乘除函数
void caculator( int x, int y, int *add, int *jian, int *cheng, float *chu) {
// 虽是 void 类型,但是通过指针,间接地返回了 4 个值
*add = x + y;
*jian = x -y;
*cheng = x * y;
*chu = x / ( float )y;
void caculator( int x, int y, int *add, int *jian, int *cheng, float *chu) {
// 虽是 void 类型,但是通过指针,间接地返回了 4 个值
*add = x + y;
*jian = x -y;
*cheng = x * y;
*chu = x / ( float )y;
}
二级指针
二级指针:如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量
多级指针同理
//
二级指针
int a = 5 ;
// 定义一个一级指针
int *p = &a;
int a = 5 ;
// 定义一个一级指针
int *p = &a;
printf("&a = %p\n", &a);
&a = 0x7fff5fbff81c
printf
(
"p = %p\n"
, p);
p = 0x7fff5fbff81c
printf
(
"---------------\n"
);
// 定义一个二级指针
//** 的个数 :表示几级指针,当前二级指针
// 定义一个二级指针
//** 的个数 :表示几级指针,当前二级指针
int **p1 = &p;
printf("&p = %p\n", &p);
&p = 0x7fff5fbff810
printf
(
"p1 = %p\n"
, p1);
p1 = 0x7fff5fbff810
printf ( "---------------\n" );
printf("*p = %d\n", *p);
*p = 5
//p
的值,
a
的地址
printf("*p1 = %p\n", *p1);
*p1 = 0x7fff5fbff81c
//**p
的值,即为
a
的值
printf("**p1 = %d\n", **p1);
**p1 = 5
指针为什么要区分类型:
在同一个编译器下,一个指针变量占用的内存空间是固定的,本编译器64位,8个字节
//
不管什么类型的指针,占用字节固定
int
*p1;
char
*p2;
double *p3;
float *p4;
void *p5; // 空指针
printf ( "sizeof(p1) int ---->%ld\n" , sizeof (p1));
printf ( "sizeof(p2) char ---->%ld\n" , sizeof (p2));
printf ( "sizeof(p3) double ---->%ld\n" , sizeof (p3));
printf ( "sizeof(p4) float ---->%ld\n" , sizeof (p4));
double *p3;
float *p4;
void *p5; // 空指针
printf ( "sizeof(p1) int ---->%ld\n" , sizeof (p1));
printf ( "sizeof(p2) char ---->%ld\n" , sizeof (p2));
printf ( "sizeof(p3) double ---->%ld\n" , sizeof (p3));
printf ( "sizeof(p4) float ---->%ld\n" , sizeof (p4));
printf("sizeof(p5) void ---->%ld\n", sizeof(p5));
sizeof(p1) int ---->8
sizeof(p2) char ---->8
sizeof(p3) double ---->8
sizeof(p4) float ---->8
sizeof(p5) void ---->8
不区分类型,可能改变值或丢失值
定义什么类型的指针就去读什么类型的值
数组指针
数组指针:指向数组元素的指针,使用数组指针间接访问数组的元素
int a[10];
int *p;
p = &a[0]; 等价于 p = a; //仅仅是传给p,a[0]的地址,第一个元素的地址,p指向第一个元素
引用数组个元素方法
1、下标法
2、通过数组名计算数组地址
3、使用指针引用数组元素
在指针指向数组元素时,允许以下运算
p + 1:指向数组的下一个元素
p - 1:上一个元素
p++
p--
p +=
两个指针相减p1 - p2,只有p1和p2同时指向数组
地址加减:p + i 和 a + i等价
地址加减后,取值:*(p + i) 和 *(a + i)等价
*p++和*(p++)一样,都是先取值,再++
学习误区:*a++,是不对的,数组名a是一个常量,a++是 常量++是不对的
对于一维数组来说
1)获取a[i]的地址有几种方法
(1)&a[i]
(2)a+i
(3)int *p = a;
p+i
2)获取a[i]的值有几种方法
(1)a[i]
(2)*(a+i)
(3)int *p = a;
*(p+i)
(4)*(&a[i])
//
用指针将数组
a
中
n
个整数按相反顺序存放,逆序数组
// 解题思路 : 将 a[0] 与 a[n-1] 对换 ......
// 注意: a[i] = *(a+i)
int arr[ 10 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };
nixuArray (arr, 10 );
for ( int i = 0 ; i < 10 ; i++) {
printf ( "%d " , *(arr + i));
// 解题思路 : 将 a[0] 与 a[n-1] 对换 ......
// 注意: a[i] = *(a+i)
int arr[ 10 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };
nixuArray (arr, 10 );
for ( int i = 0 ; i < 10 ; i++) {
printf ( "%d " , *(arr + i));
}
//
逆序一个数组函数
void
nixuArray(
int
arr[],
int
len) {
// 定义一个数组指针
int *p = arr;
// 定义下标
int i= 0 , j = len - 1 ;
// int temp;
while (i < j) {
int temp;
// 交换 a[i] 和 a[j]
temp = *(p + i);
*(p + i) = *(p + j);
*(p + j) = temp;
// 修改下标
i++;
j--;
}
// 定义一个数组指针
int *p = arr;
// 定义下标
int i= 0 , j = len - 1 ;
// int temp;
while (i < j) {
int temp;
// 交换 a[i] 和 a[j]
temp = *(p + i);
*(p + i) = *(p + j);
*(p + j) = temp;
// 修改下标
i++;
j--;
}
}
一维指针数组
指针数组:一组有序的指针集合。指针数组的所有元素都必须具有相同存储类型和指向相同数据类型的指针变量。
形式:类型说明符 *数组名[数组长度]
int *a[3]
表示a是一个指针数组,有三个数组元素,每个元素都是一个指针,指向整型变量
int
a =
3
, b =
4
, c =
5
;
int *pia[ 3 ] = {&a, &b, &c};
// 打印指针数组第一个元素的值
printf ( " &a = %p\n" , &a);
printf ( "pia[0] = %p\n" , pia[ 0 ]);
// 打印指针数组的首地址
printf ( " pia = %p\n" , pia);
printf ( "&pia[0] = %p\n" , &pia[ 0 ]);
// 访问 a 的值, a = 3
printf ( " *(&a) = %d\n" , *(&a));
printf ( "*(pia[0]) = %d\n" , *(pia[ 0 ]));
printf ( "**pia = %d\n" , **pia);
int *pia[ 3 ] = {&a, &b, &c};
// 打印指针数组第一个元素的值
printf ( " &a = %p\n" , &a);
printf ( "pia[0] = %p\n" , pia[ 0 ]);
// 打印指针数组的首地址
printf ( " pia = %p\n" , pia);
printf ( "&pia[0] = %p\n" , &pia[ 0 ]);
// 访问 a 的值, a = 3
printf ( " *(&a) = %d\n" , *(&a));
printf ( "*(pia[0]) = %d\n" , *(pia[ 0 ]));
printf ( "**pia = %d\n" , **pia);
printf("*(&pia[0]) = %d\n", **(&pia[0]));
&a = 0x7fff5fbff7bc
pia[0] = 0x7fff5fbff7bc
pia = 0x7fff5fbff7d0
&pia[0] = 0x7fff5fbff7d0
*(&a) = 3
*(pia[0]) = 3
**pia = 3
pia[0] = 0x7fff5fbff7bc
pia = 0x7fff5fbff7d0
&pia[0] = 0x7fff5fbff7d0
*(&a) = 3
*(pia[0]) = 3
**pia = 3
*(&pia[0]) = 3
两个指针变量之间的运算
1)两个指针之间的减法运算(只有减法)
(1)直接相减意义不大
(2)常见的用法:两个指针都指向同一个数组
1、判断两个指针变量指向的元素是否连续(相减绝对值1)
2、判断两个指针变量之间相隔几个元素
2)两个指针变量之间的关系运算
(1)返回值1或0,判断高位低位地址
1、数组名访问二维数组
a代表第0行的首地址a[0],a+1代表第一行的首地址a[1],a+i代表第i行的首地址a[i]
*(a + 1) == &a[1][0]
a[0]也代表第0列的首地址
a[i] + j 获取&a [i][j]
*(a[i] + j) 获取a[i][j]
a[i] 获取*(a + i)
*(*(a + i) + j) 获取a[i][j]
二维数组指针:指向二维数组的指针(行指针),可以替代数组名去使用(数组名是常量不能改变没用指针更灵活)
形式:数据类型 (*p)[二维数组列数(数组第二维的长度)]
int a[3][4];
int (*p)[4];
*(*(p + i)+ j) 就是获取a[i][j]
区别指针数组和二维数组指针
int *p[3];
int (*p)[4];
保存字符串:1、字符数组
char String[] =
“hello world”
;
2、字符串指针:字符串指针指向字符串
形式: char *变量名 = “字符串内容”; //
“字符串内容”是常量,但是该指针存的是字符串的首地址
指向字符变量的指针变量:赋予该字符的地址(char ch = ‘b’; char *p = &ch;)
使用字符指针来保存字符串,它保存的是字符串常量地址,常量区是只读的,所以不可以修改字符串的字符
但是可以修改指向的字符串
动态申请内存malloc(),头文件
”
stdlib.h"
二维字符数组:可以存储多个字符串char ch[3][10]
第一维存的是每个字符串的首地址(字符串的个数)3
第二维度应该大于或等于每个字符串的长度 10 (此处长度包含\0)
char
map[
kRows
][
kCols
] = {
"**********" ,
"*0 **** *" ,
"* X**** *" ,
"* *" ,
"****** *" ,
"* *** *" ,
"* *" ,
"* ******" ,
"* " ,
"**********"
"**********" ,
"*0 **** *" ,
"* X**** *" ,
"* *" ,
"****** *" ,
"* *** *" ,
"* *" ,
"* ******" ,
"* " ,
"**********"
};
字符指针数组:在存上面的图形是,更有优势,只是存了字符串的首地址,长度不受限制
字符串指针和字符数组区别:
1、字符串指针常量区,只可以改变指向,修改整体值
2、字符数组在栈区,数组名是个地址,是一个常量,不可以被赋值,但是字符数组可以修改单个值
//
输入
5
个国家并按字符的顺序排列后输出
//char *name[] = {"CHINA", "AMERICA".....};
// 排序结果
//AMERICA
//CHINA
char *name[] = {
"CHAIN ” , 0
//char *name[] = {"CHINA", "AMERICA".....};
// 排序结果
//AMERICA
//CHINA
char *name[] = {
"CHAIN ” , 0
"AMERCIA
”
, 1
"AUSTRALIA
”
, 2
"FRANCE” 3
};
sortString (name, 4 );
for ( int i = 0 ; i < 4 ; i++) {
printf ( "%s\n" , name[i]);
sortString (name, 4 );
for ( int i = 0 ; i < 4 ; i++) {
printf ( "%s\n" , name[i]);
}
//头文件
#include
<string.h>
// 定义函数排序 , 实现字符串排序
void sortString( char *arr[], int len) {
// 冒泡排序
char *temp; // 临时存放地址
for ( int i = 0 ; i < len - 1 ; i++) {
// 定义函数排序 , 实现字符串排序
void sortString( char *arr[], int len) {
// 冒泡排序
char *temp; // 临时存放地址
for ( int i = 0 ; i < len - 1 ; i++) {
for (int j = 0; j < len - 1 -i; j++) {
//arr[j] arr[j]
//
比较字符串
if ( strcmp (arr[j], arr[j+ 1 ]) > 0 ) {
temp = arr[j];
arr[j] = arr[j+ 1 ];
arr[j+ 1 ] = temp;
}
}
}
if ( strcmp (arr[j], arr[j+ 1 ]) > 0 ) {
temp = arr[j];
arr[j] = arr[j+ 1 ];
arr[j+ 1 ] = temp;
}
}
}
}