面试题集 - 在C和C++中都有效的代码在每种语言中编译时会产生不同的行为?




腾讯c++面试题 (11)

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

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

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

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

C90与C ++ 11( intdouble ):

#include <stdio.h>

int main()
{
  auto j = 1.5;
  printf("%d", (int)sizeof(j));
  return 0;
}

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


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;
}

文件2

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

另外,除非明确声明为extern ,否则C ++隐式地将任何const全局视为static ,不像C是extern是默认的。


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

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...

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

#include <stdio.h>

struct f { int x; };

int main() {
    f();
}

int f() {
    return printf("hello");
}

在C ++中,这将不会打印任何内容,因为创建并销毁临时文件f ,但在C90中,它会打印出hello因为函数可以在没有声明的情况下被调用。

如果您想知道名称f被使用了两次,C和C ++标准明确允许这样做,并且要创建一个对象,您必须说struct f以消除如果您需要该结构,或者如果您希望函数。


以下在C和C ++中有效,最有可能会导致C和C ++中的i值不同:

int i = sizeof('a');

请参阅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;
}

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

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

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


另一个由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)); 
}

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

int a = 10 //* comment */ 2 
        + 3;

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

int a = 10 + 3;

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

int a = 10 / 2 + 3;

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


该程序在C ++中打印1在C中打印0

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

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

发生这种情况是因为在C ++中有double abs(double)重载,所以abs(0.6)返回0.6而在C中它返回0因为在调用int abs(int)之前进行隐式double-to-int转换。 在C中,你必须使用fabs来处理double


#include <stdio.h>

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

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

在C ++中,这必须打印1。


struct abort
{
    int x;
};

int main()
{
    abort();
    return 0;
}

在C ++中返回退出代码为0,或者在C中退出3。

这个技巧可能可以用来做更有趣的事情,但是我想不出创建一个可以让C友好的构造函数的好方法。我试着用复制构造函数做一个类似无聊的例子,它会让参数虽然以非常便携的方式通过:

struct exit
{
    int x;
};

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

    exit(code);

    return 0;
}

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







c