C语言-指针(2)

C/C++面试高频知识点八股文 带你了解C/C++必须要会的知识点,告诉你怎么答。 阅读详情

本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。例如:

int(*ptr)[10];

  则在32位程序中,有:


sizeof(int(*)[10])==4
sizeof(int[10])==40
sizeof(ptr)==4


  实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。

  指针和结构类型的关系


  可以声明一个指向结构类型对象的指针。

  例十一:


structMyStruct
{
 inta;
 intb;
 intc;
}
MyStructss={20,30,40};
//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。
MyStruct*ptr=&ss;
//声明了一个指向结构对象ss的指针。它的类型是MyStruct*,它指向的类型是MyStruct。
int*pstr=(int*)&ss;
//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。


  请问怎样通过指针ptr来访问ss的三个成员变量?

  答案:

ptr->a;
ptr->b;
ptr->c;

  又请问怎样通过指针pstr来访问ss的三个成员变量?

  答案:

*pstr;//访问了ss的成员a。
*(pstr+1);//访问了ss的成员b。
*(pstr+2)//访问了ss的成员c。

  虽然我在我的MSVC++6.0上调式过上述代码,但是要知道,这样使用pstr来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指针来访问数组的各个单元:

  例十二:

intarray[3]={35,56,37};
int*pa=array;

  通过指针pa访问数组array的三个单元的方法是:

*pa;//访问了第0号单元
*(pa+1);//访问了第1号单元
*(pa+2);//访问了第2号单元

  从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。

  所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个"填充字节",这就导致各个成员之间可能会有若干个字节的空隙。

  所以,在例十二中,即使*pstr访问到了结构对象ss的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员a和成员b之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,嘿,这倒是个不错的方法。

通过指针访问结构成员的正确方法应该是象例十二中使用指针ptr的方法。

  指针和函数的关系

  可以把一个指针声明成为一个指向函数的指针。


intfun1(char*,int);
int(*pfun1)(char*,int);
pfun1=fun1;
....
....
inta=(*pfun1)("abcdefg",7);//通过函数指针调用函数。


  可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。

  例十三:


intfun(char*);
inta;
charstr[]="abcdefghijklmn";
a=fun(str);
...
...
intfun(char*s)
{
intnum=0;
for(inti=0;i{
num+=*s;s++;
}
returnnum;
}


  这个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。前面说了,数组的名字也是一个指针。在函数调用中,当把str作为实参传递给形参s后,实际是把str的值传递给了s,s所指向的地址就和str所指向的地址一致,但是str和s各自占用各自的存储空间。在函数体内对s进行自加1运算,并不意味着同时对str进行了自加1运算。
  指针类型转换


  当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。在我们前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。

  例十四:

  1、floatf=12.3;

  2、float*fptr=&f;

  3、int*p;
 
  在上面的例子中,假如我们想让指针p指向实数f,应该怎么搞?是用下面的语句吗?

  p=&f;

  不对。因为指针p的类型是int*,它指向的类型是int。表达式&f的结果是一个指针,指针的类型是float*,它指向的类型是float。两者不一致,直接赋值的方法是不行的。至少在我的MSVC++6.0上,对指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致,其它的编译器上我没试过,大家可以试试。为了实现我们的目的,需要进行"强制类型转换":


p=(int*)&f;


  如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*TYPE, 那么语法格式是:

  (TYPE*)p;

  这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。

一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,也会发生指针类型的转换。

  例十五:


voidfun(char*);
inta=125,b;
fun((char*)&a);
...
...
voidfun(char*s)
{
charc;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
}


  注意这是一个32位程序,故int类型占了四个字节,char类型占一个字节。函数fun的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗?在函数调用语句中,实参&a的结果是一个指针,它的类型是int*,它指向的类型是int。形参这个指针的类型是char*,它指向的类型是char。这样,在实参和形参的结合过程中,我们必须进行一次从int*类型到char*类型的转换。结合这个例子,我们可以这样来想象编译器进行转换的过程:编译器先构造一个临时指针char*temp, 然后执行temp=(char*)&a,最后再把temp的值传递给s。所以最后的结果是:s的类型是char*,它指向的类型是char,它指向的地址就是a的首地址。

  我们已经知道,指针的值就是指针指向的地址,在32位程序中,指针的值其实是一个32位整数。那可不可以把一个整数当作指针的值直接赋给指针呢?就象下面的语句:


unsignedinta;
TYPE*ptr;//TYPE是int,char或结构类型等等类型。
...
...
a=20345686;
ptr=20345686;//我们的目的是要使指针ptr指向地址20345686(十进制
)
ptr=a;//我们的目的是要使指针ptr指向地址20345686(十进制)


  编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗?不,还有办法:


unsignedinta;
TYPE*ptr;//TYPE是int,char或结构类型等等类型。
...
...
a=某个数,这个数必须代表一个合法的地址;
ptr=(TYPE*)a;//呵呵,这就可以了。


  严格说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。这里的(TYPE*)的意思是把无符号整数a的值当作一个地址来看待。上面强调了a的值必须代表一个合法的地址,否则的话,在你使用ptr的时候,就会出现非法操作错误。
想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。完 全可以。下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个整数当作一个地址赋给一个指针:

  例十六:


inta=123,b;
int*ptr=&a;
char*str;
b=(int)ptr;//把指针ptr的值当作一个整数取出来。
str=(char*)b;//把这个整数的值当作一个地址赋给指针str。


  现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。

  指针的安全问题

  看下面的例子:

  例十七:


chars='a';
int*ptr;
ptr=(int*)&s;
*ptr=1298;



  指针ptr是一个int*类型的指针,它指向的类型是int。它指向的地址就是s的首地址。在32位程序中,s占一个字节,int类型占四个字节。最后一条语句不但改变了s所占的一个字节,还把和s相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。

  让我们再来看一例:

  例十八:

  1、chara;

  2、int*ptr=&a;

  ...
  ...

  3、ptr++;

  4、*ptr=115;

  该例子完全可以通过编译,并能执行。但是看到没有?第3句对指针ptr进行自加1运算后,ptr指向了和整形变量a相邻的高地址方向的一块存储区。这块存储区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代码。而第4句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用指针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。

  在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是不安全的。至于为什么,读者结合例十七来想一想,应该会明白的。

别再死磕MuJoCo了!用PyBullet+Stable-Baselines3搞定机械臂强化学习(保姆级环境配置) 本文详细介绍了如何使用PyBullet和Stable-Baselines3构建机械臂强化学习系统,绕过昂贵的MuJoCo许可壁垒。通过实战指南,包括环境配置、机械臂Gym环境构建、训练策略与调优技巧,帮助开发者快速掌握机器人强化学习技术,提升智能控制效率。 阅读详情

相关推荐

设计师必看!用Figma MCP自动生成开发文档的隐藏技巧

本文为设计师揭秘利用Figma MCP协议自动生成开发文档的隐藏技巧。通过将设计稿转化为结构化数据,实现设计规范的自动化输出、版本同步与动态更新,并借助AI助手提升协作效率,彻底告别繁琐的手动标注与沟通。

berry的博客 923

请编写函数int fun( char *s),统计一行字符串中单词的个数作为函数值返回。规定所有单词由小写字母组成,单词之间由若干个空格隔开,一行的开始没有空格。

#include<stdio.h> #include <string.h> #define N 200 //字符串最大个数 int fun( char *s) { int n=1; while(*s!='\0') //当不上\0的时候判断条件 { if(*s==' ') { n++; } s++; } return n; } main() { int n=0; char aa[200]; printf("请输入1组的字符串:

weixin_45619208的博客 2773

大麦网回流票监控实战:如何利用sign参数和cookie实现自动抢票(避坑指南)

本文详细介绍了如何利用sign参数和cookie构建大麦网回流票监控系统,实现自动化抢票。通过分析大麦网票务系统的核心机制、获取关键身份凭证、逆向分析sign签名算法,并构建完整的监控系统,帮助用户高效捕捉回流票机会。文章还提供了实战避坑指南和合法合规的注意事项,确保系统稳定运行且符合平台规则。

weixin_28568819的博客 411

c++char* s作为函数形参并不能改变s

c++char* s作为函数形参并不能改变s 今天写了一个c的代码 ,发现把char* s作为函数的参数传入,在函数中对s进行++,在主函数中s仍然是原来的值,没有改变。 例子: #include<stdio.h> int fun(char *s) { int n = 0; for (; *s != '\0'; s++) { n++; } return n; } int main() { char *s = "hgksjafhs"; printf("%s\n", s);

weixin_44125720的博客 1667

Fluent动网格+滑移网格+重叠网格对比介绍(以震荡水翼flapping foil为例)

初学fluent笔记,本文主要包含: Flapping foil在fluent内的几种实现方式 在哪设置动网格、滑移、重叠网格 flapping的UDF 示例 写在前面:Fluent在预览网格运动/提交计算之前,注意保存一个cas版本。 -------------------------Flapping foil 网格几种组合实现方式:---------------------- 直接重叠网格,overset, 需要建立两个几何part,part要重合。水翼局部区域(component),背景网格(ba

深海民工CFDer 2万+

【vue看板】

大屏看板提示:以下是本篇文章正文内容,下面案例可供参考提示:这里对文章进行总结:代码量较多,看板中的。

weixin_46223284的博客 2771

#define BYTE0(dwTemp) ( *( (char *)(&dwTemp) ) )是什么意思?

1.数据拆分宏定义 #define BYTE0(dwTemp) ( *( (char *)(&dwTemp) ) ) #define BYTE1(dwTemp) ( *( (char *)(&dwTemp) + 1) ) #define BYTE2(dwTemp) ( *( (char *)(&dwTemp) + 2) ) #d...

quinn1994的博客 2310

【推荐100个unity插件之8】实现多人在线联机游戏——Mirror插件的使用介绍(附项目源码)

基于Mirror的Unity多人游戏开发指南 本文介绍了使用Mirror插件开发Unity多人游戏的基本流程。Mirror是Unity的高级网络API,替代了已废弃的UNet系统。文章详细讲解了如何导入Mirror插件,创建NetworkManager网络管理器,处理常见错误,以及创建玩家角色和出生点。 主要内容包括: 创建场景NetworkManager并配置必要组件 为玩家添加NetworkIdentity和NetworkTransform组件 设置玩家初始生成位置和生成方式 常见错误及解决方法 通过M

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1万+

C语言中char*和char[]用法区别分析&微软编译器的坑

一般来说,很多人会觉得这两个定义效果一样,其实差别很大。以下是个人的一些看法,有不正确的地方望指正。 本质上来说,char *s定义了一个char型的指针,它只知道所指向的内存单元,并不知道这个内存单元有多大,所以: 当char *s = "hello";后,不能使用s[0]='a';语句进行赋值。这是将提示内存不能为"written"。 当用char s[]="hello";后

hsx5555621的博客 1183

在 SAP 里玩转 FTP:标准程序 RSFTP00x 全景解析与落地实践

摘要:SAP系统通过封装的标准FTP程序(RSFTP001-008)提供便捷的文件传输能力,适用于各类集成场景。关键点包括:1)依赖SAPFTP可执行程序和RFC目的地;2)使用RSFTP005进行前置检查并生成RFC配置;3)标准报表各司其职,如RSFTP002用于命令测试、RSFTP007/008提供参考实现;4)开发时组合FTP_CONNECT/COMMAND/DISCONNECT等函数模块。特别注意前台(SAPFTP)与后台(SAPFTPA)的环境差异,这是常见故障点。该方案通过标准化工具链显著降低

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 209

生成模型实战 | StyleGAN详解与实现

StyleGAN 能够生成非常高分辨率人脸图像的关键在于,在增加分辨率的步骤中逐步增加生成网络和判别网络的复杂性,以便在每一步中,两个模型都可以很好地完成任务。我们学习了如何通过确保每个分辨率的特征由一个独立的输入(称为风格向量)来控制,从而操纵生成图像的风格,以及如何通过将图像之间的风格进行交换来操作不同图像的风格。

盼小辉丶的博客 7565

【C语言】指针2——高级指针

const 在 * 的左边,说明 const 是用来修饰 *p 的,*p 不能被改变,保证指针指向的地址所存放的值不能通过指针来改变,但指针指向的地址是可以被改变的。因为 p 存放着 a 的地址,所以 p 的值是 0000009484F3FA14,解引用拿到 a 的值,所以 *p 是 5。因为 m 存放着 p 的地址,解引用拿到 p 的值,所以 *m 是 5,则再解引用,拿到 a 的值,所以 **m 是 5。所以我们使用指针前,先用 assert 断言检测指针的有效性,就可以避免野指针的出现。

2401_87753837的博客 1603

化工模拟与优化软件:Aspen HYSYS二次开发_(15).工业应用与项目管理

项目背景、目标和主要流程。

kkchenjj的博客 1129

C语言指针详解2

C语言指针详解(2)

2401_84797190的博客 1705

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

【C语言】指针笔试题2

C语言指针笔试题解析摘要 本文通过5道典型C语言指针笔试题,深入解析指针运算、数组指针、强制类型转换等核心概念: 数组指针运算:&a+1跳过整个数组,ptr-1回退指向最后一个元素 结构体指针运算:不同类型指针加减运算的字节差异(结构体20字节 vs 无符号长整形) 内存布局分析:结合小端存储模式,解析强制类型转换后的指针访问(0x02000000) 逗号表达式陷阱:数组初始化中(0,1)实际值为1,影响后续元素访问 数组指针类型差异:int(*p)[4]与int[5][5]的指针运算差异导致-4的

suxi_na的博客 1160

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

C语言指针2

指针 字符指针 在指针的类型中,字符指针表示方式char* 一般使用: int main() { char ch='w'; char *pc=&ch; *pc='w'; return 0; } 不常见: int main() { char* pstr = "hello world"; printf("%s\n", pstr); return 0...

Nakahara_Chuya的博客 272

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

c语言中指针p 2什么意思,C语言中的指针以及二级指针代码详解

很多初学者都对C中的指针很迷糊,希望这篇blog能帮助到大家:1.什么是“指针”:在执行C程序的时候,由于我们的数据是存储在内存中的。所以对于C程序本身来说,如果想找到相应被调用的数据,就要知道存储该数据的内存地址是多少,换言之,C程序通过已知的内存地址到相应的内存位置存储数据。这里简单说一下内存管理(对于初学者来说。为了避免专业术语引发的理解问题,下面的叙述尽量避免专业定义:),对于现代计算机系...

weixin_39586683的博客 835

【C语言】带你层层深入指针——指针详解2

本文深入解析C语言指针的核心概念,分为字符指针、数组指针和函数指针三部分。字符指针部分演示了常量字符串地址的存储特性,多个指针指向同一字符串时地址相同。数组指针部分详细讲解了数组名与地址的关系,强调一维数组传参本质是传递首元素地址,二维数组传参则是传递行地址。函数指针部分介绍了声明语法和使用方法,可通过指针直接调用函数。最后通过传址调用示例,说明通过指针修改变量值的原理。全文通过代码示例和运行结果,帮助读者逐步理解指针的多层次应用。

zore__的博客 1696

c语言指针2,c语言指针2

指针的重要性#include/*指针的重要性:1,表示一些复杂的数据结构2,快速的传输数据3,使函数返回一个以上的值4,能直接访问硬件5,能够方便的处理字符串6,是理解面向对象语言中引用的基础总结:指针是c语言的灵魂指针的定义:地址:内存单元的编号从零开始的非负数整数范围:如果是32位的机器就是从【0--4G-1】指针:指针就是地址,地址就是指针指针变量就是存放内存单元编号的变量,或者说指针变量...

weixin_39753857的博客 247

C语言: 指针 -2

一 指针的运算 指针可以作为算数运算,赋值运算和关系运算表达式的有效操作。但是,并非所有在这些表达式中使用运算符都可以处理指针变量。指针只能参与有限的几种算数运算。指针可以进行增 1 (++) 和 减 1(--),给指针加上一个整数(+或+=),从指针中减去一个整数(-或-=),以及用一个指针减去另外一个指针这几种运算。 示例: #include<stdio.h> #inclu...

C++++ ++ 795

c语言指针2

c语言指针2:void空指针,const关键字修饰指针变量,指针的运算,野指针,如何避免野指针

something for nothing的博客 763

C语言—指针2

我们把a,b传给函数Swap,Swap函数内部创建了形参x和y接受a和b的值,但我们可以看到x和y确实接收了a和b的值并且交换了,但是他们的地址是不一样的,所以交换x和y并不会影响到a和b的值,Swap函数在使用的时候,是把变量本身传给了函数,这种调用的方式我们叫传值调用。arr[i]是访问arr数组的元素,arr[i]找到数组的元素指向了整型一维数组,arr[i][j]就是整型一维数组中的元素。——&数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址是和首元素的地址是有区别的)

2301_79842481的博客 826

【C语言】指针2

在上一章节我们在使用指针访问数组的内容的时候,我们有下面这样的代码在上述的代码中我们使用&arr[0]来取得数组首元素的地址,但是我们前面也说过,数组名字 其实也就是数组的地址,而且还是首元素的地址。那么是不是说&arr[ 0 ]=arr;呢?下面我们 通过测试看看。代码如下:输出结果:可以看到数组名和数组首元素地址打印出的结果是一模一样的,数组名就是首元素的地址, 那么如果数组名就是首元素地址那么有下面的代码,又该如何解释呢?

2302_81083101的博客 1245

C语言之指针2

这一篇的有些内容牵连很广,本是想简单提下各种概念和用法,主写各种应用时需要注意的一些重要内容,但写着写着发现篇幅有点长了,那这里先浅浅划分一下,搞清楚概念和简单用法,后续补充拓展吧。

m0_71936409的博客 900
上一篇: C语言-指针(1)
下一篇: [面试题]C基础题目-测试下自己(附答案)
linuxgroup
博客等级 码龄18年 15粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值