网页资讯视频图片知道文库贴吧地图采购
进入贴吧全吧搜索

 
 
 
日一二三四五六
       
       
       
       
       
       

签到排名:今日本吧第个签到,

本吧因你更精彩,明天继续来努力!

本吧签到人数:0

一键签到
成为超级会员,使用一键签到
一键签到
本月漏签0次!
0
成为超级会员,赠送8张补签卡
如何使用?
点击日历上漏签日期,即可进行补签。
连续签到:天  累计签到:天
0
超级会员单次开通12个月以上,赠送连续签到卡3张
使用连续签到卡
10月18日漏签0天
c语言吧 关注:800,375贴子:4,367,151
  • 看贴

  • 图片

  • 吧主推荐

  • 视频

  • 游戏

  • 11回复贴,共1页
<<返回c语言吧
>0< 加载中...

遇到诡异的bug?不知时编译器的问题还是代码有UB;

  • 只看楼主
  • 收藏

  • 回复
  • 然则我拒绝
  • 毛蛋
    1
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
注意:代码使用了gcc的__uint128_t
目前发先,在使用gcc 11, 12, 13时,以下代码在-O2和-O3下结果"错误"
不开优化则没有问题,gcc15以及gcc10及以前似乎也没有问题
mingw的情况大致相同


正确的情况应该是打印
route 2
03
开启O2或O3后会打印
route 1
01


  • 然则我拒绝
  • 毛蛋
    1
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
大致解释一下代码
首先定义了一个320比特大整数类型BN_320bits,用40个bytes表示
数据的表示方式是以uint64为单位大端表示, 而uint64内部小端表示
bn_sub_320做了一个大整数减法 r = r - a
为了方便借位的判断,运算时提升至__uint128_t
main函数,定义了m, u, v
其中m = 1, u = 0, v = 1
逻辑上
u = u - v
然后判断u的奇偶
u是偶数则进入route 1,只打印
u是奇数则 u = u - m
最后 v = v - u
然后打印v(为了方便我只打印了数据最低字节,其它字节都是0)
正常情况下
u = u - v 后 u = -1
然后判定奇数
u = u - m 后 u = -2
最后 v = v - u,得 v = 3
但是在开启-O3后,编译器似乎认为它确定了u - v的结果?,直接优化掉了u的奇偶判断,优化掉了变量m,优化掉了对u, v的初始化,直接调用bn_sub_320, 然后直接打印route1,再调用一次bn_sub_320, 最后直接打印1
对,是直接打印1,和v[32]无关了,直接mov立即数$1到第二参数寄存器,然后call printf的
如果把main函数的内容挪到一个f函数里,然后在main里调用f,那似乎会把u, v, m以及bn_sub_320的调用都优化掉,f()函数里就剩两个printf的打印


2025-10-18 21:47:30
广告
不感兴趣
开通SVIP免广告
  • 然则我拒绝
  • 毛蛋
    1
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
代码文字版(方便复制)
#include <stdint.h>
#include <stdio.h>
#include <string.h>
typedef struct {
unsigned char data[40];
} BN_320bits;
void bn_320_sub(BN_320bits* r, BN_320bits* a) {
__uint128_t diff = 0, borrow = 0;
uint64_t *r64 = (uint64_t*)(r->data);
uint64_t *a64 = (uint64_t*)(a->data);
diff = (__uint128_t)(r64[4]) - (__uint128_t)(a64[4]);
borrow = (diff >> 127) & 1;
r64[4] = (uint64_t)diff;
diff = (__uint128_t)(r64[3]) - (__uint128_t)(a64[3]) - borrow;
borrow = (diff >> 127) & 1;
r64[3] = (uint64_t)diff;
diff = (__uint128_t)(r64[2]) - (__uint128_t)(a64[2]) - borrow;
borrow = (diff >> 127) & 1;
r64[2] = (uint64_t)diff;
diff = (__uint128_t)(r64[1]) - (__uint128_t)(a64[1]) - borrow;
borrow = (diff >> 127) & 1;
r64[1] = (uint64_t)diff;
diff = (__uint128_t)(r64[0]) - (__uint128_t)(a64[0]) - borrow;
r64[0] = (uint64_t)diff;
}
int main(void) {
BN_320bits m, u, v;
memset(&m, 0, 40);
m.data[32] = 1;
memset(&u, 0, 40);
memset(&v, 0, 40);
v.data[32] = 1;
bn_320_sub(&u, &v);
if ((u.data[32] % 2) == 0) {
printf("route 1\n");
}else{
printf("route 2\n");
bn_320_sub(&u, &m);
}
bn_320_sub(&v, &u);
printf("%02x\n", v.data[32]);
}


  • GTA小鸡
  • 吧主
    14
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
第11,12行是UB
把结构体定义改成
typedef union {
unsigned char data[40];
uint64_t u64[5];
} BN_320bits;
然后改用u64成员访问


  • XeO2
  • 小吧主
    14
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
你的代码违反严格别名规则,第13、17、21、25、29行对r64和a64的解引用都是未定义行为。
如果把BN_320bits的定义改成这样
typedef union {
uint64_t u64[5];
umsigned char data[40];
}BN_320bits;
然后第11行改为
uint64_t *r64=r->u64;
第12行同理,即可在GCC11、12、13上得到和GCC15一致的结果


  • 然则我拒绝
  • 毛蛋
    1
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
草,顺便说一个恐怖的事情
在今天的早晨,在我发这个贴子之前,我的知乎推送了一个问题是
"为什么C/C++要有UB"
然后我随手点开,直接跳转的回答的评论区里正在讨论Strict-aliasing
但是,我没有细看


  • 草酱
  • 彩虹面包
    13
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
我觉得11行和12行是UB,uint64指针通常要求是8字节对齐的,但是结构体的对齐系数是1字节,两者并不匹配。
可以使用union解决这个问题。
typedef union{
uint8_t data[40];
uint64_t v64[5];
}xxxxxxx;


登录百度账号

扫二维码下载贴吧客户端

下载贴吧APP
看高清直播、视频!
  • 贴吧页面意见反馈
  • 违规贴吧举报反馈通道
  • 贴吧违规信息处理公示
  • 11回复贴,共1页
<<返回c语言吧
分享到:
©2025 Baidu贴吧协议|隐私政策|吧主制度|意见反馈|网络谣言警示