当前位置:网站首页>C语言-7月19日-指针的学习
C语言-7月19日-指针的学习
2022-08-11 05:30:00 【曾铎000811】
目录
* (p + 1) <=> ar[2] 也就是 p + 1 <=> &ar[2]
今天对指针进行了系统的学习,回顾一下今天所讲的知识点:
什么是指针?
首先需要明确的是:指针,就是地址的别名,用来存放一个值的地址。
指针的大小:
指针的大小与指针的级别无关,也与指针的类型无关。
例如我在这里分别设置int类型指针和double类型指针,我们分别来看他们的大小:
如图,int类型的指针和double类型的指针的大小都是8字节,所以指针的大小与指针的类型并没有关系。
例如我在这里设置一级指针p和二级指针q,我们分别来看他们的大小:
如图,一级指针和二级指针的大小都是8字节,所以指针的大小与指针的级数无关。
指针的大小只和操作系统(OS)的种类有关,在x86架构(32bit)下,指针的大小就是4字节,在x64架构(64bit)下,指针的大小就是8字节。例如我的苹果电脑是64位操作系统,指针的大小就是8字节:
*的含义 :
*的在C语言编程中有三种含义:
第一种:乘法的运算符号,表示方法为:数据*数据
第二种:解引用:*变量
第三种:指针的定义:类型 *名称
解引用:
在这里我查阅了相关资料,更加深入了解了解引用,“*” 属于指针的操作符,它的作用就是引用指针指向的变量值,前面说到过,指针的别名就是地址,也就是指针存放变量的地址,然后指针指向变量,所以*的作用就是引用指针所指向的变量值,引用其实就是引用该变量的地址,然后再利用指针操作符“*”将该地址对应的东西解开,所以就是解引用。
例如我这里写的代码:
整形变量a的值是10毋庸置疑,这里设置两个指针分别是p和q,其中指针p存放了变量a的地址,指向a,二级指针q存放了一级指针p的地址,而p因为存放了a的地址又指向a,所以二级指针q指向p最终指向a,所以解引用*p就是a本身,解引用**q也是a本身。
如图分别将a,*p,**p以十进制输出,得到的结果均为10,上面论述有据可依。
指针对数组的操作:
我在这里给出一个元素个数为5的一维数组,通过指针随意调换数组中的两个数值。
例如我需要更换数组下标为0和数组下标为5的数,对应数组里面的第一个数和第五个数:
#include<stdio.h>
#include<assert.h>
void Swap(int *ar,int index1,int index2)
{
assert(ar != NULL && index1 >= 0 && index2 >=0);
int temp = ar[index1];
ar[index1] = ar[index2];
ar[index2] = temp;
}
int main()
{
int ar[5] = {1,2,3,4,5};
int len = sizeof(ar) / sizeof(ar[0]);
Swap(ar,0,4);
for(int i = 0;i < len;i++){
printf("%d ",ar[i]);
}
return 0;
}
如图为运行结果,调换完成:
这里我写了一个Swap函数,其中形参index1和index2对应的就是需要交换的元素的下标,通过指针指向数组的下标实现元素的调换。
对此程序进行分析:
main主程序中有ar(地址为0x1),在main主程序中调用Swap(ar,0,4),在Swap函数中,有指针int *占四字节,ar保存下方实参和形参的关系,属地址拷贝。*ar与主函数中的ar数组进行关联(地址拷贝),在程序中temp保存index1位置元素,通过swap中的ar指针访问到数组的index1号下标元素,此时0号下标对应的元素为1,赋值给temo,然后index2号下标元素,赋值给ar指向的index2号下标元素,然后把temp赋值给index1号下标。
“[]”也具有解引用的功能
在此程序中,解引用通过数组的下标来进行元素的访问,比如程序中的语句:ar[index1] = ar[index2];*ar为解引用,ar[0]也具有解引用的功能,这两者其实是有关联的:
*ar <=> *(ar + 0);
ar[2] <=> *(ar+2);
这类似于我的上一篇博客,二维数组的学习 中所提到的数组名加1,我在计算二维数组的行数的时候曾经说过,行数 = 数组的总大小 / 数组类型,而当我以十进制输出数组和数组+1时发现两个地址所产生的差值 / 数组中元素的类型名的大小时,得出来的结果正好是二维数组一行的个数:
所以类型对指针变量起到两个作用:
(1)解析存储单元的大小
例如,int a = 10; int *p = &a; 因为指针类型为int 所以存储单元的大小为4字节
(2) 指针变量加1的能力
因为在这里牵扯到计算机的存储方式问题,因为我是macOS,不同于Windows操作系统,已知Windows电脑的存储方式是小端存储,即最低地址存放最低字节,先来验证我的电脑是大端存储还是小端存储:
#include<stdio.h>
void judge_litteEndian()
{
int i = 48;
int *p = &i;
char c = 0;
c = *((char *)p);
if(c == '0'){
printf("你的计算机的存储方式为小端存储\n");
}
else{
printf("你的计算机的存储方式为大端存储\n");
}
}
int main()
{
judge_litteEndian();
return 0;
}
将int 48存起来,然后取得其地址,再将这个地址转为char* 这时候,如果是小端存储,那么char*指针就指向48;
48对应的ASCII码为字符‘0’
运行结果为:
我的操作系统的存储方式为小端存储。
在这里给出一个一位数组:
int ar[5] = {1,2,3,4,5};
按照小端存储的方式进行存储,数组元素1和2的地址分别为:0x00000001、0x00000002,如图填充地址,当定义指针p指向数组时,(p + 1)则代表的是指针向后偏移一个单元格,此时指向20,所以*(p + 1)就是引用(p + 1)指向元素的地址,然后解开对应地址的元素,也就是数组中的第二个元素,2
所以在这里:
* (p + 1) <=> ar[2] 也就是 p + 1 <=> &ar[2]
作业练习:
作业一:
编一个程序,输入月份号,输出该月的英文月名,例如,输入3,则输出“March”.要求用指针数组处理:
#include<stdio.h>
int main()
{
int m = 0;
printf("Please input a number:\n");
scanf("%d",&m);
char *month[13] = {"Illegal_month","Januray","Feburay","March","April","May","June","July","August","Septmber","October","November","December"};
if(m <= 12 && m>= 0){
printf("The month is %s",*(month + m));
}
else{
printf("The month is %s",*(month));
}
return 0;
}
我分别输出0和12,运行结果为:
输出完成
作业二:
有n个整数,使前面各数顺序向后移m个位置,最后m个数变成最前面m个数,见图。写一个函数实现以上功能,在主函数中输入n个整数和输出调整后的n个数:
利用数组:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int array_backwards(int *array,int n,int m)
{
assert(array != NULL && n > 0 && m >0);
int i = 0;
int j = 0;
int temp = 0;
for(i = 0;i < m;++i){
temp = array[n - 1];
for(j = n - 2;j >= 0;j--){
array[j + 1] = array[j];
}
array[0] = temp;
}
return 0;
}
int main()
{
int m = 0;
int n = 0;
printf("请输入数组元素的个数:\n");
scanf("%d",&n);
int array[n];
printf("请输入这些元素:\n");
for(int i = 0;i < n;++i){
scanf("%d",&array[i]);
}
printf("请输入您需要移动的元素个数:\n");
scanf("%d",&m);
array_backwards(array,n,m);
for(int i = 0;i < n;++i){
printf("%d ",array[i]);
}
return 0;
}
运行结果:
利用指针:
这个方法的优点就是不用开辟额外的空间,直接利用指针在原数组进行操作,时间和空间复杂度都较低,先来理解这个方法,
例如在这里我给出一个数组:
int ar[5] = {1,2,3,4,5};
这里我需要将前两位挪到后面,后面的三个数挪到最前面,最终结果为:3 4 5 1 2
这里分三步进行:
第一步:将原数组整个进行逆转:变为:5 4 3 2 1
第二步:对前三个元素进行操作,使其变成3 4 5,那么我只需要将0号下标元素和2号下标元素进行调换即可,此为局部逆转
第三步:对后两个元素进行操作,使其变成1 2,也就是将3号下标元素,与末尾元素进行调换
由于所有的逆转都要进行调换工作,在这里直接写一个利用指针进行调换的函数Swap
上述三步操作如何写成适用于所有数组的函数?
全局逆转可以利用调换函数Swap,从第一个元素和最后一个元素调转开始,依此类推,逆转的终止条件就是左右指针重合或者相邻。
局部逆转分为两段,第一段可以从0号下标元素和len - m - 1号下标元素逆转(m代表需要挪动的数据个数),循环;
第二段从len - m号下标元素到末尾元素(len - 1号下标元素)调换,循环。
将思路代码化:
void Swap(int *p,int *q)
{
assert(p != nullptr && q != nullptr);
int temp = *p;
*p = *q;
*q = temp;
}
void Reverse(int *ar,int begin_index,int end_index)
{
assert(ar != nullptr && begin_index >= 0 && end_index >= 0);
int *p = ar + begin_index;
int *q = ar + end_index;
assert(p != nullptr && q != nullptr);
while(p < q){//指针p和q的不重合和不相邻是循环的条件,反之则终止
Swap(p,q);
p++;
q--;//每次循环后p指针向后偏移一位,q指针向前偏移一位
}
}
void Adjust(int *ar,int len,int m)//调整函数
{
assert(ar != nullptr && len >= 0 && m >= 0);
Reverse(ar,0,len - 1);//对数组中的元素进行全部逆转
Reverse(ar,0,len - m - 1);//区间逆转
Reverse(ar,len -m,len - 1);//区间逆转
}
int main()
{
int ar[5] = {1,2,3,4,5};
int m = 3;
Adjust(ar,5,3);
for(int i = 0;i < 5;i++){
printf("%2d",ar[i]);
}
return 0;
}
运行结果:
调换完成
作业三:
写一函数,实现两个字符串的比较。 即自己写一个strcmp函数,函数原型为
mtstrcmp(char ·*pl.char ·><' p2);
设 pl 指向字符串 sl, p2 指向字符串 sZ。 要 求 当 sl=s2 时,返回值为 O;若 sl-::/=-s2.返回它 们二者第 1个不同字符的 ASCII码差值(如"BOY"与"BAD",第 2个字母不同,0与 A之差为79-65=14)。 如果sl>s2,则输出正值;如果sl<s2,则输出负值。
#include<stdio.h>
int my_strcmp(char *p1,char *p2)
{
int i = 0;
while(*(p1 + i) == *(p2 + i)){
if(*(p1 + i++) == '\0'){
return (0);
}
else{
return(*(p1 + i) - *(p2 + i));
}
}
}
int main()
{
char str1[100];
char str2[100];
char *p1 = &str1[0];
char *p2 = &str2[0];
printf("请输入需要比较的字符串:\n");
scanf("%s%s",str1,str2);
printf("字符串比较后的结果是:%d ",my_strcmp(p1,p2));
return 0;
}
例如我分别输入字符串:BOY和BAD,运行结果如图:
边栏推荐
猜你喜欢
论文解读:跨模态/多光谱/多模态检测 Cross-Modality Fusion Transformer for Multispectral Object Detection
Wonderful linkage | OpenMLDB Pulsar Connector principle and practical operation
星盟-pwn-fog
js学习进阶BOM部分(pink老师笔记)
经纬度求距离
[Meetup]OpenMLDBxDolphinScheduler 链接特征工程与调度环节,打造端到端MLOps工作流
JVM学习四:垃圾收集器与内存回收策略
【无标题】
精彩联动 | OpenMLDB Pulsar Connector原理和实操
本地缓存cookie的使用
随机推荐
Jetpack use exception problem collection
开源之夏 2022 火热来袭 | 欢迎报名 OpenMLDB 社区项目~
Building a data ecology for feature engineering - Embrace the open source ecology, OpenMLDB fully opens up the MLOps ecological tool chain
Interpretation of the paper: GAN and detection network multi-task/SOD-MTGAN: Small Object Detection via Multi-Task Generative Adversarial Network
Day 84
Event Preview | On April 23, a number of wonderful sharing sessions of OpenMLDB will come, which will live up to the good time of the weekend
Visual studio2019 configuration uses pthread
The whole process of Tinker access --- Compilation
Fourth Paradigm OpenMLDB optimization innovation paper was accepted by VLDB, the top international database association
OpenMLDB: Consistent production-level feature computing platform online and offline
127.0.0.1 connection refused
Byte (byte) and bit (bit)
Manufacturer Push Platform-Huawei Access
Visual studio2019 配置使用pthread
Day 75
Day 77
The third phase of the contributor task is wonderful
Simple mine sweeping in C language (with source code)
C-动态内存管理
C语言实现猜数字(附带源码,可直接运行)