Tag: language lawyer

C中的隐含声明

以下程序是否在C中调用Undefined Behaviour ? int main() { printf(“Printf asking: Where is my declaration ?”); } 在上面的程序中有一个printf()的隐式声明,上面的代码是完全符合标准的,还是只有一些特定于实现的行为?

关于错误的atoi()的行为,我可以假设什么?

标准C库函数atoi在ISO 9899:2011中记录为: 7.22.1数字转换函数 1 atof , atoi , atol和atoll函数不需要影响错误的整数表达式errno的值。 如果无法表示结果的值,则行为未定义。 … 7.22.1.2 atoi , atol和atollfunction 概要 #include int atoi(const char *nptr); long int atol(const char *nptr); long long int atoll(const char *nptr); 描述 2 atoi , atol和atoll函数分别将nptr的字符串的初始部分转换为int , long int和long long int表示。 除了出错的行为,它们相当于 atoi: (int)strtol(nptr, (char **)NULL, 10) atol: strtol(nptr, (char **)NULL, 10) atoll: strtoll(nptr, […]

是UB访问一个二维数组行结束的元素吗?

以下程序的行为是否未定义? #include int main(void) { int arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; int *ptr1 = &arr[0][0]; // pointer to first elem of { 1, 2, 3 } int *ptr3 = ptr1 + 2; // pointer to last elem of { 1, 2, 3 } int *ptr3_plus_1 = […]

内存映射文件和指向易失性对象的指针

我对C和C ++中volatile的语义的理解是它将内存访问转化为(可观察的)副作用 。 每当读取或写入内存映射文件(或共享内存)时,我都希望指针是volatile限定的,以表明这实际上是I / O. (John Regehr写了一篇关于volatile的语义的非常好的文章 )。 此外,我希望使用像memcpy()这样的函数来访问共享内存是不正确的,因为签名表明易失性限定被丢弃,并且内存访问不被视为I / O. 在我看来,这是一个支持std::copy() ,其中volatile限定符不会被丢弃,并且内存访问被正确地视为I / O. 但是,我使用指向volatile对象的指针和std::copy()访问内存映射文件的经验是,它比使用memcpy()要慢memcpy()数量级。 我很想得出结论,或许clang和海湾合作委员会在处理volatile上过于保守。 是这样的吗? 关于volatile访问共享内存有什么指导,如果我想遵循标准的字母并让它依赖我依赖的语义? 标准[intro.execution]§14的相关引用: 读取由volatile glvalue指定的对象,修改对象,调用库I / O函数或调用执行任何这些操作的函数都是副作用,这些都是执行环境状态的变化。 表达式(或子表达式)的评估通常包括值计算(包括确定用于glvalue评估的对象的身份以及获取先前分配给用于prvalue评估的对象的值)和启动副作用。 当对库I / O函数的调用返回或通过volatile glvalue进行访问时,即使调用所隐含的某些外部操作(例如I / O本身)或易失性访问,也会认为副作用已完成可能尚未完成。

C标准是否要求平台不得定义超出标准规定的行为

C标准明确表示允许编译器/库组合使用以下代码执行任何操作: int doubleFree(char *p) { int temp = *p; free(p); free(p); return temp; } 但是,如果编译器不需要使用特定的捆绑库,那么C标准中是否有任何禁止库定义有意义行为的内容? 举一个简单的例子,假设代码是为具有引用计数指针的平台编写的,例如在p = malloc(1234); __addref(p); __addref(p); p = malloc(1234); __addref(p); __addref(p); 前两次调用free(p)会减少计数器但不释放内存。 编写用于此类库的任何代码自然只能用于这样的库(并且__addref()调用可能会在大多数其他库中失败),但是在许多情况下这样的特性可能会有所帮助,例如,有必要通过一个字符串重复到一个方法,该方法希望给出一个用strdup生成的字符串,因此在其上调用free 。 如果库会为某些操作定义一个有用的行为,例如双重释放指针,那么C标准中是否有任何内容可以授权编译器单方面破坏它?

是否可以将int **和const int **替换为别名?

我的理解是这样的事情是可以的: const int ci = 42; const int *cip = &ci; int *ip = (int *)cip; int j = *ip; 那这个呢? const int ci = 42; const int *cip = &ci; const int **cipp = &cip; int **ipp = (int **)cipp; int j = **ipp;

可以保证UB在编译时被拒绝吗?

考虑这个程序: #include int main(void) { int x; while ( 1 == scanf(“%d”, &x) ) printf(“%c\n”, “hello”[x]); } 编译器必须成功编译,因为程序没有UB,只要用户不输入0范围之外的任何数字。 但是,根据这个线程, UB可以及时返回。 现在考虑这个程序: int main(void) { printf(“hello\n”); “hello”[6]; } 对此程序的任何调用都会导致未定义的行为,并且因为这可能是时间旅行,所以此程序在任何调用中的整个行为都是未定义的。 因此,编译器可以拒绝该程序而不生成可执行文件吗? (我们可能会说UB回到编译阶段!)

为什么主编译器对stdint.h使用typedef,而对stdbool.h使用#define?

我只是注意到gcc和clang似乎都对stdint.h使用typedef,而对stdbool.h使用#define。 例如: clang的stdint.h #ifdef __INT8_TYPE__ #ifndef __int8_t_defined /* glibc sys/types.h also defines int8_t*/ typedef __INT8_TYPE__ int8_t; #endif /* __int8_t_defined */ typedef __UINT8_TYPE__ uint8_t; # define __int_least8_t int8_t # define __uint_least8_t uint8_t #endif /* __INT8_TYPE__ */ clang的stdbool.h #ifndef __cplusplus #define bool _Bool #define true 1 #define false 0 #elif defined(__GNUC__) && !defined(__STRICT_ANSI__) /* Define _Bool, bool, […]

通过在作为另一个结构的一个结构而不是第一个成员的结构之间转换指针来实现C中的inheritance是否合法?

现在我知道我可以通过将指向struct的指针强制转换为该struct的第一个成员的类型来实现inheritance。 然而,纯粹作为一种学习经验,我开始想知道是否有可能以稍微不同的方式实现inheritance。 这段代码合法吗? #include #include struct base { double some; char space_for_subclasses[]; }; struct derived { double some; int value; }; int main(void) { struct base *b = malloc(sizeof(struct derived)); b->some = 123.456; struct derived *d = (struct derived*)(b); d->value = 4; struct base *bb = (struct base*)(d); printf(“%f\t%f\t%d\n”, d->some, bb->some, d->value); return 0; } […]

这会避免UB吗?

这个问题更像是一个学术问题,因为没有正当理由再编写自己的offsetof宏了。 不过,我已经在这里和那里看到了这个本土实现的弹出窗口: #define offsetof(s, m) ((size_t) &(((s *)0)->m)) 从技术上讲,这是取消引用NULL指针(AFAIKT): C11(ISO / IEC 9899:201x)§6.3.2.3指针第3节 值为0的整型常量表达式或类型为void *的表达式称为空指针常量 所以上面的实现是根据我阅读标准的方式,与写作相同: #define offsetof(s, m) ((size_t) &(((s *)NULL)->m)) 它确实让我想知道,通过改变一个微小的细节,下面的offsetof定义将是完全合法的,可靠的: #define offsetof(s, m) (((size_t)&(((s *) 1)->m)) – 1) 看起来,而不是0,1用作指针,我在结尾处减去1,结果应该是相同的。 我不再使用NULL指针了。 据我所知,结果是一样的 。 所以基本上:有没有理由在这个offsetof定义中使用1代替0可能不起作用? 在某些情况下它是否仍会导致UB,如果是这样的话:何时以及如何? 基本上,我在这里问的是: 我在这里遗漏了什么吗?