再战指针!(C Primer Plus 第六版基础整合,2024年最新公司初级程序员Java面试题

  • 小知识点

  • 八、保护数组中的数据

    • 对形参使用const
  • 九、指针和多维数组的关系

    • 实例
  • 十、总结

  • 十一、复习题

学前服用


动画版的这个听完再去看书上的实例真的很不错!

https://www.bilibili.com/video/BV1MJ411D7EX

前言


刚升本科学校开了一门数据结构,自己在b站往后学了一下发现不搞懂C语言的指针很难去理解,于是我又重温经典C Primer Plus

这本书写的真的很细致,加上看书和看优质的b站视频,我对指针有了一点简单的了解

纸上得来终觉浅,绝知此事要躬行

好了,开始奥里给了~

资源在这

Screenshot_20211001_171431_com.tencent.mm.jpg

它来了,它来了。C语言的灵魂!

没错它就是指针

上干货!!!

一、什么是指针


从根本上看,指针是一个值为内存地址的变量,如int类型的值是整数

char类型的值是字符

指针变量的值就是内存地址

二、 指针的用法


把指针作为函数参数使用,以及为何这样用

假设一个指针变量名是ptr,可以编写如下语句:

ptr = &pooh; //把pooh的地址赋给ptr

对于这条语句,我们说ptr“指向”pooh。

ptr和&pooh 的区别是ptr是变量,而&pooh是常量。或者,ptr是可修改的左值,而&pooh是右值。

当然还可以把ptr指向别处:

ptr = &bah; //把ptr指向bah,而不是pooh

现在ptr的值是bah的地址。

要创建指针变量,先要声明指针变量的类型。假设想把ptr声明为储存int类型变量地址的指针,就要使用下面的新运算符。

2.1 间接运算符 *

假设已知 ptr指向bah,

如下所示:

ptr = &bah;

然后使用间接运算符* (indirection operator)找出储存在 bah 中的值,该运算符有时也称为解引用运算符(dereferencing operator)。不要把间接运算符和二元乘法运算符(*)混淆,虽然它们使用的符号相同,但语法功能不同。

val = *ptr;//找出ptr指向的值

语句ptr = &bah;和val = *ptr;放在一起相当于下面的语句;

val = bah

我的理解:

指针变量里面存放的是内存地址,既然是存放,那就有对于的取出操作,怎么取出?就在指针变量前面加上*即可!

小总结:

地址运算符:&

后面跟一个变量名时,&给出该变量的地址

示例:

&nurse表示变量nurse的地址

地址运算符:*

后跟一个指针名或地址时,*给出存储在指针指向地址上的值

示例:

nurse = 22;

ptr = &nurse; //指向nurse的指针

val = *ptr; //ptr指向的地址上的值赋给val

以上三条语句的结果是把22赋给val

2.2 声明指针

声明指针变量时,必须指定指针所指向变量的类型。咋听着这么绕呢?

用我的理解来说就是:

指针的变量类型要和指针所指向变量的类型一样,指针值的变量是啥类型,指针变量就是啥类型

例如:

int * pi; // pi是指向int类型变量的指针

char * pc; // pc是指向char类型变量的指针

float * pf, * pg; // pf、pg都是指向float类型变量的指针

类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。int * pi;声明的意思是pi是一个指针,*pi是int类型

在这里插入图片描述

这个图是从上往下看,能简单理解下使用指针的过程

*和指针名之间的空格可有可无。通常,程序员在声明时使用空格,在解引用变量时省略空格。

2.3 使用指针在函数间通信

#include <stdio.h>

void interchange(int * u,int * v);//声明在函数里传入一个int类型指针u和int类型指针v

int main(void)

{

//解决交换函数的问题

int x = 5, y = 10;

printf(“Originally x = %d and y = %d.\n”, x, y);

interchange(&x,&y); //把地址发送给函数

printf(“Now x = %d and y = %d.\n”,x ,y);

return 0;

}

void interchange(int * u, int * v)

{

int temp;

temp = *u;

*u = *v;

*v = temp;

}

程序分析

首先看函数调用,

interchange(&x,&y);这里的两个参数,不是x和y的值,而是x和y的地址

后来就是通过一个temp变量进行变量的地址交换

小总结:

变量:名称、地址和值

编写程序时,可以认为变量有两个属性:名称和值(还有其他性质,如类型,暂不讨论)。

计算机编译和加载程序后,认为变量也有两个属性:地址和值。地址就是变量在计算机内部的名称。

简而言之,普通变量把值作为基本量,把地址作为通过&运算符获得的派生量,而指针变量把地址作为基本量,把值作为通过*运算符获得的派生量。

三、指针和数组


指针提供一种以符号形式使用地址的方法。因为计算机硬件非常依赖于地址,指针在某种程度上把程序员想要传达的指令以更尽皆机器的方式表达。

使用指针能让程序更有效率

flizny == &flizny[ 0]; //数组名是该数组首元素的地址

flizny和sflizny[0] 都表示数组首元素的内存地址(&是地址运算符)。两者都是常量,在程序的运行过程中,不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值

#include <stdio.h>

#define SIZE 4

int main(void)

{

short dates[SIZE];

short * pti;

short index;

double bills[SIZE];

double * ptf;

pti = dates;//把数组地址赋值给指针

ptf = bills;

for (index = 0;index < SIZE; index++)

{

printf(“pointers+%d : %p %p\n”,index,pti + index,ptf+index);

}

return 0;

}

pointers+0 : 000000000061FE00 000000000061FDE0

pointers+1 : 000000000061FE02 000000000061FDE8

pointers+2 : 000000000061FE04 000000000061FDF0

pointers+3 : 000000000061FE06 000000000061FDF8

我们的系统中,地址按字节编址,short类型占用2字节,double类型占用8字节。在C中,指针加1指的是增加一个存储单元。

对数组而言,**这意味着把加1后的地址是下一个元素的地址,而不是下一个字节的地址,这是为什么必须声明指针所指向对象类型的原因之一。**只知道地址不够,因为计算机要知道储存对象需要多少字节(即使指针指向的是标量变量,也要知道变量的类型,否则*pt就无法正确地取回地址上的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N1lGRJaX-1634202380884)(E:\Typora\Image\image-20211012153924351.png)]

对于上图的理解:

在C语言中,指针加1是增加一个存储单元,因为pti指针的类型是short类型,所以指针+1,其值每次递增两个字节

  • 指针的值是它所指向对象的地址。地址的表示方式依赖于计算机内部的硬件。许多计算机(包括PC和 Macintosh)都是按字节编址,意思是内存中的每个字节都按顺序编号。这里,一个较大对象的地址(如double类型的变量)通常是该对象第一个字节的地址。

  • 在指针前面使用*运算符可以得到该指针所指向对象的值。

  • 指针加1,指针的值递增它所指向类型的大小(以字节为单位)。

data + 2 == &data[2]; //相同地址

*(data + 2) == data[2]; //相同的值

容易混淆的概念

*(dates + 2) //dates第3个元素的值

*dates + 2 //dates的第一个元素的值加2

实例

#include <stdio.h>

#define MONTH 12

int main(void)

{

int days[MONTH] = {31,28,31,30,31,30,31,31,30,31,30,31};

int index;

for (index = 0; index < MONTH; index++)

{

printf(“Month %2d has %d days.\n”,index+1,*(days + index));

}

return 0;

}

四、函数、数组和指针


假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为marbles的int类型数组。应该如何调用该函数?也许是下面这样:

total = sum(marbles);//可能的函数调用

那么,该函数的原型是什么?记住,数组名是该数组首元素的地址,所以实际参数marbles是一个存储int类型值的地址,应把它赋给一个指针形式参数,即该形参是一个指向int的指针

int sum(int * ar); //对应的原型

sum()从该参数获得了什么信息?它获得了该数组首元素的地址,知道要在该位置上找出一个整数,注意,该参数并未包含数组元素个数的信息。第一种方法是,在函数代码中写上固定的数组大小;

实例

#include <stdio.h>

#define SIZE 10

int sum(int ar[],int n);

int main(void)

{

int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20};

long answer;

answer = sum(marbles,SIZE);

printf(“The total number of marbles is %ld.\n”,answer);

printf(“The size of marbles is %zd bytes.\n”,sizeof marbles);

return 0;

}

int sum(int ar[], int n) //数组的大小是?

{

int i;

int total = 0;

for ( i = 0; i < n; i++)

total += ar[i];

printf(“The size of ar is %zd bytes.\n”, sizeof ar);//ar是一个指向

return total;//sum函数会返回一个数组所有元素之和

}

输出结果为:

The size of ar is 8 bytes.

The total number of marbles is 190.

The size of marbles is 40 bytes.

注意,marbles的大小是40字节。这没问题,因为marbles内含10个int类型的值,每个值占

4字节,所以整个marbles的大小是40字节。

但是,ar才8字节。这是因为ar并不是数组本身,它是一个指向marbles数组首元素的指针。

我们的系统中用8字节储存地址,所以指针变量的大小是8字节(其他系统中地址的大小可能不是8字节)。

ar是一个指向marbles数组首元素的指针,利用C中数组和指针的特殊关系,可以用数组表示法来表示指针ar。

五、使用指针形参


函数要处理数组必须知道何时开始、何时结束。sum()函数使用一个指针形参标识数组的开始,用一个整数形参表明待处理数组的元素个数(指针形参也表明了数组中的数据类型)。但是这并不是给函数传递必备信息的唯一方法。 还有一种方法是传递两个指针,第1个指针指明数组的开始处(与前面用法相同), 第2个指针指明数组的结束处。同时该程序也表明了指针形参是变量,这意味着可以用索引表明访问数组中的哪一个 元素。

实例

#include <stdio.h>

#define SIZE 10

int sump(int * start,int * end);

int main(void)

{

int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20};

long answer;

answer = sump(marbles,marbles + SIZE);

printf(“The total number of marbles is %ld .\n”,answer);

return 0;

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。

《Java高级面试》

《Java高级架构知识》

《算法知识》

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

外链图片转存中…(img-eAFsJ3SK-1712812784264)]
[外链图片转存中…(img-n0cpk0FE-1712812784264)]
[外链图片转存中…(img-OwDTyvjf-1712812784264)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-YN4FhXoE-1712812784265)]

最后

分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。

《Java高级面试》

[外链图片转存中…(img-6SD5Ylq7-1712812784265)]

《Java高级架构知识》

[外链图片转存中…(img-7WUa3gYw-1712812784266)]

《算法知识》

[外链图片转存中…(img-Y8absJFH-1712812784266)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-RXpwLfzs-1712812784266)]

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值