pointer_to_the_advanced(指针进阶)——重置版
Posted Dark And Grey
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pointer_to_the_advanced(指针进阶)——重置版相关的知识,希望对你有一定的参考价值。
文章目录
回顾指针
1,指针就是个变量,用来存放地址,地址 唯一标识 一块 内存空间。
2.指针的大小是固定的 4 / 8字节(32位平台 / 64位平台)。
3.指针是有类型的 ,指针的类型决定了指针的 +- 整数的步长,指针解引用操作的时候的权限(一次访问几个字节内容)
4.指针的运算
举个例子回忆一下
#include<stdio.h>
void test(int* arr)// 这里的 arr 是 数组首元素地址的一份拷贝
{
printf("%d\\n",sizeof(arr[0]));// 计算的数组的一个元素大小,输出为 4
printf("%d\\n",sizeof(arr));// 计算的是地址的大小(这里不能算是数组的地址,因为 该地址是test传过来的 首元素地址 的一份拷贝),输出为 4
// 32位系统
sizeof(arr)在这里是求指针(地址)大小(4 byte)
sizeof(arr[0])在这里求的是一个元素的大小,int类型(4字节)
如果是64位系统。指针大小为 8 字节,输出此时就为 2
}
int main()
{
int arr[10] = { 0 };
test(arr);//传过去的是数组首元素地址,因为不是单独 与 sizeof 和 & 操作符连用
return 0;
}
1.字符指针
在指针的类型 中 我们知道有一种 指针类型 为 字符指针 char*·
程序一:
#include<stdio.h>
int main()
{
char ch = 'w';
char* pc = &ch;
*pc =='w';
return 0;
}
程序二:
#include<stdio.h>
int main()
{
char arr[] = "abcdef";
char* pc = arr;//这里存入的是首元素 a 的地址
printf("%s\\n",arr);//abcdef
printf("%s\\n",pc);//abcdef 两者都是向后打印,直到遇到'\\0'停止
%s 就是 根据 给的地址位置开始向后打印的,知道遇到'\\0'停止
return 0;
}
程序三:
#include<stdio.h>
int main()
{
char* p = "abcdef";// "abcdef" 双引号引起来的abcdef\\0,是一个常量字符串
// 上表达式的意思是,把 a 的地址 存入指针变量p里面去
printf("%c\\n",*p);// *p == a
// 输出为 a
printf("%s\\n",p);//从存入p的这个地址(首元素a的地址)开始往后打印知道遇到 '\\0'
// 即输出为 abcdef
return 0;
}
程序四:
#include<stdio.h>
int main()
{
const char* p = "abcdef";// 最稳妥写法,就是在 * 前面加上 const
*p = 'w';// 这时候你想改都改不了,况且 "abcdef" 是一个常量字符串,也改不了
printf("%s\\n", p); //你会发现 没有任何输出,程序崩溃。违规操作
//还有一个原因 abcdef\\0 是一个常量字符串,是不可以被改变的(const:是变量具有常量属性)
return 0;
}
程序五:
#include<stdio.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (arr1 == arr2)//这里是两个不同数组的数组名(不同的首元素地址),是否相同
//肯定不同,两个不同数组的起始空间肯定不一样
{
printf("hehe\\n");
}
else
{
printf("haha\\n");// 所以输出这条语句
}
return 0;
}
程序六:
#include<stdio.h>
int main()
{
建议下面两句表达式在 * 前面防疫 const ,这样就更保险,无法通过解引用去修改常量字符串的内容
这样写,虽然意义不大(常量字符串本身并不能被修改),语法更为准确
const char* p1 = "abcdef";
const char* p2 = "abcdef";
这里把常量字符串 abcdef\\0 的首元素(a)地址分别存入 2 个指针变量
是因为abcdef\\0是常量字符串,不可改变,因此没有必要创建2个,直接共用一个,即 p1 == p2
if (p1 == p2)
{
printf("hehe\\n");// 所以输出这条语句
}
else
{
printf("haha\\n");
}
return 0;
}
指针数组 - 本质上是一个数组
在pointer文章中,我们也学了指针数组,指针数组 指的是一个 存放指针 的数组
程序一:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };// 整形数组
char ch[5] = { 0 };// 字符数组
int* parr[4];// 这就是一个存放 整形指针 的数组,简称 指针数组
char* pch[5];// 这就是一个存放 字符指针 的数组,简称 指针数组
return 0;
}
程序二:
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int* arr[4] = { &a, &b, &c, &d };
等价于 //int* pa = &a;
//int* pb = &b;
//int* pc = &c;
//int* pd = &d;
int i = 0;
for (i = 0; i < 4; i++)
{
printf("%d ",*arr[i]);// 10 20 30 40
}
return 0; // 指针数组很少怎么用 ,下面程序将告诉你,指针数组怎么使用
}
程序三:
#include<stdio.h>
int main()
{
int arr1[] = { 1, 2, 3, 4, 5 };
int arr2[] = { 2, 3, 4, 5, 6 };
int arr3[] = { 3, 4, 5, 6, 7 };//以上三个表达式,就是在说我有三数组,内容是。。。。
int* parr[] = { arr1, arr2, arr3 };
// 把上面 三个数组 的 数组名/数组首元素地址 存入这个 指针数组
int i = 0;
for (i = 0; i < 3; i++)// 遍历 指针数组 parr 的元素
{
int j = 0;
for (j = 0; j < 5; j++)// 遍历 指针数组 parr 的 元素 所指向的 数组 的 元素
{
printf("%d ",*(parr[i] + j));
}
printf("\\n");
}
return 0;
}
这里我们回顾一下 二级指针
int* arr1[]; //一级整形指针数组.
int** arr[];// 二级整形指针数组
ichar* arr2[]; //一级字符指针数组
char** arr2[]; //二级字符指针数组
数组指针 - 指针
程序一:
#include<stdio.h>
int main()
{
int* p = NULL;// 整形指针 - 指向 整形 的指针 作用:可以 存放 整形的地址
char* pc = NULL;//字符指针 - 指向 字符 的指针 作用:可以 存放 字符的地址
数组指针 - 指向 数组 的指针 作用:可以 存放 数组的地址
//int arr[10] = { 0 }; 整形数组
// arr - 首元素地址
// &arr[0] - 首元素的地址
// &arr - 数组的地址
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int(* p)[10] = &arr;// 把数组的地址存起来
// 为什么不能去掉(),因为 [] 的优先级 比 * 高,去掉(),p就和数组[10]先结合,就成 指针数组(存放指针的数组) 了
// 加 () 把 * 和 p 先结合,使 p 成为一个指针 (另外请注意 这里不是解引用,只是说明 p 是一个指针)
// 把 *p 去掉,还剩int [10],意思就是说该指针(p)指向 一个 元素个数为 10 的数组,且数组每个元素的类型为整形
// 即 上表达式 int(*p)[10] 就是数组指针
return 0;
}
程序二:
#include<stdio.h>
int main()
{
char* arr[5];
//如何把上表达式的数组存入 数组指针?
如下
char*(*pa)[5]=&arr;
// 先用()把 * 和 pa(指针变量名) 结合起来,使 pa 为一个指针,也就是说 * 告诉我们 pa 是个指针
//再在后面加[5],意思是 指针 指向一个元素个数为5的数组
// 又因为 指针 指向的数组 的 元素类型 为 char*,所以在前面补上
int arr2[10] = { 0 };
int (*pa2)[10] = &arr2;
return 0;
}
&数组名 VS 数组名
数组名 绝大部分时候 都是为 首元素地址
只有两种情况例外 &数组名 和 sizeof(数组名)
在这两种情况下的数组名,代表是整个数组,取出的是数组的地址(与数组首元素地址相同,但意义不同)
举个例子
&arr+1 - 直接 跳过一整个 数组的字节
比如 int arr[10],他的大小是40个字节,&arr+1 数组地址会加上40
如果是 arr+1 它就只跳过一个元素,意思就是 跳过第一个元素,地址指向第二个元素
数组指针的用法 : 一般用在 二维数组
程序一:
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
int (*pa)[10] = &arr;
int i = 0;
for (i = 0; i < 10; i++)
{
//printf("%d ",(*pa)[i]);// *pa 就相当于数组名 *pa == arr
printf("%d ",* (*pa+i) );//等价于 printf("%d ",*(arr+i))
}
return 0; // 但 数组指针 不是这么用的,以上只是让你对它理解更深一点
}
在 明白 数组指针 的真正用途之前,我们需要观察一个程序
#include<stdio.h>
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int i = 0;
int* p = arr;// 指针变量p 接收的是 数组的首元素地址,即 p == arr
for (i = 0; i < 10;i++)
{
printf("%d ",*(p+i));// 通过数组首元素的地址,来遍历数组元素
}
printf("\\n");
上下两个循环表达大额效果是相同的
for (i = 0; i < 10; i++)
{
printf("%d ", *(arr + i));
}
printf("\\n");
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);// arr[i] == *(arr+i) == *(p+i) == p[i]
}
printf("\\n");
上下两个循环表达大额效果是相同的
for (i = 0; i < 10; i++)
{
printf("%d ", p[i]);// arr[i] == p[i]
}
//以上所有写法都是等价的。
return 0;
}
程序二(数组指针的用法):
#include<stdio.h>
void print1(int arr[3][5], int x, int y)
{
int i = 0;
int j = 0;
for (i = 0; i < x; i++)// 双重循环遍历二维数组
{
for (j = 0; j < y; j++)
{
printf("%d ",arr[i][j]);// printf("%d ". *(*(arr+i)+j) );
}
printf("\\n");
}
}
void print2(int(*p)[5], int x, int y)//由于 二维数组转过的是 首元素地址(一维数组的地址),需要一个一维数组指针来接收
{
int i = 0;
int j = 0;
for (i = 0; i < x; i++)// t通过双重循环,来遍历二维数组
{
for (j = 0; j < y; j++)
{
//printf("%d ", *(*(p+i)+j));// p 是 一维数组的地址 (一行),*p 就是找到了第一行的数据,也就是一维数组的数组名
// *( p + i),i=0,还是第一行,i=1 是第二行, i=2 第三行
// *(p+i)+j 就是第几行 第几个元素
printf("%d ",p[i][j]);
// *(*(p+i)+j)== p[i][j] 先用一个括号将 p 和 i括起来,得到一维数组的首元素地址,在对其解引用得到"数组名",
再用一个括号将其和 j 括起来,得到 i 行 第 j 个元素的地址,在对其 解引用,得到该元素的值,并将其打印(注意! [] 比 * 的优先级高)
//printf("%d ", *(p[i]+j))
// *(p+i) == arr[i] == p[i]
// *(p[i]+j) == p[i][j]
}
printf("\\n");
}
}
int main()
{
int arr[3][5] = { { 1, 2, 3, 4, 5 }, { 2, 3, 4, 5, 6 }, {3, 4, 5, 6, 7} };
print1(arr,3,5);
这里的 数组名 就是 首元素【{1,2, 3,4, 5}】地址
首先我们要 把二维数组 看作 一维数组,把 第一行{1,2,3,4,5}数据,也就是我们的第一个元素(首元素),看作一个一维数组 int a[5]
//以此类推 第二行数据 就是 第二个元素(也是一个一维数组), 第三行数据 就是 第三个元素(也是一个一维数组)
print2(arr,3,5);// 那么 传过去的是数组首元素地址,而且是一个一维数组的地址
return 0;
}
小汇总
int arr[10] //arr是一个整形数组,具有10个元素.
int* parr1[10];//parr1是指针数组,首先它是一个数组,具有10个元素,且每个元素的类型是(int*) ,
int(*parr2)[10]; parr2是一个整形数组指针
用括号让 * 与 parr2 先结合
所以parr2是一个指针,该指针指向了一个数组,数组有10个元素,每个元素的类型是int
int( * parr3 [10] ) [5]
因为()的优先级最高,所以先判断()里的内容
又因为 * 和[],[]的优先级更高,所以 parr3先与[]结合,所以parr3是个数组
把 parr3[10]去掉,还剩下int(* )[5] ,就是它的元素类型.
例子: int arr[10];去掉arr[10],乘下的 int,就是它的类型(整形)
那int(* )[5]是个什么类型?
仔细观察一下,你会发现,它和数组指针 int(*p)[10] 一样
.那么、我们可以说 parr3是个数组,元素有10个,每个元素都是一个整形数组指针,这个指针能指向5个元素,每个元素的类型是int.的整形数组。(我们称 int( * )[5] 为 整形数组指针类型,简称 数组指针类型 )
数组参数、指针参数
在写代码的时候难免要把 [ 数组 ] 或者 [ 指针 ]传给函数,那函数的参数该如何设计?
一维数组传参
这些是指哪些键位