在每种语言中编译时,C和C++中有效的代码是否会产生不同的行为?

Meh*_*dad 653 c c++

C和C++有许多不同之处,并非所有有效的C代码都是有效的C++代码.
("有效"是指具有已定义行为的标准代码,即不是特定于实现/未定义/等.)

在使用每种语言的标准编译器编译时,是否有任何一种在C和C++中都有效的代码会产生不同的行为?

为了使它成为一个合理/有用的比较(我试图学习一些实用的东西,而不是试图在问题中找到明显的漏洞),让我们假设:

  • 没有任何预处理器相关(这意味着没有hacks #ifdef __cplusplus,pragma等)
  • 任何实现定义在两种语言中都是相同的(例如数字限制等)
  • 我们正在比较每个标准的合理最新版本(例如,比如C++ 98和C90或更高版本)
    如果版本很重要,那么请提及每个版本的哪些版本产生不同的行为.

Set*_*gie 459

下面是一个利用C和C++中函数调用和对象声明之间差异的示例,以及C90允许调用未声明函数的事实:

#include <stdio.h>

struct f { int x; };

int main() {
    f();
}

int f() {
    return printf("hello");
}
Run Code Online (Sandbox Code Playgroud)

在C++中,这将不会打印任何内容,因为临时f创建和销毁,但在C90中它将打印,hello因为可以在未声明的情况下调用函数.

如果您想知道f两次使用的名称,C和C++标准明确允许这样做,并制作一个对象,您必须说明struct f是否需要结构消除歧义,或者struct如果您想要该功能则不要使用.

  • 允许隐式声明C函数中的@Sogartar. (21认同)
  • @Sogartar,真的吗?http://codepad.org/STSQlUhh C99编译器会给你一个警告,但他们仍然会让你编译它. (14认同)
  • @AlexB不在C99和C11中. (9认同)
  • 严格来说,在C下这将无法编译,因为"int f()"的声明是在"int main()"的定义之后:) (7认同)
  • @jrajav那些不是C99编译器.C99编译器将未声明的标识符检测为语法错误.不这样做的编译器是C89编译器,或者是预标准或其他类型的不符合的编译器. (4认同)
  • @ user529758-他们很可能是C99编译器。需要一个检测未声明标识符的C99来将其视为语法错误,并且需要“发出诊断信息”;*不需要*来编译文件。 (3认同)
  • @ H2CO3 OP提到C90是可接受的基线,所以它仍然有效.此外,C89仍然普遍存在. (2认同)

Jer*_*fin 420

对于C++与C90,至少有一种方法可以获得不是实现定义的不同行为.C90没有单行注释.稍加注意,我们可以使用它来创建一个在C90和C++中具有完全不同结果的表达式.

int a = 10 //* comment */ 2 
        + 3;
Run Code Online (Sandbox Code Playgroud)

在C++中,从行//到结尾的所有内容都是注释,因此这可以解释为:

int a = 10 + 3;
Run Code Online (Sandbox Code Playgroud)

由于C90没有单行注释,因此只有/* comment */注释.第一个/和第二个2都是初始化的部分,所以它出现在:

int a = 10 / 2 + 3;
Run Code Online (Sandbox Code Playgroud)

所以,一个正确的C++编译器会给出13,但是一个严格正确的C90编译器8.当然,我只是在这里选择了任意数字 - 你可以根据需要使用其他数字.

  • 即使没有`2`,它也会读作有效的(10/+ 3)(一元+). (88认同)
  • WHOA这是令人兴奋的!! 在我可能永远不会想到的所有可能的事情中,可以使用评论来改变行为哈哈.+1 (33认同)
  • @RyanThompson琐事.S/2/1 / (21认同)
  • 现在为了好玩,修改它以便C和C++都计算出不同的算术表达式,并将其评估为相同的结果. (12认同)
  • @Mehrdad我错了还是评论与预处理器相关?因此,应将它们排除在您的问题的可能答案之外!;-) (4认同)

Ale*_*nze 385

以下内容在C和C++中有效(最有可能)导致iC和C++中的值不同:

int i = sizeof('a');
Run Code Online (Sandbox Code Playgroud)

有关差异的解释,请参阅C/C++中的字符大小('a').

本文的一篇文章:

#include <stdio.h>

int  sz = 80;

int main(void)
{
    struct sz { char c; };

    int val = sizeof(sz);      // sizeof(int) in C,
                               // sizeof(struct sz) in C++
    printf("%d\n", val);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • +1第二个例子是一个很好的例子,因为C++在结构名称之前不需要`struct`. (16认同)
  • 绝对不期待这个!我希望有一些更具戏剧性的东西,但这仍然有用,谢谢.:) +1 (8认同)
  • @SethCarnegie:一个不合格的程序不需要失败,但它也不能保证工作. (3认同)
  • `struct sz {int i [2];};`意味着C和C++*有*生成不同的值.(而具有sizeof(int)== 1的DSP,*可以*产生相同的值). (3认同)

det*_*zed 172

C90 vs. C++ 11(intvs. double):

#include <stdio.h>

int main()
{
  auto j = 1.5;
  printf("%d", (int)sizeof(j));
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

在C中auto表示局部变量.在C90中,可以省略变量或函数类型.它默认为int.在C++ 11中auto意味着完全不同的东西,它告诉编译器从用于初始化它的值推断变量的类型.

  • @KeithThompson啊,我猜你的意思是推断的`int`.尽管如此,在现实世界中,有大量遗留代码并且市场领导者仍未实施C99并且无意这样做,所以谈论"过时版本的C"是荒谬的. (22认同)
  • @SethCarnegie:是的,这是一个存储类; 当你省略它时默认会发生什么,所以没有人使用它,并且它们改变了它的含义.我认为默认情况下它是`int`.这很聪明!+1 (19认同)
  • C90有`auto`? (9认同)
  • "每个变量都必须有一个明确的存储类.你真正的,高层管理人员." (8认同)
  • C11没有implicit -`int`. (5认同)
  • @KeithThompson呃?C11仍然有自动,自其构思以来一直在C中. (4认同)
  • 有人可能会说这不是一个有效的比较; 您将过时的C版本与当前版本的C++进行比较. (3认同)

god*_*eek 115

另一个我还没有提到过的例子,这个例子突出了预处理器的区别:

#include <stdio.h>
int main()
{
#if true
    printf("true!\n");
#else
    printf("false!\n");
#endif
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这在C中打印"false",在C++中打印"true" - 在C中,任何未定义的宏的计算结果为0.在C++中,有1个例外:"true"计算结果为1.

  • `#define truefalse`ಠ_ಠ (21认同)
  • @DarioOO:是的,你错了.不允许重新定义关键词,惩罚留给命运(UB).预处理器是一个独立的编译阶段,不能承受. (3认同)
  • 有趣.有谁知道这种变化背后的理由? (2认同)
  • 因为"true"是关键字/有效值,所以它被评估为真,就像任何"真值"一样(所以像任何正整数一样).你仍然可以#define true false在C++中打印"false";) (2认同)
  • @DarioOO不会在UB中重新定义这样的结果吗? (2认同)

Kir*_*lev 105

每个C++ 11标准:

一个.逗号运算符在C中执行左值到右值的转换但不是C++:

   char arr[100];
   int s = sizeof(0, arr);       // The comma operator is used.
Run Code Online (Sandbox Code Playgroud)

在C++中,此表达式的值将为100,而在C中,这将是sizeof(char*).

在C++中,枚举器的类型是它的枚举.在C中,枚举器的类型是int.

   enum E { a, b, c };
   sizeof(a) == sizeof(int);     // In C
   sizeof(a) == sizeof(E);       // In C++
Run Code Online (Sandbox Code Playgroud)

这意味着sizeof(int)可能不等于sizeof(E).

C.在C++中,使用空参数列表声明的函数不带参数.在C空参数列表中,表示函数参数的数量和类型是未知的.

   int f();           // int f(void) in C++
                      // int f(*unknown*) in C
Run Code Online (Sandbox Code Playgroud)

  • 有一个简单的解决方法 - 只需将示例更改为:`char arr [sizeof(char*)+ 1]; int s = sizeof(0,arr);` (3认同)
  • 为了避免实现定义的差异,您还可以使用`void*arr [100]`.在这种情况下,元素的大小与指向同一元素的指针的大小相同,因此只要有2个或更多元素,该数组必须大于其第一个元素的地址. (3认同)

Pav*_*aev 51

该程序1以C++和0C 格式打印:

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

int main(void)
{
    int d = (int)(abs(0.6) + 0.5);
    printf("%d", d);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

发生这种情况是因为double abs(double)C++中存在重载,因此在C中abs(0.6)返回0.6时返回,0因为在调用之前隐式的double-int转换int abs(int).在C中,您必须使用fabsdouble.

  • 不得不调试其他人的代码与该问题.哦我多么喜欢那个.无论如何,你的程序也在C++中打印0.C++必须使用标题"cmath"看比较第一个returnin 0 http://ideone.com/0tQB2G第二个返回1 http://ideone.com/SLeANo (5认同)
  • 这个特定的代码示例依赖于实现:`stdlib.h` 只定义了 `abs(int)` 和 `abs(long)`;`abs(double)` 版本由 `math.h` 声明。所以这个程序可能还是会调用 `abs(int)` 版本。`stdlib.h` 是否也导致包含 `math.h` 是一个实现细节。(我认为如果调用 `abs(double)` 会是一个错误,但不包括 `math.h` 的其他方面)。 (3认同)

Ale*_*x B 37

另一个sizeof陷阱:布尔表达式.

#include <stdio.h>
int main() {
    printf("%d\n", (int)sizeof !0);
}
Run Code Online (Sandbox Code Playgroud)

它等于sizeof(int)C,因为表达式是类型的int,但在C++中通常是1(尽管不是必须的).在实践中,它们几乎总是不同的.

  • 一个`!`应该足够一个`bool`. (6认同)
  • !是布尔转换运算符的int :) (4认同)

Ada*_*eld 36

#include <stdio.h>

int main(void)
{
    printf("%d\n", (int)sizeof('a'));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在C中,这将打印sizeof(int)当前系统上的任何值,这通常4在当今通常使用的大多数系统中.

在C++中,必须打印1.

  • 在C中,值是实现定义的,1是可能的.(在C++中,它必须按照规定打印1.) (13认同)
  • 这将成为一个有趣的面试问题 - 特别是对于那些将c/c ++专家放在他们的简历上的人 (9认同)
  • 是的,我实际上熟悉这个技巧,因为'c'是C中的int,而C++中是char,但是将它列在这里仍然很好. (3认同)
  • 实际上它在两种情况下都有不确定的行为.`%d`不是`size_t`的正确格式说明符. (3认同)
  • 虽然有点卑鄙.sizeof的全部目的是让您无需确切知道类型的大小. (2认同)
  • @R..:很公平,但这就是这个例子的重点。我对其进行了修改,这样情况就不再是这样,并且它也是有效的 C89,因为 C89 没有“z”长度修饰符。 (2认同)

der*_*ert 25

C++编程语言(第3版)给出了三个例子:

  1. sizeof('a'),正如@Adam Rosenfield所说;

  2. // 用于创建隐藏代码的注释:

    int f(int a, int b)
    {
        return a //* blah */ b
            ;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 结构等将东西隐藏在范围内,如您的示例所示.


dmc*_*kee 24

一个依赖于C编译器的旧栗子,没有识别C++行尾注释......

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...
Run Code Online (Sandbox Code Playgroud)


Joh*_*itb 20

C++标准列出的另一个:

#include <stdio.h>

int x[1];
int main(void) {
    struct x { int a[2]; };
    /* size of the array in C */
    /* size of the struct in C++ */
    printf("%d\n", (int)sizeof(x)); 
}
Run Code Online (Sandbox Code Playgroud)


fkl*_*fkl 20

C中的内联函数默认为外部作用域,而C++中的内联函数则不行.

将以下两个文件一起编译将在GNU C的情况下打印"我是内联"但对C++没有任何内容.

档案1

#include <stdio.h>

struct fun{};

int main()
{
    fun();  // In C, this calls the inline function from file 2 where as in C++
            // this would create a variable of struct fun
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

档案2

#include <stdio.h>
inline void fun(void)
{
    printf("I am inline\n");
} 
Run Code Online (Sandbox Code Playgroud)

此外,C++隐式地将任何const全局视为static除非显式声明extern,不像C extern是默认值.

  • @fayyazkl显示的行为只是因为查找的差异(`struct fun` vs`fn`)而且函数是否内联无关.如果删除`inline`限定符,结果是相同的. (10认同)
  • 在ISO C中,这个程序生成错误:`inline`直到C99才被添加,但在C99中,如果没有原型范围,可能无法调用`fun()`.所以我认为这个答案只适用于GNU C. (3认同)
  • 这里演示了内联函数和`extern`的不同行为是什么? (2认同)

小智 16

struct abort
{
    int x;
};

int main()
{
    abort();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

返回C++中的退出代码为0或C中的3.

这个技巧可能会用来做一些更有趣的事情,但是我想不出一个创建一个适合C的构造函数的好方法.我尝试用复制构造函数创建一个类似无聊的例子,这会让一个参数通过,虽然是一种相当不便携的方式:

struct exit
{
    int x;
};

int main()
{
    struct exit code;
    code.x=1;

    exit(code);

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

然而,VC++ 2005拒绝在C++模式下编译它,抱怨重新定义了"退出代码".(我认为这是一个编译器错误,除非我突然忘记了如何编程.)当编译为C时,退出时进程退出代码为1.


wef*_*fa3 15

#include <stdio.h>

struct A {
    double a[32];
};

int main() {
    struct B {
        struct A {
            short a, b;
        } a;
    };
    printf("%d\n", sizeof(struct A));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当使用C++编译器编译时以及使用C编译器编译时,此程序打印128(32 * sizeof(double))4.

这是因为C没有范围解析的概念.在C结构中包含的其他结构被置于外部结构的范围内.

  • 请注意,您通过[打印`size_t`并使用'%d`]获得UB(/sf/ask/65806121/) (3认同)

小智 7

不要忘记C和C++全局命名空间之间的区别.假设你有一个foo.cpp

#include <cstdio>

void foo(int r)
{
  printf("I am C++\n");
}
Run Code Online (Sandbox Code Playgroud)

和一个foo2.c

#include <stdio.h>

void foo(int r)
{
  printf("I am C\n");
}
Run Code Online (Sandbox Code Playgroud)

现在假设你有一个main.cmain.cpp,它们看起来像这样:

extern void foo(int);

int main(void)
{
  foo(1);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

当编译为C++时,它将使用C++全局命名空间中的符号; 在C中它将使用C一:

$ diff main.cpp main.c
$ gcc -o test main.cpp foo.cpp foo2.c
$ ./test 
I am C++
$ gcc -o test main.c foo.cpp foo2.c
$ ./test 
I am C
Run Code Online (Sandbox Code Playgroud)

  • 这是未定义的行为(`foo`的多重定义).没有单独的"全局命名空间". (4认同)

归档时间:

查看次数:

42791 次

最近记录:

6 年,10 月 前