restrict / __restrict / __restrict__ 关键字
最近在RHEL7上使用字符串拷贝函数wcscpy(),发现出来的结果不对,好像是dest的缓冲区被破坏了:
int main(){wchar_tbuf[256] = L"\n \n Total memory used\n Total buffer size\n \n Totalphysical memory used\n Total memory actually used.\n";wchar_tnlStr[5] = L"\n ";wchar_t *tmp =buf;int nlStrLen =wcslen(nlStr);while (tmp =wcsstr(tmp, nlStr)){ wcscpy(tmp + nlStrLen -1, tmp + nlStrLen);tmp ++;} printf("%ls",buf);return 0;}
这段代码的作用是将buf中的"\n"后的空格去掉,关键是对wcscpy()的调用。wcscpy()将src字串拷贝到dest。而本例中,src与dest出现了内存上的交叠。
一般来讲,src与dest的交叠方式分两种 (如下图):
1. dest的头与src的尾相交叠。
2. src的头与dest的尾相交叠,本例尾这种情况。
虽然manpage对wcscpy()的说明中强调了,src与dest不应该交叠,但分析wcscpy()的实现逻辑,发现对于交叠情况2,也应该正确工作,因为wcscpy()总是从src的头开始向dest的头拷贝数据,所以当拷贝指针移动到src的尾部并拷贝数据写入dest的尾部,也即src的头部时,并不会损害dest。
但是现在问题来了,最终输出的结果显示buf仍然被损坏了:
Toaal memory usedTotal buffer sizTtaal physiclmmeemory usedToaal memory actually used.
这其实问题是一个很出名的问题:即*cpy系列的函数不能正确处理src与dest内存交叠的情况。附一篇大牛们为该问题吵架的连接:
https://sourceware.org/bugzilla/show_bug.cgi?id=12518
回答也非常简单:
当src与dest内存有交叠时,改用*move系列函数: memmove / wmemmove。
只是,我感兴趣为什么这个问题会出现?
如果把wcscpy()的实现拷贝来放到本地,名字改成wcscpy_local 并调用之,发现工作正常了,附上wcscpy_local()的实现:
char_t* wcscpy_local (wchar_t * dest, const wchar_t * src){wint_t c;wchar_t*wcp;if(__alignof__ (wchar_t) >= sizeof (wchar_t)){constptrdiff_t off = dest - src - 1;wcp = (wchar_t *) src;do{c =*wcp++;wcp[off] = c;}while(c != L'\0');}else{wcp =dest;do{c = *src++;*wcp++ = c;}while (c != L'\0');}return dest;}
那么,为什么调用系统提供的wcscpy就会出问题呢?
我查到的理解是:系统定义的wcscpy原型中,参数多了一个限制符“__restrict”:
File: /usr/include/wchar.h
extern wchar_t *wcscpy (wchar_t *__restrict __dest, constwchar_t *__restrict __src) __THROW;
关于restrict
在C99中,restrict是一个关键字,而C++中却没有该关键字,但当代编译器也在C++实现了差不多的功能,用的关键字是__restrict,或__restrict__。(为了简便,以下只称restrict)
restrict 用来定义指针变量,表明该变量没有别名,意思就是:除了该变量以外,没有别的方法可以访问其指向的地址空间。
举个有别名的例子:
int *pA, *pB;pB = pA; //那么pB就是pA的别名,通过pB照样可以访问pA指向的内容
编译器如果知道一个变量没有别名,那么,它就会放心大胆的作优化,比如这段代码:
void update(int *ptrA, int * ptrB, int * val)
{*ptrA +=*val;*ptrB +=*val;
}
编译出来的ASM类似于:
mov eax val;
add ptrA eax;
mov eax val;
add ptrB eax;
其中,变量val加载了两次。因为它不知道ptrA,ptrB是否与val有内存上的交叠,所以为了保险起见,只能每次用到val时都从内存里读。
如果加上__restrict:
void update_restrict(int * __restrict ptrA, int * __restrictptrB, int * __restrict val);
则汇编变成(需要打开编译优化选项,底下有讲到):
mov eax val;
add ptrA eax;
add ptrB eax;
可看到,val只加载了一次,因为编译器知道val没有别名,不会被别人改变,所以可以重复使用寄存器里的值。
猜想wcscpy的错误原因
那么,我就在想,因为wcscpy的参数加了__restrict,这就要求src与dest不能出现内存交叠,而事实上,我们传进的参数没有保证这点,导致了优化后的结果不正确。
不过我并没能重现个问题,即便对自己版本的wcscpy_local()参数加上__restrict,并打开编译优化选项,仍然没有重现问题。
抛砖引玉,希望有人解开我的疑惑~
restrict/__restrict/__restrict__的编译选项:
为了观察__restrict的汇编结果,需要打开优化选项,以下是GCC / G++的选项:
对C:
-O3 -std=c99
对C++,-std=c99已经默认开启,因此只用:
-O3
参考:
(1) WiKi: http://en.wikipedia.org/wiki/Restrict
(2) Demystifying The Restrict Keyword:
http://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html
restrict / __restrict / __restrict__ 关键字相关推荐
- Understanding C/C++ Strict Aliasing
Understanding C/C++ Strict Aliasing 深入理解C/C++中的`Strict Aliasin`规则 or - Why won't the #$@##@^% compil ...
- linux vim配置c,Linux入门学习教程:GNU C及将Vim打造成C/C++的半自动化IDE
C语言在Linux系统中的重要性自然是无与伦比.不可替代,所以我写Linux江湖系列不可能不提C语言.C语言是我的启蒙语言,感谢C语言带领我进入了程序世界.虽然现在不靠它吃饭,但是仍免不了经常和它打交 ...
- linux编译c 自动化,Linux江湖06:感悟GNU C以及将Vim打造成C/C++的半自动化IDE
C语言在Linux系统中的重要性自然是无与伦比.不可替代,所以我写Linux江湖系列不可能不提C语言.C语言是我的启蒙语言,感谢C语言带领我进入了程序世界.虽然现在不靠它吃饭,但是仍免不了经常和它打交 ...
- NVIDIA GPU SM和CUDA编程理解
SM硬件架构基础 不同架构的变化可以参考: 从AI系统角度回顾GPU架构变迁--从Fermi到Ampere(V1.2) - 知乎 英伟达GPU架构演进近十年,从费米到安培 - 知乎 Vol ...
- 6.CUDA编程手册中文版---附录AB
附录A 支持GPU设备列表 更多精彩内容,请扫描下方二维码或者访问https://developer.nvidia.com/zh-cn/developer-program 来加入NVIDIA开发者计划 ...
- linux模块移植到freertos,opus移植到freertos系统
硬件平台:cortex-M4F 200MHZ平台(RTL8721DM) 软件系统:FREERTOS 编译器: Using built-in specs. COLLECT_GCC=/home/kuili ...
- linux标准c和c编译器6,Linux折腾记(六):感悟GNU C及把Vim打造成C/C++的半自动化IDE...
C语言在Linux系统中的重要性自然是无与伦比.不可替代,所以我写Linux江湖系列不可能不提C语言.C语言是我的启蒙语言,感谢C语言带领我进入了程序世界.虽然现在不靠它吃饭,但是仍免不了经常和它打交 ...
- linux 中输入一个c程序,从c源程序到Linux可执行代码的过程
你写了一个C程序,然后用gcc编译之后得到一个可执行程序.看起来相当简单,是吗? 你有没有想过编译的过程中发生了什么,C程序怎么转变成二进制程序的呢? 其实,源程序最终成为可执行程序经历了如下4个阶段 ...
- 《编写高质量代码:改善c程序代码的125个建议》——第1章 数据,程序设计之根本建议1:认识ANSI C...
本节书摘来自华章计算机<编写高质量代码:改善c程序代码的125个建议>一书中的第1章,建议1,作者:马 伟 更多章节内容可以访问云栖社区"华章计算机"公众号查看. 第1 ...
最新文章
- 架构选型必读:集中式与分布式全方位优劣对比
- 15.4. syslog, klogctl - read and/or clear kernel message ring buffer; set console_loglevel
- 终于弄明白了 Singleton,Transient,Scoped 的作用域是如何实现的
- [MSSQL]也说SQL中显示星期几函数
- GitHub使用入门讲解--官方文档翻译让你最真实了解
- Python之split()函数
- oracle用户密码规则,使用Oracle自带profile以及函数简单设定Oracle用户名密码规则...
- java后端简历项目经历_java程序员简历项目经验怎么写
- Java三大体系JavaSE、JavaEE、JavaME的区别
- 核磁共振成像基本原理——杨正汉(1)
- 推荐一个文字生成图片的网站
- System Repair Engineer (SREng) 2.5 常用操作
- PostgreSQL12中文手册
- mybatis plus(包米豆)json存储Mysql数据库
- C# 批量图片打包下载
- 快手科技2020年总收入人民币588亿元,同比增长50.2%
- mysql数据库行列矩阵调换位置(行与列调换)
- 请教dalao,为什么运行时二三步会合并到一起?
- 在 TensorFlow 上使用 LSTM 进行情感分析
- 前端面试题《CSS》