相关疑难解决方法(0)

(x+1) > x 如何评估为 0 和 1?

我正在学习未定义的行为,并在没有任何明确解释的情况下偶然发现了这段代码:

#include <stdio.h>
#include <limits.h>

int foo ( int x) {
    printf ("% d\n" ,  x );   //2147483647
    printf ("% d\n" ,  x+1 ); //-2147483648  overflow
    return ( x+1 ) > x ;      // 1 but How????
}

int main ( void ) {
    printf ("% d\n" ,  INT_MAX );     //2147483647
    printf ("% d\n" ,  INT_MAX+1 );   //-2147483648  overflow
    printf ("% d\n" , ( INT_MAX+1 ) > INT_MAX );  //0  makes sense, since -ve < +ve
    printf ("% d\n" , …
Run Code Online (Sandbox Code Playgroud)

c c++ undefined-behavior

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

C堆栈变量是否反向存储?

我试图理解C如何在堆栈上分配内存.我一直认为堆栈上的变量可以描述为结构成员变量,它们占用堆栈中连续的,连续的字节块.为了帮助说明我在某个地方发现的这个问题,我创建了这个小程序来重现这个现象.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void function(int  *i) {
    int *_prev_int =  (int *) ((long unsigned int) i -  sizeof(int))  ;
    printf("%d\n", *_prev_int );    
}

void main(void) 
{
    int x = 152;
    int y = 234;
    function(&y);
}
Run Code Online (Sandbox Code Playgroud)

看看我在做什么?假设sizeof(int)是4:我在传递的指针后面看了4个字节,因为它会int y调用者堆栈中的位置之前读取4个字节.

它没有打印152.奇怪的是当我看下4个字节时:

int *_prev_int =  (int *) ((long unsigned int) i +  sizeof(int))  ;
Run Code Online (Sandbox Code Playgroud)

现在它可以工作,打印x调用者堆栈内的任何内容.为什么x地址低于y?堆栈变量是否颠倒存储?

c stack pointers memory-address

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

在不同的体系结构上转换指针类型

我有以下结构和"getter"函数,它返回a强制转换为无符号整数:

struct s {
    uint32_t a;
};

void get_a(struct s *st, unsigned *ret)
{
    *ret = (unsigned)st->a;
}
Run Code Online (Sandbox Code Playgroud)

运行以下代码:

struct s st;
uint16_t x;

st.a = 1;
get_a(&st, (unsigned *)&x);
Run Code Online (Sandbox Code Playgroud)

对于x86_64,i686,armv7hl,ppc64le等架构x == 1,但对于ppc64 x == 0.为什么是这样?Little-vs. big-endian?

c pointers casting endianness

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

为什么 malloc(1) 可以存储 4 字节整数?

据我了解,malloc(x) 返回一个 x 字节长的内存块。

因此,要存储 4 字节整数,我会这样做:

int *p = (int *)malloc(4);
*p = 100;
Run Code Online (Sandbox Code Playgroud)

因为 sizeof(int) 对我返回 4 。

但是,如果我这样做:

int *p = (int *)malloc(1);
*p = 100;
Run Code Online (Sandbox Code Playgroud)

它的工作原理似乎完全相同,存储值没有问题。

为什么使用 malloc() 请求的内存量似乎并不重要?4 字节整数不应该需要 malloc(4) 吗?

c memory malloc

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

什么((size_t*)ptr)[ - 1]在C中意味着什么?

我想知道分配给指针的大小.

所以我找到了这个答案: 我怎么知道c中指针变量的分配内存大小

它有以下代码.

#include <stdlib.h>
#include <stdio.h>

void * my_malloc(size_t s) 
{
  size_t * ret = malloc(sizeof(size_t) + s);
  *ret = s;
  return &ret[1];
}

void my_free(void * ptr) 
{
  free( (size_t*)ptr - 1);
}

size_t allocated_size(void * ptr) 
{
  return ((size_t*)ptr)[-1];
}

int main(int argc, const char ** argv) 
{
  int * array = my_malloc(sizeof(int) * 3);
  printf("%u\n", allocated_size(array));
  my_free(array);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这条线(((size_t*)ptr)[-1])很完美,但我不明白为什么......

有人能帮我理解这条神奇的线条吗?谢谢!

c size pointers

6
推荐指数
2
解决办法
262
查看次数

为什么我的程序从右到左评估参数?

我正在学习C,因此尝试了以下代码,并获得了7,6而不是的输出6,7。为什么?

#include <stdio.h>
int f1(int);
void main()
{
    int b = 5;
    printf("%d,%d", f1(b), f1(b));
}
int f1(int b)
{
    static int n = 5;
    n++;
    return n;
}
Run Code Online (Sandbox Code Playgroud)

c function-call order-of-execution

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

将分配运算符实现为“销毁+构造”是否合法?

我经常需要为“原始”资源句柄(例如文件句柄,Win32 OS句柄等)实现C ++包装。这样做时,我还需要实现move运算符,因为默认的编译器生成的操作符不会清除move-from对象,从而产生双删除问题。

在实现移动分配运算符时,我更喜欢显式调用析构函数,并使用new放置就地重新创建对象。这样,我避免了析构函数逻辑的重复。另外,我经常根据copy + move(在相关时)实现副本分配。这将导致以下代码:

/** Canonical move-assignment operator. 
    Assumes no const or reference members. */
TYPE& operator = (TYPE && other) noexcept {
    if (&other == this)
        return *this; // self-assign

    static_assert(std::is_final<TYPE>::value, "class must be final");
    static_assert(noexcept(this->~TYPE()), "dtor must be noexcept");
    this->~TYPE();

    static_assert(noexcept(TYPE(std::move(other))), "move-ctor must be noexcept");
    new(this) TYPE(std::move(other));
    return *this;
}

/** Canonical copy-assignment operator. */
TYPE& operator = (const TYPE& other) {
    if (&other == this)
        return *this; // self-assign

    TYPE copy(other); // may throw

    static_assert(noexcept(operator …
Run Code Online (Sandbox Code Playgroud)

c++ assignment-operator move-semantics move-assignment-operator

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

在特定实现上定义了两个数组之间的指针差异吗?

根据C标准:

当两个指针相减时,两者都应指向同一数组对象的元素,或指向数组对象最后一个元素的元素(第 6.5.6 1173 节)

[注意:不要假设我对标准或 UB 了解很多,我只是碰巧发现了这个]

  1. 我知道在几乎所有情况下,无论如何取两个不同数组中指针的差异都是一个坏主意。
  2. 我也知道在某些架构上(我在某处读到的“分段机器”),行为未定义有充分的理由。

现在另一方面

  1. 它在某些极端情况下可能有用。例如,在这篇文章中,它将允许使用具有不同数组的库接口,而不是复制一个数组中的所有内容,然后将其拆分。
  2. 似乎在“普通”架构上,“所有对象都存储在一个大数组中,从大约 0 开始到大约内存大小”的思维方式是对内存的合理描述。当您实际查看不同数组的指针差异时,您会得到合理的结果。

因此我的问题是:从实验来看,似乎在某些体系结构(例如 x86-64)上,两个数组之间的指针差异提供了合理的、可重现的结果。它似乎与这些架构的硬件相当吻合。那么某些实现是否真的确保了特定的行为?

例如,在野外是否有一个实现可以保证a并且b存在char*,我们有a + (reinterpret_cast<std::ptrdiff_t>(b)-reinterpret_cast<std::ptrdiff_t>(a)) == b

c c++ pointer-arithmetic language-lawyer

6
推荐指数
3
解决办法
257
查看次数

为什么在 Awk 中未定义用于连接的表达式的求值顺序?

在 GNU Awk 用户指南中,我浏览了6.2.2 字符串连接部分,并发现了有趣的见解:

由于字符串连接没有显式运算符,因此通常需要通过使用括号将要连接的项括起来来确保它在正确的时间发生。

然后,我很惊讶地阅读以下内容:

除了最常见的上下文外,所有的连接都应该使用括号,例如在“=”的右侧。请注意字符串连接中使用的表达式类型。特别是,用于连接的表达式的求值顺序在 awk 语言中是未定义的。考虑这个例子:

BEGIN {
   a = "don't"
   print (a " " (a = "panic"))
}
Run Code Online (Sandbox Code Playgroud)

未定义对 a 的第二次赋值是在检索 a 的值以生成连接值之前还是之后。结果可能是“不要惊慌”或“惊慌失措”。

特别是,在我的 GNU Awk 5.0.0 中,它是这样执行的,在打印值之前进行替换:

$ gawk 'BEGIN {a = "dont"; print (a " " (a = "panic"))}'
dont panic
Run Code Online (Sandbox Code Playgroud)

但是,我想知道:为什么没有定义表达式的求值顺序?根据您运行的 Awk 版本的不同,拥有“未定义”的输出有什么好处?

awk

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

为什么 -O3 向下转换/更改对变量的 const 引用的值?

#include <iostream>
#include <stdint.h>

class Test {
  public:
  Test(const int64_t & val) : val_(val) {
    std::cout << "initialized: " << val_ << std::endl;
  }
  void print() {std::cout << "reference val: " << val_ << std::endl;}

  private:
  const int64_t & val_;
};


int main() {
  long long int input_val= 1628020800000000000L;
  auto t = Test(input_val);
  std::cout << "input_val: " << input_val << std::endl; 
  t.print();
}
Run Code Online (Sandbox Code Playgroud)

如果您在没有优化构建的情况下构建,则会得到以下结果:

g++ main.cpp -std=c++17
initialized: 1628020800000000000
input_val: 1628020800000000000
reference val: 1628020800000000000
Run Code Online (Sandbox Code Playgroud)

如果您使用优化构建(例如 -O3)构建,您将获得以下信息:

g++ main.cpp -std=c++17 -O3 …
Run Code Online (Sandbox Code Playgroud)

c++ c++17

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