我们都知道解除引用空指针或指向未分配内存的指针会调用未定义的行为.
但是在传递给表达式时使用的规则是什么sizeof?
例如:
int *ptr = 0;
int size = sizeof(*ptr);
Run Code Online (Sandbox Code Playgroud)
这也未定义吗?
码:
#include<iostream>
using namespace std;
int main()
{
size_t i = sizeof new int;
cout<<i;
}
Run Code Online (Sandbox Code Playgroud)
在GCC编译器中,工作正常,没有任何警告或错误和打印输出8.
但是,在clang编译器中,我收到了以下警告:
warning: expression with side effects has no effect in an unevaluated context [-Wunevaluated-expression]
size_t i = sizeof new int;
Run Code Online (Sandbox Code Playgroud)
sizeof new int;未定义的行为?我遇到了一段代码片段,对我来说应该会因为分段错误而崩溃,但它可以毫无障碍地工作.有问题的代码加上相关的数据结构如下(在上面找到相关的评论):
typedef struct {
double length;
unsigned char nPlaced;
unsigned char path[0];
}
RouteDefinition* Alloc_RouteDefinition()
{
// NB: The +nBags*sizeof.. trick "expands" the path[0] array in RouteDefinition
// to the path[nBags] array
RouteDefinition *def = NULL;
return (RouteDefinition*) malloc(sizeof(RouteDefinition) + nBags * sizeof(def->path[0]));
}
Run Code Online (Sandbox Code Playgroud)
为什么这样做?我收集了的sizeof的字符*将解决对给定的体系结构指针的大小,但它不应该和好如初,同时取消引用NULL终场?
TL; DR
给出以下代码:
int* ptr;
*ptr = 0;
Run Code Online (Sandbox Code Playgroud)
在应用间接之前,是否*ptr需要进行左值到右值的转换ptr?
该标准涵盖了许多地方的左值到左值的主题,但似乎没有指定足够的信息来确定*运算符是否需要这样的转换.
细节
在左值到右值的转换是覆盖在N3485的部分4.1 左值到右值转换段落1和说(重点矿山前进):
可以将非函数非数组类型T的glvalue(3.10)转换为prvalue.53如果T是不完整类型,则需要进行此转换的程序是错误的.如果glvalue引用的对象不是T类型的对象,并且不是从T派生的类型的对象,或者如果该对象未初始化,则需要此转换的程序具有未定义的行为.[...]
那么*ptr = 0; 转换需要吗?
如果我们转到4第1节,它说:
[...] 如果需要,标准转换序列将应用于表达式 ,以将其转换为所需的目标类型.
那么什么时候需要呢?如果我们看一下5 表达式,第9段中提到了左值到右值的转换,它说:
每当glvalue表达式作为操作符的操作数出现时,该操作符需要该操作数的prvalue,左值到右值(4.1),数组到指针(4.2)或函数到指针(4.3)标准转换是用于将表达式转换为prvalue.[...]
和第11段说:
在某些情况下,表达式仅出现其副作用.这样的表达式称为丢弃值表达式.[...]当且仅当表达式是volatile限定类型的左值并且它是以下之一时,才应用左值到右值转换(4.1). ...]
两段似乎都不适用于此代码示例和5.3.1 一元运算符第1段它说:
一元*运算符执行间接:它所应用的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是指向表达式指向的对象或函数的左值.如果表达式的类型是"指向T的指针",则结果的类型为"T".[注意:通过指向不完整类型(cv void除外)的指向的间接有效.由此获得的左值可以以有限的方式使用(例如,初始化参考); 这个左值不能转换为prvalue,见4.1. - …
这个答案的评论部分中的一个论点促使我提出这个问题.
在以下代码中,bar指向可变长度数组,因此sizeof在运行时而不是编译时确定.
int foo = 100;
double (*bar)[foo];
Run Code Online (Sandbox Code Playgroud)
该论点是关于sizeof当操作数是可变长度数组时是否使用计算其操作数,在未初始化sizeof(*bar)时产生未定义的行为bar.
是否使用未定义的行为,sizeof(*bar)因为我正在取消引用未初始化的指针?sizeof当类型是可变长度数组时,是实际评估的操作数,还是仅确定其类型(sizeof通常如何工作)?
编辑:每个人似乎都引用了C11选秀中的这段话.有谁知道这是否是官方标准中的措辞?
有一个众所周知的模式来计算数组长度:
int arr[10];
size_t len = sizeof(arr) / sizeof(arr[0]);
assert(len == 10);
Run Code Online (Sandbox Code Playgroud)
此模式适用于静态数组和常量大小的自动数组.它也适用于C99中的可变长度数组.
我想应用类似的想法来计算动态数组大小(以字节为单位):
size_t known_len = 10;
int *ptr = malloc(known_len * sizeof(int));
size_t size = known_len * sizeof(ptr[0]);
assert(size == known_len * sizeof(int));
Run Code Online (Sandbox Code Playgroud)
这比known_len * sizeof(int)因为sizeof(ptr[0])没有引用实际的数组元素类型更好.因此,它不需要代码的读者知道类型.
然而,我不清楚表达是否sizeof(ptr[0])会导致不确定的行为.随着它扩大:
sizeof(ptr[0]) -> sizeof(*((ptr) + (0))) -> sizeof(*ptr)
Run Code Online (Sandbox Code Playgroud)
如果ptr是,结果表达式是有问题的0:
sizeof(*((int*) 0))
Run Code Online (Sandbox Code Playgroud)
根据C99标准:
(C99,6.3.2.3p3):"带有值的整型常量表达式
0,或者这种表达式转换为类型void *,称为空指针常量." 取消引用空指针是未定义的行为.(C99,6.5.3.2.p4)"如果为指针分配了无效值,则一元运算
*符的行为未定义.87)"87):"一元运算
*符解除引用指针的无效值中有一个空指针,一个与指向的对象类型不一致的地址,以及一个对象在其生命周期结束后的地址."
但是从未指明这种表达式的sizeof是否会导致未定义的行为.实际上,应该在编译时评估这样的sizeof.
我的问题是:
sizeof(ptr[0])当类型ptr已知并且值ptr …前提:
C++ 11 Standard将表达式分为三个不相交的值类别:lvalues,xvalues和prvalues(第3.10/1节).此处提供了对哪些值类别的解释.
我正在努力弄清楚不同运营商对其操作数的价值类别的要求是什么.第3.10/1段规定:
[...]每个表达式都属于此分类法中的基本分类之一:lvalue,xvalue或prvalue.表达式的此属性称为其值类别.[注意:第5章中对每个内置运算符的讨论表明了它产生的值的类别以及它所期望的操作数的值类别.例如,内置赋值运算符期望左操作数是左值,右操作数是prvalue并产生左值作为结果.用户定义的运算符是函数,它们期望和产生的值的类别由它们的参数和返回类型决定. - 尾注]
尽管上面的说明声称,第5条并不总是非常清楚运营商的操作数的价值范畴.例如,关于赋值运算符的操作数的值类别(第5.17/1段),这就是所说的:
赋值运算符(=)和复合赋值运算符都是从右到左分组.所有都需要一个可修改的左值作为左操作数,并返回一个左值操作数的左值.如果左操作数是位字段,则所有情况下的结果都是位字段.在所有情况下,在右和左操作数的值计算之后,以及在赋值表达式的值计算之前,对赋值进行排序.对于不确定序列的函数调用,复合赋值的操作是单个评估.[注意:因此,函数调用不应介入左值到右值的转换和与任何单个复合赋值运算符相关的副作用. - 尾注]
合适的操作数怎么样?
整个5.17节中不再出现"rvalue"和"lvalue"字样.虽然第3.10/1段中的注释明确指出内置赋值运算符期望将prvalue作为右操作数,但在第5.17节中没有明确提到.甚至5.17/1的最后一个注释,提到左值到右值的转换,似乎暗示rvalues是以某种方式预期的(否则转换的需要是什么?),但笔记毕竟是非规范性的.
涉及其他运算符的部分,包括乘法运算符和加法运算符,通常对其操作数的值类别保持沉默.我在标准中找不到任何"默认语句",声明如果没有另外指定,内置运算符的操作数是rvalues.因此,问题.
题:
很高兴参考C++ 11标准.
我在isocpp FAQ上阅读这个问题,这个问题是解释如何编写返回类型???
template<class T, class U>
??? mul(T x, U y)
{
return x*y;
}
Run Code Online (Sandbox Code Playgroud)
我理解简单的方法是写auto mul(T x, U y) -> decltype(x*y),但问题也提供另一种方式,即替换???为decltype(*(T*)(0)**(U*)(0)).但是我不完全理解这decltype(*(T*)(0)**(U*)(0))是做什么的,它似乎是声明一个临时指针T*并将其初始化为零然后取消引用指针,然后乘以相同的类型对应U,我的理解是对的吗?
但为什么要使用指针?我认为decltype(T(0)*U(0))或decltype(T{0}*U{0})应该也有效.
在某些情况下,请参见下面一个例子,std::is_floating_point在返回false的float.
#include <iostream>
#include <type_traits>
#include <vector>
int main()
{
::std::cout << typeid(decltype(::std::vector< float >()[::std::vector< float >().size()])).name() << ::std::endl;
if (::std::is_floating_point< decltype(::std::vector< float >()[::std::vector< float >().size()]) >::value)
{
::std::cout << "floating point" << ::std::endl;
}
else
{
::std::cout << "not floating point" << ::std::endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
GCC的输出
f
not floating point
Run Code Online (Sandbox Code Playgroud)
在这个例子中,人们可以看到它typeid被::std::vector< float >()[::std::vector< float >().size()]视为a float,它返回正确的名称.人们也可以检查typeid(decltype(::std::vector< float >()[::std::vector< float >().size()])) == typeid(flat)退货true.但是, …
#include<stdio.h>
int main()
{
printf("%d\n", sizeof(2147483648));
printf("%d" , sizeof(2147483647+1));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
8
4
Run Code Online (Sandbox Code Playgroud)
我知道这sizeof(2147483648)是8个字节,因为它不能适合4个字节并被提升为long long int.但我不明白发生了什么事情sizeof(2147483647+1)
我发现了类似的问题,但没有讨论第二种情况.
我遇到了这段代码:
void f(const std::string &s);
Run Code Online (Sandbox Code Playgroud)
然后打个电话:
f( *((std::string*)NULL) );
Run Code Online (Sandbox Code Playgroud)
我想知道其他人对这种结构的看法,它用于表示函数f()应该使用一些默认值(它计算)而不是一些用户提供的值.
我不知道该怎么想,它看起来很奇怪,但你觉得这个结构怎么样?
我的代码中有一个非常愚蠢的错字......
is.read((char*)&this->id, sizeof(this-id));
Run Code Online (Sandbox Code Playgroud)
失踪>后的性格this-
有趣的是sizeof(this - id)返回8!
我认为...因为this是一个指针,进行减法this将导致另一个指针被id的值关闭,这可能是任何取决于id值的东西.
并且......在64位系统上,指针通常是8个字节!
我对么?还是遗失了什么?
以下是我的课程.
class IndexItem : public Serializable {
public:
IndexItem(uint32_t id, std::streampos pos) :
id(id),
pos(pos)
{ }
uint32_t id;
std::streampos pos;
protected:
std::ostream& Serialize(std::ostream& os) const override {
os.write((char*)&this->id, sizeof(this->id));
os.write((char*)&this->pos, sizeof(this->pos));
return os;
}
std::istream& Deserialize(std::istream& is) override {
is.read((char*)&this->id, sizeof(this->id));
is.read((char*)&this->pos, sizeof(this->pos));
return is;
}
};
Run Code Online (Sandbox Code Playgroud)