请考虑以下代码:
#include <iostream>
struct foo
{
// (a):
void bar() { std::cout << "gman was here" << std::endl; }
// (b):
void baz() { x = 5; }
int x;
};
int main()
{
foo* f = 0;
f->bar(); // (a)
f->baz(); // (b)
}
Run Code Online (Sandbox Code Playgroud)
我们期望(b)崩溃,因为x空指针没有相应的成员.在实践中,(a)不会崩溃,因为this从不使用指针.
因为(b)取消引用this指针((*this).x = 5;),并且this为null,程序进入未定义的行为,因为取消引用null总是被称为未定义的行为.
会(a)导致未定义的行为吗?如果两个函数(和x)都是静态的呢?
c++ standards-compliance null-pointer undefined-behavior language-lawyer
class Foo {
public:
static const int kType = 42;
};
void Func() {
Foo *bar = NULL;
int x = bar->kType;
putc(x, stderr);
}
Run Code Online (Sandbox Code Playgroud)
这是定义的行为吗?我阅读了C++标准但是找不到任何关于访问静态const值的内容......我已经检查了GCC 4.2,Clang ++和Visual Studio 2010生成的程序集,并且它们都没有执行NULL的解引用指针,但我想确定.
一位博客作者提出了有关空指针解除引用的讨论:
我在这里提出一些反驳论点:
他引用标准的主要推理理由是:
当'podhd'是空指针时,'&podhd-> line6'表达式在C语言中是未定义的行为.
C99标准说明了以下关于'&'地址的运算符(6.5.3.2"地址和间接运算符"):
一元&运算符的操作数应该是函数指示符,[]或一元*运算符的结果,或者是一个左值,它指定一个不是位字段的对象,并且不用寄存器存储类说明符声明.
表达式'podhd-> line6'显然不是函数指示符,是[]或*运算符的结果.这是一个左值表达式.但是,当'podhd'指针为NULL时,表达式不指定对象,因为6.3.2.3"Pointers"表示:
如果将空指针常量转换为指针类型,则保证将结果指针(称为空指针)与指向任何对象或函数的指针进行比较.
当"左值在评估时未指定对象时,行为未定义"(C99 6.3.2.1"左值,数组和函数指示符"):
左值是具有对象类型或除void之外的不完整类型的表达式; 如果左值在评估时未指定对象,则行为未定义.
所以,同样的想法简要说明:
当在指针上执行 - >时,它会计算到没有对象存在的左值,因此行为是未定义的.
这个问题纯粹是基于语言的,我不会问一个给定的系统是否允许用任何语言篡改地址0的内容.
据我所知,取消引用一个值等于的指针变量没有限制nullptr,甚至认为指针与nullptr(或(void *) 0)常量的比较在某些情况下可能会因为所述段落而在优化中消失,但这看起来像另一个问题是,它不会阻止取消引用其值等于的指针nullptr.请注意,我已经检查了其他SO问题和答案,我特别喜欢这组引用,以及上面的标准引号,我没有偶然发现一些明显从标准中推断出如果指针ptr比较等于nullptr,取消引用这将是未定义的行为.
我得到的最多是将常量(或其转换为任何指针类型)引用的是UB,但没有任何关于变量等于从中得到的值的变量nullptr.
我想清楚地将nullptr常量与保持值等于它的指针变量分开.但解决这两种情况的答案都是理想的.
我确实意识到,当进行比较nullptr等时,优化可以快速进行,并且可以简单地基于此来剥离代码.
如果结论是,如果ptr等于nullptr解除引用的值肯定是UB,另一个问题如下:
说我有班级:
class A
{
public:
void foo() { cout << "foo"; }
};
Run Code Online (Sandbox Code Playgroud)
并像这样调用foo:
A* a = NULL;
a->foo();
Run Code Online (Sandbox Code Playgroud)
我怀疑这会调用未定义的行为,因为它等同于(*a).foo()(或者它是?),并且取消引用a NULL 是 UB,但我找不到引用.谁能帮我吗?还是定义了?
不,功能不是virtual.不,我没有访问任何成员.
编辑:我投票结束这个问题,但不会删除它,因为我自己找不到副本,我怀疑这个标题可能更容易被其他人找到.
在C/C++中,如何确定结构的成员变量的大小,而无需定义该结构类型的虚拟变量?这是一个如何做错的例子,但显示了意图:
typedef struct myStruct {
int x[10];
int y;
} myStruct_t;
const size_t sizeof_MyStruct_x = sizeof(myStruct_t.x); // error
Run Code Online (Sandbox Code Playgroud)
作为参考,如果你首先定义一个虚拟变量,这应该是如何找到'x'的大小:
myStruct_t dummyStructVar;
const size_t sizeof_MyStruct_x = sizeof(dummyStructVar.x);
Run Code Online (Sandbox Code Playgroud)
但是,我希望避免为了获得'x'的大小而创建一个虚拟变量.我认为有一种聪明的方法可以将0重新设置为myStruct_t,以帮助找到成员变量'x'的大小,但是已经足够长,以至于我忘记了细节,似乎无法在这方面获得良好的Google搜索.你知道吗?
谢谢!
我用GCC 5.2(C++ 11)测试了以下代码:
#include <iostream>
#include <memory>
struct Foo
{
Foo() { std::cout << "Foo::Foo\n"; }
~Foo() { std::cout << "Foo::~Foo\n"; }
void bar() { std::cout << "Foo::bar\n"; }
};
void f(const Foo &)
{
std::cout << "f(const Foo&)\n";
}
int main()
{
std::unique_ptr<Foo> p1(new Foo); // p1 owns Foo
if (p1) p1->bar();
{
//p1->bar();
std::unique_ptr<Foo> p2(std::move(p1)); // now p2 owns Foo
f(*p2);
p1->bar();
if(p1==nullptr)
{
std::cout<<"NULL"<<std::endl;
}
p1 = std::move(p2); // ownership returns to p1
std::unique_ptr<Foo> p3;
p3->bar();
std::cout …Run Code Online (Sandbox Code Playgroud) 这里出现了一个问题,当一个指针变得晃来晃去时,问"为什么这个工作".答案是它是UB,这意味着它可能起作用.
我在一个教程中学到了:
#include <iostream>
struct Foo
{
int member;
void function() { std::cout << "hello";}
};
int main()
{
Foo* fooObj = nullptr;
fooObj->member = 5; // This will cause a read access violation but...
fooObj->function(); // Because this doesn't refer to any memory specific to
// the Foo object, and doesn't touch any of its members
// It will work.
}
Run Code Online (Sandbox Code Playgroud)
这相当于:
static void function(Foo* fooObj) // Foo* essentially being the "this" pointer
{
std::cout << "Hello";
// Foo …Run Code Online (Sandbox Code Playgroud) 我有一个包含多个类的程序,其中一些需要随机双精度和整数。在其中一个类中,我定义了一个结构来为随机引擎提供种子,并且能够在需要时通过该结构的对象生成随机实数和整数。带有类和结构声明的 .hpp 文件如下所示:
struct RNG {
public:
static std::mt19937 gen;
static std::uniform_int_distribution<uint32_t> dist_ui;
static std::uniform_real_distribution<double> dist_d;
uint32_t r_ui = dist_ui(gen);
double r_d = dist_d(gen);
private:
static unsigned seed;
};
class A {
private:
uint32_t a_;
public:
A();
};
Run Code Online (Sandbox Code Playgroud)
.cpp 文件如下所示:
#include "A.hpp"
unsigned RNG::seed = 42;
std::mt19937 RNG::gen(RNG::seed);
std::uniform_int_distribution<uint32_t> RNG::dist_ui(1, std::numeric_limits<uint32_t>::max());
std::uniform_real_distribution<double> RNG::dist_d(0.0,1.0);
A::A() {
RNG* r;
a_ = r->dist_ui(r->gen);
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我调用uniform_real_distribution
r->dist_d(r->gen)
Run Code Online (Sandbox Code Playgroud)
一切正常。但是,如果我调用uniform_int_distribution
r->dist_ui(r->gen)
Run Code Online (Sandbox Code Playgroud)
与上面的代码片段一样,我遇到了分段错误。如果我仅使用 int dist 而不是 real 和 int 来定义结构,或者将 int dist 的边界更改为 [0,100) 或其他任何内容,也会发生错误。有谁知道这里发生了什么?我感谢任何帮助!
编辑:如果我像这样访问非静态成员,我会得到同样的错误
RNG …Run Code Online (Sandbox Code Playgroud) 这个C++代码适用于我的平台和编译器(Windows,GCC 4.7):
#include <stdio.h>
class A {
public:
/* ... */
int size() const
{
if ( this == NULL ) {
return 0;
}
return m_size;
}
private:
int m_size;
};
int main()
{
A* a = NULL;
printf( "%d\n", a->size() );
}
Run Code Online (Sandbox Code Playgroud)
但这段代码是有效的标准C++还是可移植的?方法接受这个== NULL是否合适?