一、了解指针
指针是C语言的灵魂 。指针的本质就是一个直接访问内存的工具,在众多语言中只有C语言有指针。
1、指针共有两个含义:
①、指针变量 ,也可通俗是了解为一个变量存放了一个内存的地址 ;
②、一个内存地址指针常量 。
2、指针变量的定义:
存储类型 指针指向的空间的数据类型 * 变量名;
eg: int *p = &a; //定义一个名为p的指针并把变量a的地址赋值给p
3、指针变量的赋值: 类型匹配才可以赋值
物理意义: 与指定的内存 建立联系
取(&)地址运算: 得到变量的内存地址
野指针: 一个指针 指向的内存 不可知 或 不确定 或 非法(不可访问)
空指针: 一个指针 指向内存 0地址 即这个指针中的值是0 int *p = NULL;
宏 NULL 空指针 (void *) 0
void * 类型 : 表示 万能指针, 一个可以指向 任意类型的指针
指针的访问: * 指针取值运算符
*指针变量 即等同于 指针指向的目标(对象)
int b;
int *p = &b;
*&b; 等同于 b
* 是 & 的逆运算
&*p; 等同于 &b
& 是 * 的逆运算
#include <stdio.h>
int a =5;
int main()
{
int b;
int *p = &a; //定义一个指针变量并初始化
int **pp = &p;
int *x; // 野指针
x = NULL;// 空指针
//指针的表示
printf("&a=%p\n",&a);
printf("p= %p\n",p); // 打印变量p中存放的地址
p = &b; //给p赋值
printf("p= %p\n",p); // 打印变量p中存放的地址
printf("&p=%p\n",&p); // 打印变量p的地址
printf("pp=%p\n",pp); // 打印变量pp中存放的地址
//指针的访问
*p = 20; //*p 等同于b *p = 20; 等同于 b = 20; 给*p赋值
printf("b=%d\n",b);
printf("*p=%d\n",*p);
printf("**pp=%d\n", **pp); // **pp 等同于 *p 等同于b
printf("*pp=%p\n", *pp); // *pp 等同于p
printf("*&b=%d\n", *&b );
printf("&*p=%p\n", &*p );
//空指针
//int *q = NULL;
//printf("%d",*q); 段错误
//void *
void *q = p;
printf("q=%p\n",q);
//printf("*q=%d\n",*q); //语法错误,无法编译
printf("*(int*)q=%d\n",*(int*)q); //
return 0;
}
二、指针的算数运算
1、指针 + 或 - 一个整数 ,其结果仍然是一个指针
其运算本质就是 地址的移动 + 向大地址方向移动 - 小地址方向移动
其移动的字节大小 由其指向的类型长度决定 +1 / -1 移动一个指向的类型
指针的 ++ -- 运算
#include <stdio.h>
//指针的算数运算
int main()
{
int a= 10;
int *p = &a;
printf("p =%p\n",p);
printf("p+1=%p\n",p+1);
printf("p+2=%p\n",p+2);
printf("p-1=%p\n",p-1);
char b;
char *q = &b;
printf("q =%p\n",q);
printf("q+1=%p\n",q+1);
printf("q+2=%p\n",q+2);
printf("q-1=%p\n",q-1);
//printf("*(q-1)=%d\n",*(q-1));
//指针与数组
int arr[5] = {1,2,3,4,5};
//p = &arr[0]; //p指向arr的0号元素
p = arr; // arr数组首地址 int * &arr[0]
//遍历数组
for(int i = 0; i< 5; i++)
printf("%d ",p[i]);
printf("\n");
for(int i = 0; i< 5; i++)
printf("%d ",*(arr+i));
printf("\n");
//p = arr;
// arr = p; 不能进行该操作 arr数组名是一个常量指针
return 0;
}
三、 指针 与 指针的 运算
1. 大地址 - 小地址 结果是一个整数其大小为 这两个地址之间相隔的 指向的类型个数
2. 关系运算 本质是 判断这个两个地址在内存中的位置
p > q p地址 在 q地址的 大方向
p < q p地址 在 q地址的 小方向
p == q p和q指针指向同一个内存
p != q p和q指针指向不同的内存
p >= q p地址 在 q地址的 大方向 或 相同
p <= q p地址 在 q地址的 小方向 或 相同
四、一级指针和一维数组
数组名可以当指针用
指针变量 可以做数组用
区别在于 指针变量 可以重新赋值 而数组名不行
int arr[5] = {1,2,3,4,5};
int *p = arr; // p = &arr[0] 等同
p[0] == arr[0] == *arr == *p //等同
p[1] == arr[1] == *(arr+1) == *(p+1)
练习: 用指针方式遍历数组 写函数实现
void show_arr(int *arr, int len) //要求使用arr和len完成遍历 不再定义其他变量
{
while(len--) printf("%d ",*(arr++));
}
//*arr++ == *(arr++) == 先*arr用 然后 arr=arr+1;
int main()
{
int arr[] = {1,2,3,4,5};
show_arr(arr,5);
return 0;
}
有如下定义:
int a[10]={0,1,2,3,4,5};
int *p = a;
假设每个表达式中p=a;求表达式代表的值?
*p=? a[0] == 0
*a=? a[0] == 0
p[0]=? a[0] == 0
a[0]=?
*p++=? == *(p++) == 先*p用 作为表达式的值, 然后 p=p+1
*(p++)=?
*p+1=? a[0] + 1 == 1
*(p+1)=? a[1] == 1
*a+1=? a[0] + 1 == 1
*(a+1)=? a[1] == 1
练习: 数组倒叙
void arr2rra(int *start, int *end) //倒叙 从start到end之间的 数组元素, 不能定义其他变量
{
}
int main()
{
int arr[] = {1,2,3,4,5};
show_arr(arr,5);
arr2rra(arr, arr+4);
show_arr(arr,5);
return 0;
}
五、指针与字符串
char s[] = "hello"; //s是一个数组 将"hello"复制一份到s数组中
cosnt char *p = "hello"; // p是一个指针变量 指向常量区字符串
练习:求字符串的长度
int len(const char *s)
{
char *p = s;
while(*p) p++;
return p-s;
}
注:const 修饰指针
cosnt char *p; *p 只读 p 可读可写
char * const p; *p 可读可写 p 只读
const char * const p ; *p 只读 p 只读