指针
指针的特点
1. 使程序更简洁、紧凑、高效 这个有点不算是指针比较鲜明或者独特的点2. 有效的表达更复杂的数据结构
3. 动态分配内存 合理利用空间
4. 得到多于一个数的函数返回值 容易实现函数的编写和调用。
一级指针
地址:内存中每一个单元的编号(门牌号)
指针:就是地址,内存地址
指针变量
概念:存储指针的变量
格式:存储类型 数据类型*指针变量名
例:int *p;//定义了一个指针变量p
int a=10;
int*p=&a;//p中存储的就是a的地址
访问值:*指针变量名 *p
修改值:*指针变量名=新值
指针和变量之间的关系:
int a=5;
int*p=&a; //a===*p &a===p a=10===*p=10
指针操作符:
*:取内容
&:取地址
单目运算:从右往左
int a =10;
*&a; //10*&两者互逆
&*a; //错误
初始化:
1.将普通变量赋值给指针
int a=20;
int *p=&a;//定义时同时赋值
int *p;//野指针 乱指向
int *p=NULL;
int b=10;
p=&b; //先定义后赋值
2.将数组的地址赋值给指针
char s[]="hello";
char *p=s;
3.将指针变量的值交给另一个指针变量
int a=10;
int *p=&a;
int*q=p:
指针运算
算术运算
+ - ++ --
char s[]="hello";
char *p=s;
p+1:向高地址方向移动一个数据单位
p+n:向高地址方向移动n个数据单位,但是指针本身指向不会发生改变
p=&s[4];
p-1:向低地址方向移动一个数据单位
p-n:向低地址方向移动n个数据单位,但是指针本身指向不会发生改变
数据单位:指针的类型大小
char s[]="hello";
char *p=s;
p++ ----->p=p+1
p++:向高地址方向移动一个数据单位,指针本身指向会发生改变
p--:向低地址方向移动一个数据单位,指针本身指向会发生改变
注意:
1.指针的数据类型要和将要存储的变量的地址相同
例:int a; int *p=&a;
short a; short*p*&a;
2.指针在+ - ++ --运算时移动的数据单位为指针的类型的大小
即:指针的偏移地址=n*sizeof(指针的数据类型)
3.两个地址之间的差=两个地址之间间隔的元素的个数
关系运算
> < == !=
指针之间做关系运算时,指向高地址的指针大于指向低地址的指针
注意:
地址之间的比较 同数组之间的比较 指向不同区域的指针之间的比较无意义
#include <stdio.h> //引入头文件 std:标准 i:输入 o:输出 .h:库文件 #include <strings.h> #include <string.h> int main(int argc, char const *argv[]) { int x[5] = {10, 20 , 30}; // 12 20 30 int *px = x; printf("%d,", ++*px); // 11 printf("%d\n", *px); // 11 px = x; printf("%d,", (*px)++); // 11 printf("%d\n", *px); // 12 px = x + 1; printf("%d,", *px++); //20 printf("%d\n", *px); // 30 px = x+1; printf("%d,", *++px); //30 printf("%d\n", *px); //30 return 0; }
总结: 在输出或者赋值语句中: *p++:先取内容,再移动指针的方向(向高地址方向移动一个数据单位) (*p)++:先取内容*p,再对内容+1 ++*p:先取内容*p,再对内容+1 ++(*p):先取内容*p,再对内容+1 *++p:先取内容,再移动指针的方向(向高地址方向移动一个数据单位)
指针的大小
取决于操作系统的大小 64位 ----8字节
总结:
- 32位操作系统,指针大小4字节,64位操作系统,指针大小8字节
- 内存地址是固定的,但是变量的地址不固定的,(栈区变量(局部变量)随机分配)
- 指针类型根据指针指向空间的数据类型而决定的
指针修饰
const常量化 只读
使用const修饰变量
const int a=10;\\变量a只读
a=100; \\错误
int const a=10;
const修饰指针
const int *p;//修饰*p
int * const p;//修饰p
const int *const*p;//同时修饰p *p
void 空
void不能修饰变量
void修饰指针
void *p;//修饰任意类型的指针
int a=19;
void *p=&a;
如果对任意类型的指针赋值后进行取值 需要强转
int a=19;
void *p=&a;
printf("%d\n",*((int*)p));//强制转换
int a=19;
void*p=&a;
int *p1=(int*)p:
printf("%d\n",p1)://强制转换
段错误
野指针
- 指针变量没有赋值
- 指针置空后,对空间赋值
- 堆区空间free后,指针没有不及时置空
内存泄漏
内存空间溢出或程序结束未释放
对非法空间进行操作
大小端
大端:在低地址存放高字节数据,在高地址存放低字节数据
小端:在低地址存放低字节数据,在高地址存放高字节数据
int ---4字节
int a=0x12345678 起始地址:0x100
0x100 | 0x101 | 0x102 | 0x103 | |
大端 | 0x12 | 0x34 | 0x56 | 0x78 |
小端 | 0x78 | 0x56 | 0x34 | 0x12 |
内存:
验证电脑大小端
小端存储
二级指针
一级指针:存放变量的地址 二级指针:存放一级指针的地址
格式:存储类型 数据类型**指针变量名
int **p;//二级指针
int a=10;
int *p1=&a;
int **p2=&p1;
地址:
取a的内容:a *p1 **p2
取a的地址:&a p1 *p2
访问p1的地址:&p1 *p2
指针和数组
访问
直接访问:按变量的地址存取变量的值(通过数据名访问)
间接访问:通过存放变量地址的变量去访问变量(通过指针访问)
指针和一维数组
直接访问:
间接访问:
访问数组元素a[i]的地址:
直接访问:a+i ,&a[i]
间接访问:p+i ,&p[i]
访问数组元素a[i]的值:
直接访问:*(a+i) ,a[i]
间接访问:*(p+i) ,p[i]
注意:
a和p本质不同, a:数组名 地址常量 p:指针变量
指针和二维数组
直接访问:
二维数组的数组名 a: 第一行的首地址
如何表示第一行的其他元素?降级 降级到第一行第一列的地址 使用*
*a 第一行第一列的地址
*a+1 第一行第二列的地址
a
a[0]:第一行第一列的地址
a[0]+1:第一行第二列的地址
a[1]:第二行第一列的地址
a[1]+1:第二行第二列的地址
访问数组元素的地址(a[i][j]):
&a[i][j] *(a+i)+j a[i]+j
访问数组元素(a[i][j]):
a[i][j] *(*(a+i)+j) *(a[i]+j)
数组指针
定义:本质是指针 指向数组(行指针)
格式:存储类型 数据类型(*数组指针名)[列数]
例:int a[2][2]={1,2,3,4};
int (*p)[2]=a;//定义一个数组指针
p:第一行的首地址 p+1:第二行的首地址
p+1移动8个字节(列数2*数据类型的大小)
间接访问
访问数组元素的地址(a[i][j]):
&p[i][j] *(p+i)+j p[i]+j
访问数组元素(a[i][j]):
p[i][j] *(*(p+i)+j) *(p[i]+j)
数组指针的大小p:八个字节
sizeof(数组指针名)
和操作系统有关:64--->8 32--->4
指针数组
定义:本质是数组,里面存放的是指针(地址)
格式:存储类型 数据类型 *指针数组名[元素的个数]
例:int *arrp[3];
1.用于存储普通变量的地址
int a=10,b=20,c=30;
int *arrp[3]={&a,&b,&c};
访问a的地址:arrp[0]
a的内容:*arrp[0]
访问b的地址:*(arrp+1)
b的内容:**(arrp+1)
2.存放二维数组每一行每一列的地址
int a[3][3]={1,2,3,4,5,6,7,8,9};
int *p[3]={a[0],a[1],a[2]};
访问:
第二行第一列的地址:p[1] *(p+1)
第三行第二列的元素:*(p[2]+1 ) *(*(p+2)+1)
3.存放多个字符串
char *p[3]={"hello","asd","ak"}:
打印:”hello“字符串
printf("%s\n",p[0]);
printf("%s\n",*p);
打印:k字符
printf("%c\n",p[2][1]);
printf("%c\n",*(*(p+2)+1));
printf("%c\n",*(p[2]+1)); // []:先移动 后降级 p[2]===*(p+2) *(p+2)[1]===*(*(p+2)+1)
命令行参数
int main(int argc, char const *argv[])
{
return 0;
}
argc:字符串的个数 传递的数据的个数
argv:指针数组 存放字符串