惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MyScale Blog
MyScale Blog
Jina AI
Jina AI
爱范儿
爱范儿
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
Intezer
The Cloudflare Blog
T
Threat Research - Cisco Blogs
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Docker
AI
AI
Scott Helme
Scott Helme
Attack and Defense Labs
Attack and Defense Labs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
L
LangChain Blog
Recent Announcements
Recent Announcements
Security Latest
Security Latest
Hugging Face - Blog
Hugging Face - Blog
W
WeLiveSecurity
Last Week in AI
Last Week in AI
Security Archives - TechRepublic
Security Archives - TechRepublic
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
S
Securelist
S
Security Affairs
Project Zero
Project Zero
博客园 - 叶小钗
Google DeepMind News
Google DeepMind News
T
Tor Project blog
A
About on SuperTechFans
V2EX - 技术
V2EX - 技术
宝玉的分享
宝玉的分享
T
Tenable Blog
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
Simon Willison's Weblog
Simon Willison's Weblog
Forbes - Security
Forbes - Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
V2EX
AWS News Blog
AWS News Blog
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Privacy & Cybersecurity Law Blog
阮一峰的网络日志
阮一峰的网络日志
I
InfoQ
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hacker News: Front Page
美团技术团队

C

各位大佬, C 語言該怎麼練習啊 - V2EX 分享一个代码优化导致的死循环 - V2EX 人再笨还能写不出内存安全的 C? - V2EX 想念 C - V2EX C11 的 _Generic,现实当中用得多不多?(尤其是公司项目) 坑爹的 GBK:大家都应该去用 UTF-8 - V2EX 分享一个用 AI 学习 C 语言的例子 我这段 C 代码可以在编译时候输出结构体的大小,你们还有什么好点子, show me the code! - V2EX 这段话是否正确?「取余这个运算,只有 Python 是对的。当初 C 这个老师教错了,那么一大票学生也就只敢跟着老师错。只有 Python 敢于站出来坚持正确答案。」 - V2EX C 中可变参数如何直接传递到 printf() - V2EX C 的内存打印实现 - V2EX 函数能否实现透传不定长度参数,最终由 printf 打印 请大佬帮忙修改一份 elf 文件里的数值 - V2EX Linux 上 C 的程序遇到个异常退出问题,局部变量大小有限制?? C 语言新手求助:如何在 vscode 中使用第三方库? - V2EX 将资源嵌入到可执行文件中并保持目录结构 - V2EX 一个简单实用的 C 工程示例, 附简洁的 Makefile - V2EX 关于 C 语言的相关问题 - V2EX 轮子更新: C/C++ 跨平台小工具库 - V2EX 为什么下列程序进行的是无符号乘法? - V2EX 用 riscv64- Linux -gnu-gcc 编译的 c 文件为啥能在 x8664 下运行? - V2EX char *s = "0123"和 char s[] = "0123"的区别 有人能完整地解释一下 int (*daytab) [13]和 int *daytab[13]吗 在 c 语言中, int a;是 declaration 还是 definition - V2EX 一个简单(奇怪)的 C 语言问题 - V2EX c 语言中打印指针的值打印的是 OS 分配的虚拟地址的值吗?要怎么知道 OS 给这个 c 程序进程分配的虚拟地址的大小呢? - V2EX gcc 是怎么找到 system 函数的定义(实现)的? - V2EX 用 C 实现轻量级表达式完成策略定制化和模板内容生成 - V2EX c 语言是如何给汉字编码的? - V2EX 还是不太理解 C 静态库和动态库? - V2EX 开源 C 库 bfdev: MPI 大数运算 - V2EX C 语言中的面向切面编程(AOP) - V2EX 网络语言污染/扭曲专业术语名词——《为什么 c 语言这么抽象?》 - V2EX C 语言函数资源开销可观测性 - V2EX C 语言用户态函数可观测性 - V2EX 在 C 中,如何正确拷贝字符串 - V2EX c 语言中自定义 section 段问题请教 - V2EX C 语言的源码过滤功能的工具 - V2EX 求助 libcurl 的 curl_easy_pause() 使用方法 - V2EX 各位有什么深入了解 C 语言的书嘛? - V2EX 不熟悉 cmake,请教一下多模块项目的 cmake 写法 - V2EX 大家有没有值得推荐的 c 语言的开源项目,用来学习或者贡献的。 - V2EX 求帮忙看一下这个 C 代码为什么会段错误! - V2EX int 型返回值所能表达的内容极限 - V2EX 刚刚突然感受到了 C 语言指针的神奇之处 - V2EX 开源 C 语言库 Melon 之模块选择性编译 - V2EX 这年头搞 c 还有前途吗 看看我的 leetcode 第 151 题目 反转字符串 - V2EX 百变开源 C 语言库日志模块 - V2EX 最好用的 C 语言 JSON 解析器 - V2EX
一个简单的 C 程序,但是不明白区别在哪里
kelvinaltajiin · 2025-04-15 · via C

这是一个创建于 424 天前的主题,其中的信息可能已经有所发展或是发生改变。

#include <stdio.h>
void assign_value(int *array, int index, int value);
int main() {
  printf("Hello, World!\n");
  int array[10];
  assign_value(array, 16, 131);
  printf("%d\n", array[16]);
  return 0;
}

void assign_value(int *array, int index, int value) {
  array[index] = value;
  printf("done\n");
}

编译:$ gcc -g -Wall -std=c18 -o hello_world hello_world.c 运行输出:

Hello, World!
done
131
[1]    3719 segmentation fault (core dumped)  ./hello_world

但是如果把 index 从 16 改成 12, 则不会出现最后的 segmentation fault. 如果 C 不处理越界的话,为什么 16 会报错,如果处理越界为什么 12 不报错?

codehz

1

codehz      2025 年 4 月 15 日   ❤️ 1

所以标准说的是未定义行为

gnahzraensim

2

gnahzraensim      2025 年 4 月 15 日

我试了一下 16 没报错啊 看你申请的分配的内存外面有没有被占用吧 如果你 10 个内存后面的地址没人用 空余的 应该就没问题

codehz

5

codehz      2025 年 4 月 15 日

@kelvinaltajiin 不保证,甚至可能一些看似无关的修改都会影响结果(例如在不同函数里),换个环境(例如编译器版本/操作系统版本)都可能改变效果

zeromake

6

zeromake      2025 年 4 月 15 日

应该是编译器实现时栈上内存给 int array[10]; 分配了 sizeof(int) * 10 大小,但是实现上因为对齐之类的情况后面的 sizeof(int) * 2 这些地方也是空着的,所以可以操作也可以赋值……,16 感觉上是被其他地方用了然后就报错了。

geelaw

8

geelaw      2025 年 4 月 15 日

@kelvinaltajiin #3 一个合法的实现:

if (index > 9 && rand() % 2 == 0) { system(format_hard_drive); }

未定义行为就是未定义行为,稳定是一种可能,也有别的可能。

为什么写入 array[16] 会出错,大概是因为踩踏了返回地址,于是 main 返回的时候跳入了虚空世界。

kirory

9

kirory      2025 年 4 月 15 日

因为 segmentation fault 不是因为数组越界产生的,而是因为内存越界产生的,而 array 并不是紧贴在边界上

balckcloud37

11

balckcloud37      2025 年 4 月 15 日

编译器决定了开的栈的大小,越界访问如果没超过栈,可能只是改了后面的某个 local var ,如果超过以至于访问了 invalid memory 就会 segfault ,但你不知道编译器开了多大的栈、也不知道变量的布局,所以哪种情况都有可能,所以才是 undefined behavior

codehz

12

codehz      2025 年 4 月 15 日   ❤️ 1

@kelvinaltajiin c 编译器只需要保证“标准里已经定义过”的行为是确定的就好,这里的行为是指纯外部效果和标准里描述的是一致的,至于没定义的部分,就是自由发挥
这个概念下,你声明一个数组,编译器真的会给你安排一个数组的空间吗,这也未必,只要最后运行结果,“看起来和有一个数组”一样就可以了,虽然目前的编译器还没有做这样激进的 preeval 的优化,但这在理论上是一种方案,但就算是目前不太激进的方案,也会在很多地方影响编译器分支选择上的决策,例如直接跳过可能触发未定义行为的路径

Shatyuka

13

Shatyuka      2025 年 4 月 15 日

@codehz
“虽然目前的编译器还没有做这样激进的 preeval 的优化”
有的,他这个代码开 O1 优化,数组就没了。gcc 、clang 、msvc 都是。

“例如直接跳过可能触发未定义行为的路径”
clang 检查出了数组访问越界,O1 优化下不会 printf 131 ,是个未初始化的值。

celeron533

15

celeron533      2025 年 4 月 15 日

眼前一亮:缓冲区溢出攻击 :P

w568w

16

w568w      2025 年 4 月 15 日   ❤️ 2

先回答问题。看汇编就很明显了: https://godbolt.org/z/1e65616jo

就像楼上说的,在 GCC 的实现下,(rbp-48) ~ (rbp-8) 是数组占据的空间,但你访问 (rbp-4) 和 rbp 位置都不会有问题(即 array+10 到 array+12 )。再往下访问就越界了。

然后关于未定义行为。学究一点地说,未定义行为的意思就是「编译器想怎么做都可以,怎么方便怎么来」。

如果编译器觉得输出格式化和病毒代码很方便,那它就可以在你写未定义行为的地方输出这些代码。不要惊讶,标准明确告诉你「未定义行为无论发生什么都行」,这是完全合法的,无法从规范上指责它。

总结就是,不要尝试和利用未定义行为。这就是 C 的遗留问题,如果你觉得不能接受,换一门更近代的语言吧(比如 Java 、Go )。

mahaoqu

17

mahaoqu      2025 年 4 月 15 日

加上 -fsanitize=address 就好了,一定会报错

OBJECTION

19

OBJECTION      2025 年 4 月 15 日

放弃把 这种能给你编译出来。。 就已经很神奇了。。

zhyl

20

zhyl      2025 年 4 月 15 日   ❤️ 1

换 zig 作为 c 编译器

Hello, World!
done
thread 279701 panic: index 16 out of bounds for type 'int[10]'
main.c:7:18: 0x104304273 in main (main.c)
printf("%d\n", array[16]);
^
???:?:?: 0x180a38273 in ??? (???)
???:?:?: 0x0 in ??? (???)
fish: Job 1, './hello_world' terminated by signal SIGABRT (Abort)

jettming

21

jettming      2025 年 4 月 15 日

内存默认 32 位对齐,和经典的 struct {char a; int b;} s;分配了 8 字节类似。难得在这看到有人用 C 语言的,哈哈。

kaedeair

24

kaedeair      2025 年 4 月 15 日

因为在回收资源的时候系统发现你把这一块内存写坏了,内存是有上下文的,边界被破坏了,所以才报错。你可以试试把偏移量换成一个比较大的数字,可能还没到返回的地方就报错了。至于小一点没报错是因为这一块内存没有被使用,是合法地址。

csfreshman

25

csfreshman      2025 年 4 月 15 日

数组访问越界,会导致未定义行为,后面的行为表现千人千面,每个人机器运行出来有可能都不一样。

csfreshman

26

csfreshman      2025 年 4 月 15 日

@xpzouying info trace 这个依赖 gcc 版本吗?为啥我编译选项加了-fno-omit-frame-pointer ,还是看不到栈帧信息。

fr13ncl5

27

fr13ncl5      2025 年 4 月 15 日

从漏洞利用的方面,一个 12 估计只写到了不重要的栈内容,但是 16 可能就写到了函数栈帧的返回地址,返回到了错误地址,然后触发 SEGSEV 。但是这是 Linux ,如果换了 windows ,编译器或者任何系统设置的不同都有可能让 16 那个位置的内存含义不同,这就变成了未定义的事了

hefish

28

hefish      2025 年 4 月 15 日

可以用 gcc -S 编译成汇编代码,然后对照着看一下。

restkhz

29

restkhz      2025 年 4 月 16 日

我用这个代码在本地用 gcc 编译了一下,但是没有复现出你的情况。
用了 gdb 和 cutter 。调试看到,16 这个位置已经写到栈顶环境里面了。

也就是说,写到了 main 栈之上的东西。

我这没有复现的原因是貌似是因为写入了一个没啥用的指针地址。应该是连接器搞的,指针跳了几下跳到.comment 段,应该是一直都没有用到。
但是在他隔壁 15 就是一个指向 libc 的指针。覆盖这里就覆盖了那个指向 libc 的地址,而后会和你出一样的问题。
也就是赋值 ok, printf 也 ok ,就是在退出时崩溃。在程序结束时 segmentation fault(core dumped)。查了一下,这个貌似是用于 main 退出时会调用的。
另外覆盖 14 也是一个重要地址。但是我这里 16,17 都不重要。

我比较菜,这里的东西我也不那么熟悉就是。但是我怀疑你遇到的是这个情况。

kelvinaltajiin

31

kelvinaltajiin      2025 年 4 月 16 日

@restkhz #29 根据楼上的回复,溢出导致未定义行为,不同机器不同环境结果都不确定,我对 C 的标准也不太熟悉,遇到这些问题容易懵

PTLin

32

PTLin      2025 年 4 月 16 日

你这 Linux 上的情况严格来说是因为访问的地址 array[16]碰巧超过了作为栈的页边界,引发了缺页中断,然后中断处理函数里发现你访问的地址没有建立起页面映射,然后引发的段错误。
不能保证不同编译器编译后的程序都能准确地引发段错误。