从我系统的手册页:
void*memmove(void*dst,const void*src,size_t len);
描述
memmove()函数将字符串src中的len个字节复制到字符串dst.
两个字符串可能重叠 ; 副本总是以非破坏性的
方式完成.
从C99标准:
6.5.8.5比较两个指针时,结果取决于指向的对象的地址空间中的相对位置.如果指向对象或不完整类型的两个指针都指向同一个对象,或者两个指针都指向同一个数组对象的最后一个元素,则它们相等.如果指向的对象是同一聚合对象的成员,则指向稍后声明的结构成员的指针比指向结构中先前声明的成员的指针大,指向具有较大下标值的数组元素的指针比指向同一数组的元素的指针大.具有较低的下标值.指向同一个union对象的成员的所有指针都比较相等.如果表达
P
指向数组对象的元素,表达式Q指向同一数组对象的最后一个元素,指针表达式Q+1
比较大于P
.在所有其他情况下,行为 未定义.
重点是我的.
的参数dst
和src
可被转化为指针char
以便减轻严格别名的问题,但有可能以比较两个指针可以指向内部的不同的块,以便做以正确的顺序的拷贝的情况下,它们指向相同的块内?
显而易见的解决方案是if (src < dst)
,但未定义如果src
和dst
指向不同的块."未定义"意味着您甚至不应该假设条件返回0或1(这在标准词汇表中称为"未指定").
另一种选择是if ((uintptr_t)src < (uintptr_t)dst)
,至少是未指定的,但我不确定标准是否保证何时src < dst
定义,它等同于(uintptr_t)src < (uintptr_t)dst)
.指针比较是从指针算法定义的.例如,当我在添加时阅读第6.5.6节时,在我看来,指针算法可以与uintptr_t
算术相反的方向,即兼容的编译器可能具有,当p
类型为char*
:
((uintptr_t)p)+1==((uintptr_t)(p-1)
Run Code Online (Sandbox Code Playgroud)
这只是一个例子.一般来说,将指针转换为整数时,似乎可以保证很少.
这是一个纯粹的学术问题,因为memmove
它与编译器一起提供.在实践中,编译器作者可以简单地将未定义的指针比较提升为未指定的行为,或者使用相关的编译指示强制其编译器memmove
正确编译它们.例如,此实现具有以下代码段:
if ((uintptr_t)dst < (uintptr_t)src) {
/*
* As author/maintainer of libc, take advantage …
Run Code Online (Sandbox Code Playgroud) 我正在阅读本文,他们使用以下示例来解释未定义的行为:
// PROGRAM 1
#include <stdio.h>
int f1() { printf ("Geeks"); return 1;}
int f2() { printf ("forGeeks"); return 1;}
int main()
{
int p = f1() + f2();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,它似乎是关于子表达式的评估顺序,并且根据C标准(附件J.1),它是一个未指定的行为,而不是未定义的行为:
未指定的行为:评估子表达式的顺序以及副作用发生的顺序,除非为function-call(),&&,||指定.,?:和逗号运算符(6.5)
由于我对阅读官方规范非常陌生,我想知道我是否误解了这个例子或文档.我知道这看起来很迂腐,但我有兴趣以正确的方式学习这些高级主题.
更新:由用户ecatmur标记,它是In C99的副本,f()+ g()未定义或仅仅未指定?(虽然问题询问C99,但C++的答案没有变化).答案是:未指定(两种情况).
考虑以下C++ 14代码片段:
int i = 0;
int x() { i++; return i;}
int y() { i++; return i;}
bool z = (x() > y()); // unspecified or undefined ?
Run Code Online (Sandbox Code Playgroud)
z
仅仅是未指定的值,还是这种未定义的行为?
根据我的理解(请纠正,如果我错了),这种类型的表达式:i++ > i++
将是未定义的行为,因为我们在一对序列点之间两次改变相同的变量,但是上面的情况如何(突变发生在单独的功能) ?
那一个怎么样:
bool z = (x() > i++); // undefined or unspecified now ?
Run Code Online (Sandbox Code Playgroud) int func(int **a)
{
*a = NULL;
return 1234;
}
int main()
{
int x = 0, *ptr = &x;
*ptr = func(&ptr); // <-???
printf("%d\n", x); // print '1234'
printf("%p\n", ptr); // print 'nil'
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是一个未定义行为的例子还是与序列点有关?为什么行:
*ptr = func(&ptr);
Run Code Online (Sandbox Code Playgroud)
表现不像:
*NULL = 1234;
Run Code Online (Sandbox Code Playgroud)
编辑:我忘了提到我用gcc 4.7获得输出'1234'和'nil'.
的逗号序列操作者引入了一个顺序点的表达式中.我想知道这是否意味着下面的程序避免了未定义的行为.
int x, y;
int main()
{
return (x++, y) + (y++, x);
}
Run Code Online (Sandbox Code Playgroud)
如果它确实避免了未定义的行为,它仍然可能未指定,即返回几个可能值中的一个.我认为在C99中,它只能计算1
,但实际上,各种版本的GCC将此程序编译成可返回的可执行文件2
.Clang生成一个返回的可执行文件1
,显然与我的直觉一致.
最后,这是C11改变了吗?
可能重复:
什么属于教育工具,以证明人们在C/C++中做出的无根据的假设?
在C99中,f()+ g()未定义或仅仅未指定?
在语句中,"function1()+ function2();"将首先调用哪个函数?