0

0

C 中晦涩难懂的“restrict”关键字

PHPz

PHPz

发布时间:2024-09-11 08:07:34

|

1079人浏览过

|

来源于dev.to

转载

c 中晦涩难懂的“restrict”关键字

介绍

除此之外,c99 添加了 limit 关键字,作为程序员指定指针是指向作用域中给定对象的唯一指针的一种方式,从而给编译器一个“提示” ”,当通过该指针访问对象时,它可能会执行额外的优化。

问题

为了说明限制要解决的问题,请考虑如下函数:

void update_ptrs( int *p, int *q, int const *v ) {
  *p += *v;
  *q += *v;
}

编译器将生成 x86-64 代码,例如:

mov eax, [rdx]  ; tmp = *v   // 1
add [rdi], eax  ; *p += tmp
mov eax, [rdx]  ; tmp = *v   // 3
add [rsi], eax  ; *q += tmp

你可能想知道为什么它生成第 3 行,因为它看起来与第 1 行是多余的。问题是编译器不知道你没有做这样的事情:

int x = 1, v = 2;
update_ptrs( &v, &x, &v );   // x = 5, v = 4

在 update_ptrs() 中,p 和 v 会别名 相同 int,因此编译器必须谨慎行事并假设 *v 的值可以在读取之间发生变化,因此需要额外的 mov 指令。

一般来说,c 中的指针会混淆优化,因为编译器无法知道两个指针是否彼此别名。 在性能关键的代码中,消除内存读取可能是一个巨大的胜利如果编译器可以安全地做到这一点。

解决方案

为了解决上述问题,c 中添加了 limit,允许您指定给定指针是 唯一 指向该指针作用域中的对象的指针,即同一作用域别名中没有其他指针它。

要使用限制,请将其插入声明中的 * 和指针名称之间。 重写为使用限制的 update_ptrs() 将是:

void update_ptrs_v2( int *restrict p, int *restrict q,
                     int const *restrict v ) {
  *p += *v;
  *q += *v;
}

(从右到左读取,例如 v 是指向常量 int 的受限指针;或使用 cdecl。)

通过添加限制,编译器现在可以生成如下代码:

mov eax, [rdx]  ; tmp = *v
add [rdi], eax  ; *p += tmp
add [rsi], eax  ; *q += tmp

现在,编译器能够删除附加 mov 指令的前第 3 行。

也许最知名的使用restrict的例子是标准库函数memcpy()。 这是复制内存块的最快方法如果源地址和目标地址重叠。当地址 重叠时,可以使用稍慢的 memmove() 函数。

陷阱

滥用限制会导致未定义的行为,例如,将 do 彼此别名的指针传递给 update_ptrs_v2() 或 memcpy()。 在某些情况下,编译器可以警告您,但并非在所有情况下,因此不要依赖编译器来捕获误用。

请注意,限制是针对给定范围的。 将一个受限制的指针分配给同一范围内的另一个会导致未定义的行为:

void f( int *restrict d, int *restrict s ) {
  int *restrict p = s;    // undefined behavior

但是,您可以将受限制的指针分配给不受限制的指针:

Manus
Manus

全球首款通用型AI Agent,可以将你的想法转化为行动。

下载
void f( int *restrict d, int *restrict s ) {
  int *p = s;             // ok

即使 p 不受限制,编译器仍然可以执行相同的优化。

也可以将内部作用域中的受限指针分配给外部作用域中的另一个受限制指针(但反之则不然):

void f( int *restrict d, int *restrict s ) {
  {                       // inner scope
    int *restrict p = s;  // ok
    // ...
    s = p;                // undefined behavior
  }
}

何时(以及何时不)使用限制

首先,您绝对应该分析您的代码(甚至可能查看生成的汇编代码),看看使用限制是否确实能够带来显着的性能改进,以证明冒潜在陷阱的风险是合理的。 诊断因滥用限制而导致的错误非常很难做到。

其次,如果限制的使用仅限于实现通过受限指针访问的内存由you分配的函数,那么它会更安全。 例如,给定:

void safer( unsigned n ) {
  n += n % 2 != 0;  // make even by rounding up
  int *const array = malloc( n * sizeof(unsigned) );
  unsigned *restrict half_1st = array;
  unsigned *restrict half_2nd = array + n/2;
  // ...
  free( array );
}

代码可以安全地对数组的前半部分和后半部分进行操作,因为它们不重叠(假设您从未访问 half_1st[n/2] 或更多)。

第三,如果在函数的参数中使用restrict,那么它可能不太安全。 例如,将 safer() 与 update_ptrs_v2() 进行对比,其中 调用者 控制指针。 据所知,调用者错误并传递了别名的指针。

各种各样的

只有指向对象(或void)的指针可以用restrict限定:

restrict int x;       // error: can't restrict object
int restrict *p;      // error: pointer to restrict object
int (*restrict f)();  // error: pointer-to-function

可以对结构体成员使用restrict,例如:

struct node {
   void *restrict data;
   struct node *restrict left;
   struct node *restrict right;
};

表示 data 将是指向该数据的唯一指针,并且 left 和 right 永远不会指向同一个节点。 然而,对结构成员使用限制是非常不常见的。

最后,c++ 没有有限制。 为什么不呢? 答案很长,但 tl;dr 版本是:

  • 它可能是 c++ 委员会不想从 c 导入的难以发现的错误的来源。
  • c++ 越来越多地使用指针,例如这个,使得安全使用限制变得更加困难。

但是,许多编译器都有 __restrict__ 作为扩展。

结论

在有限的情况下,使用限制可以提高性能,但也存在一些重大缺陷。 如果您正在考虑使用限制,请先分析您的代码。

明智地使用。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1502

2023.10.24

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

240

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

192

2025.07.04

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

463

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

544

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

93

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

200

2025.08.29

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

177

2023.11.23

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 7.9万人学习

C 教程
C 教程

共75课时 | 4.3万人学习

C++教程
C++教程

共115课时 | 14.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号