相关疑难解决方法(0)

使用“memcpy”复制二维数组在技术上是未定义的行为吗?

在对这个最近问题的评论中出现了一个有趣的讨论:现在,虽然那里的语言是C,但讨论已经转向C++标准所指定的内容,即使用 a 访问多维数组的元素时构成未定义行为的内容。功能类似于std::memcpy.

\n

首先,这是该问题的代码,已转换为 C++ 并const尽可能使用:

\n
#include <iostream>\n#include <cstring>\n\nvoid print(const int arr[][3], int n)\n{\n    for (int r = 0; r < 3; ++r) {\n        for (int c = 0; c < n; ++c) {\n            std::cout << arr[r][c] << " ";\n        }\n        std::cout << std::endl;\n    }\n}\n\nint main()\n{\n    const int arr[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };\n    int arr_copy[3][3];\n    print(arr, 3);\n    std::memcpy(arr_copy, arr, sizeof arr);\n …
Run Code Online (Sandbox Code Playgroud)

c c++ multidimensional-array undefined-behavior language-lawyer

70
推荐指数
4
解决办法
6066
查看次数

C中数组的最大大小是多少?

我知道硬件将限制程序执行期间分配的内存量.但是,我的问题是不考虑硬件.假设内存量没有限制,那么阵列没有限制吗?

c arrays

45
推荐指数
4
解决办法
12万
查看次数

std::less 是否应该允许在编译时比较不相关的指针?

考虑这个代码:

#include <functional>
#include <typeinfo>

template <typename T>
inline constexpr const void *foo = &typeid(T);

int main()
{
    constexpr bool a = std::less<const void*>{}(foo<int>, foo<float>);
} 
Run Code Online (Sandbox Code Playgroud)

Run on gcc.gotbolt.org

如果我使用<而不是std::less这里,代码不会编译。这并不奇怪,因为如果指针指向不相关的对象,则关系指针比较的结果是未指定的,而且显然这样的比较不能在编译时完成。

#include <functional>
#include <typeinfo>

template <typename T>
inline constexpr const void *foo = &typeid(T);

int main()
{
    constexpr bool a = std::less<const void*>{}(foo<int>, foo<float>);
} 
Run Code Online (Sandbox Code Playgroud)

即使我使用std::less. 编译器错误是一样的。std::less似乎<至少在 libstdc++ 和 libc++ 中实现;我在 GCC、Clang 和 MSVC 上得到了相同的结果。

但是,关于 cppreference 的页面 …

c++ std compile-time language-lawyer

39
推荐指数
2
解决办法
1095
查看次数

指针比较在 C 中如何工作?可以比较不指向同一个数组的指针吗?

在 K&R(C 编程语言第 2 版)第 5 章中,我阅读了以下内容:

首先,在某些情况下可以比较指针。如果pq指向同一个数组的成员,则关系一样==!=<>=,等正常工作。

这似乎意味着只能比较指向同一数组的指针。

但是,当我尝试此代码时

    char t = 't';
    char *pt = &t;
    char x = 'x';
    char *px = &x;

    printf("%d\n", pt > px);
Run Code Online (Sandbox Code Playgroud)

1 被打印到屏幕上。

首先,我认为我会得到 undefined 或某种类型或错误,因为ptpx没有指向同一个数组(至少在我的理解中)。

也是pt > px因为两个指针都指向存储在栈上的变量,栈向下增长,所以内存地址t大于x?为什么pt > px是真的?

当 malloc 被引入时,我变得更加困惑。 同样在第 8.7 章的 K&R 中写到以下内容:

然而,仍然存在一种假设,即sbrk可以有意义地比较指向由 返回的不同块的指针。仅允许在数组内进行指针比较的标准并不能保证这一点。因此,这个版本的malloc仅在一般指针比较有意义的机器之间是可移植的。

将指向堆上 malloced …

c pointers heap-memory memory-layout undefined-behavior

35
推荐指数
3
解决办法
5731
查看次数

如何在没有中间副本的情况下在标准C中实现memmove?

从我系统的手册页:

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.在所有其他情况下,行为 未定义.

重点是我的.

的参数dstsrc可被转化为指针char以便减轻严格别名的问题,但有可能以比较两个指针可以指向内部的不同的块,以便做以正确的顺序的拷贝的情况下,它们指向相同的块内?

显而易见的解决方案是if (src < dst),但未定义如果srcdst指向不同的块."未定义"意味着您甚至不应该假设条件返回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)

c c99 undefined-behavior unspecified-behavior

33
推荐指数
2
解决办法
6493
查看次数

我可以将2D数组视为连续的1D数组吗?

请考虑以下代码:

int a[25][80];
a[0][1234] = 56;
int* p = &a[0][0];
p[1234] = 56;
Run Code Online (Sandbox Code Playgroud)

第二行是否调用未定义的行为?第四行怎么样?

c++ arrays pointers multidimensional-array undefined-behavior

33
推荐指数
3
解决办法
6087
查看次数

什么是近,远和巨大的指针?

任何人都可以用适当的例子向我解释这些指针......当这些指针被使用时?

c c++ x86 pointers x86-16

29
推荐指数
3
解决办法
2万
查看次数

如果将两个指针都强制转换为整数类型,是否将它们与&lt;未定义行为进行比较?

假设我有这段代码,可以根据它们的位置以一定的顺序将一个内存块复制到另一个内存中:

void *my_memmove(void *dest, const void *src, size_t len)
{
    const unsigned char *s = (const unsigned char *)src;
    unsigned char *d = (unsigned char *)dest;

    if(dest < src)
    {
        /* copy s to d forwards */
    }
    else
    {
        /* copy s to d backwards */
    }

    return dest;
}
Run Code Online (Sandbox Code Playgroud)

如果src并且dest不指向同一数组6.8.5p5)的成员,则这是未定义的行为。

但是,假设我将这两个指针转换为uintptr_t类型:

#include <stdint.h>

void *my_memmove(void *dest, const void *src, size_t len)
{
    const unsigned char *s = (const …
Run Code Online (Sandbox Code Playgroud)

c undefined-behavior

3
推荐指数
1
解决办法
369
查看次数

应该在64位x86中对指针比较进行签名还是不签名?

在编写x86-64用户空间程序集并比较两个指针值时,我们应该使用带符号的条件(例如jl和)jge还是使用无符号的条件(例如jb和)jae

直觉上,我认为指针是无符号的,在64位进程的情况下,指针从0到2 ^ 64-1,并且我认为该模型对于32位代码是准确的。我想这就是大多数人对他们的看法。

但是,在64位代码中,我认为您无法有效地跨越0x7FFFFFFFFFFFFFFF(2 ^ 63-1)处的有符号不连续性,并且许多有趣的内存区域倾向于聚集在有符号0附近(对于代码和静态数据,有时甚至是有时)堆的大小取决于实现),并且0x00007fffffffffff在某些实现1的堆栈地址和堆附近接近规范地址空间下半部分的最大地址(类似于当今的大多数系统)。

因此,我不确定应该采用哪种方式对待它们:带符号的优点是它在0附近是安全的,因为那里没有间断;而无符号的优点是在2 ^ 63附近,因为那里没有间断。但是实际上,您不会在2 ^ 63附近看到任何地址,因为当前商用硬件的虚拟地址空间限制为小于50位。这是否指向签名?


1 ...,有时堆和其他映射区域不靠近地址空间的底部或顶部。

x86 assembly pointers x86-64

2
推荐指数
1
解决办法
285
查看次数

空指针是如何实现的?

我想知道 void 指针是如何实现的。我试图用 x86-64 在 Godbolt 上找到它(你可以在这里看到它),但它没有透露任何信息。空指针是如何实现的?

编辑: 这是我使用的代码:

int main() {
    int volatile y = 123;
    void* volatile x = &y;
}
Run Code Online (Sandbox Code Playgroud)

我想在这里看到的只是它的x样子。我放置了 volatile 以便 gcc 不会将其作为死代码消除。

c assembly pointers x86-64 void-pointers

1
推荐指数
1
解决办法
159
查看次数