“一个有趣的C语言代码”分析

news2024/11/17 21:43:54

“一个有趣的C语言代码”

一个有趣的C语言代码-流浪的海豚-ChinaUnix博客

#include <stdio.h>
int print() {
  printf("hello world!\n");
  return 0;
}
int main(void) {
  long base[0];
  long* result = base+3;
  *(result+1) = *result;
  *result = (long)print;
  return 0;
}

该代码运行在64位linux机器上。运行结果是打印了hello world 。但是代码里面没有主动调用print函数。

ChatGPT

该代码看似没有直接调用 print 函数,但实际上通过对指针的不当操作实现了间接调用。在代码中,发生了指针越界写入,这是一种未定义行为,其结果在不同的平台和编译器上可能不同,但代码所展示的行为是在特定的条件下覆盖了`main`函数中`return`地址的值,使得`main`函数返回时跳转到了`print`函数。
下面是代码的步骤解析:

int main(void) {
  long base[0];          // 声明了一个长度为0的数组
  long* result = base+3; // 指针越界,`result`现在指向base后第3个位置的地址
  *(result+1) = *result; // 未定义行为:`result+1`可能是main函数的返回地址,但这里尚未被赋值为print函数地址
  *result = (long)print; // 未定义行为:`result`写入print函数的地址,可能是覆盖了原本的返回地址
  return 0;              // main函数返回时,可能会跳转到print函数,而不是正常返回
}

在第10行,`result`变量指向了`base`的第3个位置,但由于`base`是一个长度为0的数组,这已经是一个非法操作,因为`base`只是一个边界标记,并没有有效的存储空间。
第12行将 print 函数的地址写入了 result 指向的位置。如果这个位置是当前函数的返回地址(在栈上),那么`main`函数结束时会跳转到`print`函数,从而导致了打印`hello world!`的行为。
然而,此行为具有极高的不确定性,因为栈的布局取决于很多因素,包括编译器,编译选项,操作系统,甚至是当前运行时环境。在一些情况下,这种代码可能导致程序崩溃或执行不预期的函数。
这种类型的编写实践是危险且不应该在实际编程中使用的,它可能导致安全漏洞,如缓冲区溢出攻击。
在开发中,不应依赖于这种未定义行为,而应采用定义明确、安全的方法来实现程序需求。此外,编译器在高优化等级下可能会移除这类未定义行为的代码或以不同的方式进行优化,导致不同的执行结果。

通义灵码

这段代码存在未定义行为(Undefined Behavior),具体表现在以下几个方面:

  1. 声明长度为0的数组: 在main()函数中,声明了一个长度为0的long类型数组base[0]。根据C语言标准,长度为0的数组并不合法,虽然某些编译器可能会允许这种声明,但在内存分配和访问时可能会导致不可预测的结果。

  2. 访问空指针: 由于base[0]是一个长度为0的数组,其并不占用任何内存空间。接着,代码通过long* result = base+3;试图获取该数组的第四个元素的地址。由于数组没有实际元素,这意味着result实际上指向了一个未定义的内存位置。后续对result及其偏移量的访问(如*(result+1)*result)都是对未初始化内存的非法访问,属于未定义行为。

  3. 将函数指针赋值给未定义内存: *result = (long)print;语句试图将print函数的地址(转换为long类型)赋值给*result所指向的未定义内存位置。这同样违反了内存访问规则,且可能导致程序崩溃或产生其他不可预期的行为。

尽管这段代码存在未定义行为,但您提到它在运行时打印出了“hello world”。这种情况可能是由于以下原因:

  • 内存布局偶然性: 在特定的编译器版本、操作系统和运行环境组合下,这段代码的未定义行为碰巧没有立即触发程序崩溃或其他严重错误。base变量可能恰好被分配在内存中的某个位置,使得对result及其偏移量的非法访问没有立即破坏关键的系统数据结构或触发段错误。

  • 函数指针赋值引发间接调用: 尽管print函数的地址被错误地赋给了未定义内存,但后续的某个操作(可能是对*result的间接访问或由于未定义行为导致的内存混乱)可能意外触发了对print函数的调用。这解释了为何程序打印出了“hello world”。

总之,这段代码存在多个未定义行为,不应被视为正常工作或可接受的编程实践。其运行结果取决于具体的编译器、操作系统和运行环境,且随时可能因这些因素的改变而发生改变。在编写和调试程序时,应避免此类未定义行为,确保内存访问的安全性和代码的可预测性。

文心一言

这段代码通过一种非常规且依赖于特定平台和编译器行为的方式,间接地调用了print函数。它利用了C语言中指针的特性和内存布局来达成这一目的。

下面是对这段代码的分析:

  1. long base[0];:这里声明了一个长度为0的数组。在C语言中,这样的声明是不合法的,因为数组的大小必须为正数。然而,一些编译器可能会分配一些空间给这样的数组,但这完全是编译器特定的行为,并不保证在所有编译器上都有效。

  2. long* result = base+3;:这里创建了一个指向base数组第4个元素的指针(数组索引从0开始)。因为base实际上没有分配任何元素,所以result指向的是一个未定义的内存位置。

  3. *(result+1) = *result;:这行代码试图将result指向的内存位置的值复制到result+1指向的位置。但由于base是一个长度为0的数组,这些位置都是未定义的,所以这样的操作是无效的,并且可能导致运行时错误。

  4. *result = (long)print;:这里,将print函数的地址强制转换为long类型,并将其赋值给result指向的内存位置。

  5. 当你运行这个程序时,由于main函数的返回类型是int,而main函数返回0,大多数操作系统会将这个返回值解释为程序正常退出。在程序退出时,操作系统可能会检查并调用在退出过程中需要清理的资源,包括恢复堆栈。

  6. 当堆栈被恢复时,操作系统可能会检查堆栈上的返回地址,并尝试跳转到那个地址。由于我们之前将print函数的地址写入了堆栈(通过*result = (long)print;),那个地址很可能是print函数的地址。

  7. 因此,当程序退出并恢复堆栈时,它可能会错误地跳转到print函数的地址,并执行print函数,导致hello world!被打印出来。

这种行为高度依赖于具体的编译器、操作系统和运行时环境,并且是不安全的。它利用了未定义的行为和平台特定的行为,因此不应该被视为一种可靠或可移植的编程技巧。

总的来说,这段代码展示了C语言中一些低级的、不安全的操作,这些操作不应该在正常的程序中使用。它依赖于许多不确定和不可预测的行为,因此其运行结果在不同的环境或编译器下可能会有所不同。

讯飞星火

该代码在64位Linux机器上运行时,会打印出"hello world!"。这是因为在main函数中,将print函数的地址赋值给了base数组的第一个元素,然后通过指针操作,将print函数的地址赋值给了result指针指向的元素,最后通过解引用result指针并强制转换为long类型,调用了print函数。

Kimi

这段代码实际上涉及到了C语言中的一些指针操作和函数指针的概念。代码中的print函数返回一个整数,但在64位系统上,long类型通常是64位的,足以存储一个函数的地址。这里的关键点在于如何使用这些指针。

让我们逐步分析代码:

  1. int print() { ... }:定义了一个返回int类型的print函数,该函数输出"hello world!"。

  2. long base[0];:声明了一个长度为0的long数组。在C语言中,长度为0的数组是合法的,但它不能有活跃的元素。这里它被用作一个指针,而不是一个真正的数组。

  3. long* result = base + 3;:创建了一个指向long的指针result,它被初始化为指向base数组后面第三个long的位置。由于base是一个空数组,result实际上是一个未定义的指针(undefined pointer),但在这段代码中,它被用作一个通用指针。

  4. *(result + 1) = *result;:这一行没有做任何事情,因为base数组长度为0,所以resultresult + 1都指向无效的内存区域。不过,由于这是在初始化指针,某些编译器可能允许这样的操作而不会产生错误。

  5. *result = (long)print;:这里将print函数的地址强制转换为long类型,然后赋值给result指向的内存位置。这意味着result现在是一个指向print函数的函数指针。

  6. 由于在64位系统上,指针是64位的,long也是64位的,所以result指针可以安全地存储函数的地址。

main函数的末尾,虽然没有直接调用print函数,但是通过将print的地址赋给result指针,然后解引用这个指针(即*result),实际上隐式地调用了print函数。这就像直接调用print()一样,但是通过指针完成。

因此,当执行*result时,它调用了存储在result指针中的地址对应的函数,也就是print函数,输出了"hello world!"。

需要注意的是,这种使用未定义指针和强制类型转换的方式在实际编程中是不推荐的做法,因为它可能导致未定义行为,而且难以阅读和维护。在现代C编程实践中,应该使用void (*)()int (*)(void)等更明确的类型来声明函数指针。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1625193.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt : 在QTreeWidget中添加自定义右键菜单

一、引言 如图&#xff0c;我们需要在一个QTreeWidget 控件中添加了自定义右键菜单。 二、思路 如何做到的呢&#xff0c;很简单。浅浅记录和分享一下。 继承QTreeWidget&#xff0c;定义一个子类CustomTreeWidget &#xff0c;在重写contextMenuEvent 事件即可。 三、代…

数据结构初阶——树和二叉树

数据结构初阶——树和二叉树 1. 树的概念和结构1.1 树的概念1.2 树的表示 2. 二叉树2.1 二叉树的概念和结构2.2 二叉树的存储结构2.2.1 顺序存储2.2.2 链式存储 3. 二叉树的顺序结构及实现——堆3.1 堆的概念和结构3.2 堆的实现3.2.1 堆的定义3.2.2 堆的向上调整3.2.3 堆的向下…

Pycharm/Dataspell中使用jupyter导入ros humble包

配置ros humble对应python包路径文件 首先在~/.local/lib/python3.10/site-packages目录下新建一个.pth文件&#xff0c;如下图所示。 将对应的ros humble的python包的路径配置在上述文件中&#xff0c;一行放置一个路径&#xff0c;对应的路径如下图所示。 完成上述操作后…

ESP32 IDF环境 连接WIFI

新建wifi.h #ifndef __WIFI_H_ #define __WIFI_H_#include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_wifi.h&…

IntelliJ IDEA - 10 款 IDEA 宝贝插件,YYDS!

好久没发这种实用贴了&#xff0c;最近用到了一些能提升工作效率的IDEA插件&#xff0c;给小伙伴们分享一下。相信我&#xff0c;我分享的这些插件&#xff0c;都是实实在在能解决实际开发场景中痛处的。 1、POJO to JSON 开发工作中&#xff0c;常常在设计完API后&#xff0c…

recat组件使用Antd

安装和初始化 项目中进行安装yarn add antd或者npm i antd安装 使用 在App.js文件中引入按钮并使用 需要引入自己的css文件 模块化 import React, { Component } from react import { Button } from antd; //引入按钮 import antd/dist/antd.css; //还需要引入css样式 …

javascript使用setTimeout函数来实现仅执行最后一次操作

在JavaScript中&#xff0c;setTimeout函数用于在指定的毫秒数后执行一个函数或计算表达式。它的主要用途是允许开发者延迟执行某些代码&#xff0c;而不是立即执行。 当我们想要确保仅最后一次更新UI时&#xff0c;我们可以使用setTimeout来合并多次连续的更新请求。具体做法…

Ansible自动化

Ansible自动化 自动化的需求&#xff1a; 1. 在什么样的场景下需要自动化&#xff1f; 批量化的工作&#xff1a; 装软件包、配置服务、升级、下发文件… 2. 为什么在自动化工具中选择ansible&#xff1f; 对比shell脚本&#xff1a; 相对于用shell的脚本来实现自动化&#x…

vue+element 树形结构 改成懒加载模式(原理element有),这里只做个人理解笔记

1 找到属性标签添加 lazy 和 :load"loadNode" 这两个属性 2 引入树形接口,并和后端约定好传值,(拿我的举例 第一次获取全部父级默认第一次传参数:{ parentId : 0},可获取全部父级 第二次通过点击的子级把子级id传进去,这一步就用到了:load"loadNode&quo…

场内股票期权和场外期权(附场内期权交易攻略)

期权是交易双方关于未来买卖权利达成的合约。 就股票期权来说&#xff0c;期权的买方(权利方)通过向卖方(义务方)支付一定的费用(即期权费或权利金)&#xff0c;获得一种权利&#xff0c;即有权在约定的时间以约定的价格向期权卖方买入或卖出约定数量的标的股票或ETF。 买方(…

阶跃星辰:探索智能科技的星辰大海

引言 在当今快速发展的科技时代&#xff0c;人工智能已经成为推动社会进步的重要力量。阶跃星辰&#xff0c;正是在这一背景下诞生的。 阶跃星辰是一家专注于通用人工智能探索的公司&#xff0c;成立于2023年4月。该公司的创始团队由一群对人工智能充满热情和渴望的人组成&am…

【软考高项】二十五、整合管理7个过程

一、制定项目章程 1、定义、作用 定义&#xff1a;编写一份正式批准项目并授权项目经理在项目活动中使用组织资源的文件的过程作用&#xff1a;①明确项目与组织战略目标之间的直接联系;②确立项目的正式地位;③展示组织对项目的承诺 2、输入 立项管理文件 商业论证、成…

深圳证券交易所Binary行情数据接口规范

对接深圳证券交易所Binary行情数据接口其实并不难&#xff0c;你需要具备以下知识。 1、需要了解Binary报文设计结构&#xff0c;消息头消息体消息尾。 消息体&#xff1a; 如果是纯map结构的比较简单&#xff0c;字段平铺开来即可。如{"id":"1","…

第一届长城杯半决赛wp和AWD笔记

目录 AWD 渗透 cfs 单节点1 AWD笔记 AWD工具 文件比较工具 Web漏洞扫描工具 waf工具 代码审计工具 批量网站备份文件泄露扫描工具 cms通杀漏洞的利用 通杀脚本和批量提交flag脚本 防御流程 攻击流程 注意 AWD 解题思路] 首先就是fscan快速扫描对应C段&#xf…

如何让Ubuntu上的MySQL开发更便捷

前言 作为一款开源的数据库开发与数据库管理协同工具&#xff0c;&#xff08;OceanBase Developer Center&#xff0c;简称ODC&#xff09;&#xff0c;针对MySQL数据源&#xff0c;已提供了涵盖SQL开发、变更风险管控、数据安全合规等多个方面的功能&#xff0c;从而为MySQL…

spring cache(二)集成

一、集成方法 1、pom添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--spring cache--><dependency><groupId>org.spring…

盲人乘坐公共交通的新篇章:科技赋能,独立出行不再是梦

在我们日常生活中&#xff0c;盲人乘坐公共交通这一话题一直牵动着社会大众的心。近日&#xff0c;一位盲人朋友成功地独自完成了一次公交之旅&#xff0c;他的顺利出行背后&#xff0c;离不开一款名叫蝙蝠避障具有实时避障与拍照识别功能的辅助应用的鼎力支持。 清晨的…

DC-DC电源芯片规格书上的各种参数详解

1.输出电压精确度 输出电压的精确度,也被称为设定点精度,它描述了输出电压的允许误差。该参数通常是在常温,满载和额定输入电压的条件下测得的,它是这样定义的: 输出电压之所以产生误差,是因为元器件本身存在误差,特别是输出端的分压电阻,它将输出电压降低后比PWM比较…

C语言学习/复习34--内存相关笔试题/C与C++的内存/柔性数组

一、动态内存管理笔试题 1.值传递 注意事项1&#xff1a;指针的地址传递需要将指针的地址用二级指针接收&#xff0c;本题属于值传递不属于地址传递 修改&#xff1a;1改为地址传递&#xff0c;2释放malloc()函数申请的空间 注意事项2&#xff1a;printf()字符串时&#xff0c;…

【Pytorch】(十四)C++ 加载TorchScript 模型

文章目录 &#xff08;十四&#xff09;C 加载TorchScript 模型Step 1: 将PyTorch模型转换为TorchScriptStep 2: 将TorchScript序列化为文件Step 3: C程序中加载TorchScript模型Step 4: C程序中运行TorchScript模型 【Pytorch】&#xff08;十三&#xff09;PyTorch模型部署: T…