fsantinize 还没智能到那种程度 简直给码农添加负担 给一个静态字符数组前面添加几个空格都会报错
请看
图 1
图 2
图 3
编译时报图 3 的错误 运行就报 ==1856626==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff15ef59e9 at pc 0x55d429175340 bp 0x7fff15ef5940 sp 0x7fff15ef5938
1
eagleoflqj 2023-09-16 10:41:08 +08:00 via iPhone
你是真不懂还是在钓鱼?
|
2
zzzkkk OP @eagleoflqj
不懂 没做过 c c++项目 只会写小代码 |
3
zzzkkk OP 编译报错是合理的
运行时报错不合理啊 |
4
XiLingHost 2023-09-16 10:43:44 +08:00
你数数这数组多长了
|
5
zzzkkk OP |
6
nightwitch 2023-09-16 10:46:35 +08:00
笑拉了
|
7
XiLingHost 2023-09-16 10:46:56 +08:00
@zzzkkk 我的意思是,你看看你的初始化字符串的长度和为这个 char[]分配的长度,你的代码里为它分配了 25 个 char ,去掉末尾的\0 ,你的字符串长度应该是 24 个 ascii 字符
|
8
des 2023-09-16 10:47:01 +08:00 via iPhone
@eagleoflqj 估计是来炸鱼的
|
9
XiLingHost 2023-09-16 10:48:21 +08:00
你实在搞不明白,你就直接 char s[INT_MAX]吧
|
10
maplezzz 2023-09-16 10:54:31 +08:00 1
哈哈哈,给我逗笑了
|
12
ershierdu 2023-09-16 11:15:34 +08:00
1. 为什么编译时 warning 是合理的,而运行时报错就不合理呢?
2. 从内存的层面上看,OP 期望运行时它是怎么样的呢? |
13
yolee599 2023-09-16 11:30:41 +08:00 via Android
搁这钓鱼呢?笑死我了,报错那么明显
|
14
zzzkkk OP |
15
jonty 2023-09-16 12:00:09 +08:00
。。。。。。
|
16
zzzkkk OP 我既然能写出这种 while 循环 数组下标还是知道的 只是被 fsantinize 冲昏了
|
17
zzzkkk OP 而且 leetcode 的函数设计有问题 一个函数传只一个指针 还开 fsantinize
|
18
weidaizi 2023-09-16 13:10:05 +08:00 3
看完标题:OP 搁着炸鱼?
看完下面回复:难道这 OP 真的不懂? 看了一下 OP 的其他帖子:哦,原来是真小白啊 |
19
GeruzoniAnsasu 2023-09-16 13:34:44 +08:00 2
我此时很想看到那个秒杀 nginx 的哥们来点拨一下 OP
|
20
LykorisR 2023-09-16 13:38:31 +08:00
难绷,建议去问 gpt ,这种事浪费大家时间不值得
|
21
Kumo31 2023-09-16 13:39:45 +08:00
建议还是放弃使用 c/cpp 吧
|
22
leaflxh 2023-09-16 13:57:03 +08:00
能留个微信吗,用 sha256 加密一下发出来
|
23
liuguangxuan 2023-09-16 14:08:07 +08:00 via Android
@leaflxh 哈哈,这个梗我居然看懂了
|
25
cnbatch 2023-09-16 16:23:56 +08:00
人眼数很容易数错的,对于比较长的字符串,最好还是写成
char s[] = "这是字符串" 中括号内不写任何数字 如果想知道 s 数组有多长,那就 sizeof(s) 以 14 楼的图为例,把 s[31]的数字去掉,改成 s[],接着下面一行加一句 printf("sizeof s: %d\n", (int)sizeof(s)); 就可以准确知道实际大小 |
26
zzzkkk OP @cnbatch
是把字符串连双引号拷贝进浏览器 console 用.length 算出来的 是 30 再加 1 等于 31 刚才把下标 31 去掉了 用 sizeof 打印出来是 31 但编译加上 fsanitize 还是报错 这贴讨论的是 fsanitize 的弱智 但它还想做全面的事情 限制了码农的能力 增加了繁琐 你们着眼的细节是我在增加了很多空格后 无非是忘了改下标 你们自己去加上 fsanitize 编译看看 |
27
cy18 2023-09-16 17:22:11 +08:00
@zzzkkk #26 "无非”是忘了改下标...
fsantinize 帮你找出了这么严重的问题,这时候你就应该默默在心中唱一首《感恩的心》。 |
28
zzzkkk OP @cy18
关键是没有下标 或刚好等于 或远远大于数组元素个数 fsanitize 都报错 这不是假阳性么?所以说它弱智 但还想干全能的事 |
29
geelaw 2023-09-16 17:32:24 +08:00
请问楼主期待这段代码发生什么?
int a[1]; a[0] = 1; a[1] = 2; 用超长字符串初始化不够长的字符数组的效果和上面差不多。另外楼主并不是忘了改下标,而是忘了改长度,这是两个不同的概念。 另外 char s[] = "..."; char s2[strlen(s) + 1]; 不是合法的 C++ 代码,没有拒绝编译已经很给面子了。 |
30
zzzkkk OP |
31
zzzkkk OP 这个是合法的 char s2[strlen(s)+1]
不合法的话 你用数组长度+1 的 size 用 malloc 返回指针都不合法 |
32
zzzkkk OP 我不知道越界的话
这种高质量 while 循环都写不出来 |
33
zzzkkk OP 一开始数组下标是正确的 我当时好像去掉了下标 后来又加上正确下标 我发现 fsanitize 报假阳性
为了截图 才改了代码 改了字符串 你们没有关注假阳性 反而关注细枝末节 |
34
geelaw 2023-09-16 17:52:54 +08:00
@zzzkkk #30 所以你对 char s[25] = "长度为 30 的字符串长度为 30 的字符串"; 出错有什么异议呢?
另外 s 以空格开头的时候,在 while (start > s) { flag = 0; while (*start == ' ') start--; 这段代码里面会把 start 减到 s 之前,这也是未定义行为。 https://gist.github.com/GeeLaw/47dea7677eb48e1b9896bfe9d006b5f4 |
35
qbqbqbqb 2023-09-16 17:53:01 +08:00
你的 while 里面的
while(*start==' ') start--; 在字符串以空格开头的时候可能会越界,访问到 s[-1] |
36
cnbatch 2023-09-16 17:53:43 +08:00
简单写了一小段
#include <stdio.h> int main() { char str[30] = "https://www.v2ex.com/t/974343"; printf("sizeof str: %d\n", (int)sizeof(str)); printf("%s\n", str); } 无论 GCC 还是 Clang ,编译和运行都没报错 编译的命令行:cc -fsanitize=address GCC 版本(运行在 Debian 11 ):10.2.1 Clang 版本(运行在 FreeBSD 13.2 ):14.0.5 |
37
geelaw 2023-09-16 17:53:55 +08:00
@zzzkkk #31 C++ 不存在变长数组,所以 int n; scanf("%d", &n); int a[n]; 是完全错误的,即使 n 输入的是 1 也是错误的。
|
38
zzzkkk OP 减到 s 之前也没问题
下面 start2++ 刚好指向空格 下面的代码不会执行 |
41
qbqbqbqb 2023-09-16 17:59:18 +08:00
@zzzkkk 减到 s 之前是有问题的。
因为你这个循环至少还会再判断一次条件,必然会非法读取越界位置 s[-1]的值,这是不允许的。所以说 sanitizer 会报错。 而且你也不知道 s 前面有什么二进制数据,“下面 start2++ 刚好指向空格”,指针没有跑飞只是运气好。 |
42
zzzkkk OP |
44
qbqbqbqb 2023-09-16 18:00:40 +08:00
假如 s 数组前面二进制数据一堆 0x20 ,指针就跑飞了
|
45
cy18 2023-09-16 18:01:33 +08:00
@zzzkkk #28 聪明的你倒是把数组长度正确的,会报假阳性的代码贴上来啊...建议聪明的你别用截图,难道指望其他人对着你的图手打一遍代码么...
|
46
zzzkkk OP |
47
zzzkkk OP |
48
geelaw 2023-09-16 18:05:05 +08:00
@zzzkkk #38
int a[1]; a + 0; // OK a + 1; // OK a - 1; // 未定义行为 a + 2; // 未定义行为 和 (a-1)、(a+2) 有没有被解引用没有任何关系。见 C++ 标准 [expr.add]/4: When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P. (4.1) If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value. (4.2) Otherwise, if P points to an array element i of an array object x with n elements ([dcl.array]), the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) array element i+j of x if 0≤i+j≤n and the expression P - J points to the (possibly-hypothetical) array element i−j of x if 0≤i−j≤n. (4.3) Otherwise, the behavior is undefined. 注意 4.2 要求表达式的结果指向数组对象或者数组末尾之后,不允许数组开头之前。 |
49
zzzkkk OP @cy18
#include "stdio.h" #include "stdlib.h" #include "string.h" int main(int argc, char **argv) { char s[] = " a bc ed sdff"; printf("%d\n%d\n",sizeof(s),strlen(s)); char *start, *start2; start = s; while ( *start != '\0') { start++; } --start; char s2[strlen(s) + 1]; char *s2p = s2; char flag; while (start > s) { flag = 0; while ( *start == ' ') start--; while (start >= s && *start != ' ') start--; start2 = start; ++start2; while ( *start2 != ' ' && *start2 != '\0') { *s2p++ = *start2++; if (flag == 0) flag = 1; } if (flag == 1) { *s2p++ = ' '; } } *s2p = '\0'; start = s; s2p = s2; while ( *s2p != '\0') *start++ = *s2p++; *start = '\0'; printf("%s\n%d\n", s, (int) strlen(s)); } 编译: cc c.c -fsanitize=address -fsanitize-recover=address -fno-omit-frame-pointer -O0 -Wall |
52
misdake 2023-09-16 18:13:43 +08:00 via Android
字符串结尾不是空格的话,while(*start2 != ' ')没有判\0 会超出 s2 范围,一路写出范围
|
53
geelaw 2023-09-16 18:14:46 +08:00
@zzzkkk #47 接续 #50 ,假设数组前面一切可读取的虚拟内存都是 ' ',且假设指针加减运算未定义行为的实现就是访问这些虚拟内存,那么这个程序会因为把 start 减到不可读的虚拟内存地址之后继续读 start 而访问违规,从而崩溃。
|
54
zzzkkk OP |
57
zzzkkk OP 重新申明:
fsanitize 无论字符串多短 都报错了 早上具体什么情况 一会报错 一会不报错 想不起来了 |
58
flyqie 2023-09-16 18:31:20 +08:00 via Android
|
59
learningman 2023-09-16 18:38:37 +08:00
@GeruzoniAnsasu 别尬黑,人好歹是会写 epoll 的,只是不会配 nginx 而已
|
60
learningman 2023-09-16 18:39:57 +08:00 4
“别小看了这几个 while 循环 是老鸟才能写得出来的”
今日 V2EX 笑话 |
61
learningman 2023-09-16 18:41:40 +08:00
https://v2ex.com/t/974127#reply10 楼主不会写反转字符串,开了两个帖子一个骂 c 语言一个骂工具链
|
62
learningman 2023-09-16 18:45:51 +08:00
https://v2ex.com/t/853017#reply244 破案了,写 php 的
zzzkkk symfony form choicetype 怎么设置选中项 PHP • zzzkkk • 2021-12-31 18:36:59 PM 忽略 zzzkkk symfony 每次改动 config/services.yml 都加载很慢 PHP • zzzkkk • 2021-12-30 14:47:08 PM • 最后回复来自 zzzkkk 忽略 7 zzzkkk symfony form money type 为什么存到数据库 是价格乘以 100 ? PHP • zzzkkk • 2021-12-30 15:12:32 PM • 最后回复来自 zzzkkk 忽略 6 zzzkkk symfomy form add field 注解模式 property_path 无效 PHP • zzzkkk • 2021-12-25 20:30:33 PM 忽略 zzzkkk symfony 项目每次改动都要 clear cache? 这是把 V2EX 当 chatgpt 使呢? |
63
zzzkkk OP |
64
zzzkkk OP 是字符串里的单词顺序反转 不是简单字母反转
|
65
zzzkkk OP @learningman
你有本事写个更高效的 c 代码出来 |
66
zzzkkk OP @learningman
没用过的臃肿框架 不知道不是很正常么 |
67
vsyf 2023-09-16 19:01:11 +08:00
|
68
zzzkkk OP 各位 c c++老鸟 谢谢了 我的确没做过 c c++项目
但是我这个算法还是最好的吧 |
69
cnbatch 2023-09-16 19:10:25 +08:00
OP 把系统版本、编译器版本一起发出来吧
我用 GCC 10.2.1 (Debian 11)、GCC 13.2.1 (Fedora 38)和 Clang 14.0.5 (FreeBSD 13.2)都没遇到 OP 主贴图 3 的情况 |
70
zzzkkk OP |
71
Inn0Vat10n 2023-09-16 19:30:27 +08:00
你可以把会导致 sanitizer 报错的代码原样贴出来,大家来一起帮你数一数到底有多少个字符
https://godbolt.org/ |
72
ProPh3t 2023-09-16 19:35:11 +08:00 1
误以为进了民科吧
|
73
zzzkkk OP @Inn0Vat10n
哎呀 不知怎么回事 刚才好像是一条 printf 语句到了 sanitize 问题 现在改成这样 在 linux 上能运行了 但在 leetcode 上还不能运行 https://godbolt.org/z/M7Wq9hddP |
74
lixile 2023-09-16 20:48:26 +08:00
属实是看不下去了 sanitizers 系列工具 已经是误报率非常低的动态扫描方式了,楼主几乎在重复在类似的问题上犯错
不好好看文档 就上 所有动态扫描的编译选项不会用在正常的软件上开启 仅仅在需要的时候开启 另外如果想让扫描结果 尽可能准确 需要添加回溯和调试的编译选项 -fno-omit-frame-pointer -g 多看看官方文档吧 https://github.com/google/sanitizers/wiki/AddressSanitizer |
75
ajaxgoldfish 2023-09-16 21:07:24 +08:00
楼主可能的理论基础不够强,以为编译过就是合法的代码,其实这是编译器帮你做的优化,举个你刚才的例子在栈中开辟数组要指定大小,而且还的是字面量或者 const 量,但是在高版本 gcc 或者高版本 msvc 中编译器会在编译阶段计算出并给你填上。建议使用低版本的编译器来学习 C++
|
76
haohaolee 2023-09-16 22:08:57 +08:00 via Android
楼主你可以用 rust 去刷 leetcode ,性能不输 C++,而且不会随便就 runtime 报错
|
78
zzzkkk OP |
79
zzzkkk OP @cnbatch
你把这里的 reversWords 贴到 leetcode cn 好像第 141 题它还报错 但是在 linux 编译 已经可以运行了 https://godbolt.org/z/M7Wq9hddP |
80
qbqbqbqb 2023-09-16 23:40:54 +08:00
@zzzkkk 这不算最好的算法,太繁琐了。
最方便的写法是:全句字母反转 + 每个单词内部字母反转 = 单词顺序反转 全句字母反转,只要遍历一遍就可以 然后再对每个单词内部字母反转,只要正序抠单词,顺便去空格 比你倒着抠单词方便太多 |
81
qbqbqbqb 2023-09-17 00:00:15 +08:00
这“老鸟”看得我一愣一愣的
LeetCode 考察算法和数据结构,和编程语言关系不大,“c c++老鸟”也未必不翻车(暂且不讨论这 while 究竟是不是“高质量”) 而且 LC 上的题目在算法领域也是入门级的那一类,做了几道 LC 上的题目(甚至都没 AC )就能叫“老鸟”真让人摸不着头脑。任何一个正规的大学计算机系开设的数据结构与算法必修课教学的内容深度都比它高得多(当然指的是熟练掌握了课内知识,仅仅及格混过考试不算)。 做过 LC 就称“老鸟”真不够格(除非 Hard 题能随便秒,这种情况另说)。如果想见识真正的“老鸟”,建议看看洛谷、POJ 、Codeforces 这些网站。 |
83
11232as 2023-09-17 00:32:19 +08:00
“高质量 while”,那确实是大佬了
|
84
Inn0Vat10n 2023-09-17 01:06:20 +08:00
@zzzkkk 不熟悉 C 的话不建议直接用它刷题了,容易自找困扰,当输入是"A B"时,你上面代码第30行,34行都会写越界
|