据我所知,restrict
的含义是format
将是指针生命周期内指向数据的唯一引用.
不完全是.这意味着,如果以任何方式(通过format
或其他方式)修改format
字符串1,则printf
必须仅通过基于format
的其他表达式访问它(因此调用者不得传递另一个参数,该参数将导致printf
通过该参数访问格式字符串),根据C 2018 6.7.3.1.如果格式字符串中的任何内容在printf
调用期间都不会被修改,则有什么其他指向它的指针都无关紧要.
我认为唯一有意义的方法是使用n
转换说明符,该说明符表示对应的参数是一个指向带符号整数的指针,其中写入了这个对printf
的调用到目前为止写入输出流的字符数.因此,如果format
不是restrict-qualified
,您可以编写以下两个printf
调用:
int n;
memcpy(&n, "x%n", 4);
printf((char *) &n, &n); // Would store 1 in `n`, since “x” had been written.
const char String[] = "Hello, world.\n%n";
int *p = malloc(sizeof String);
memcpy(p, String, sizeof String);
printf(p, p); // Would store int `14` at `p`.
前者是合法的,因为任何对象都可以通过字符类型读取,据推测printf
可以做到这一点.后者是合法的,因为动态存储器的有效类型是可延展的,因此在将p
用作字符串之后将其作为int
写入是定义的.
显然,如果%n
导致写入printf
可能尚未用于格式字符串的内存,这将是有问题的.同样,这是在我们假设字符串没有restrict
限定的上下文中.如果是这样的话,就不会定义以这种方式使用%n
的行为.
(n
也可以与修饰语一起使用,如在%hhn
中写char
而不是int
,但这不影响上面的分析.)
脚注
1从技术上讲,format
上的任何对象based,实质上是format
指向的数组对象的所有元素(如果format
指向数组中间,则包括数组中较早的元素).