c99与c++98的一些不兼容之处

c++真的只是增加了类的c吗?原来c99与c++98的不同点有这么多,一起来学习一下吧。
本文是一篇读书笔记。对原文的部分翻译。原文见末尾
本文原作者是David R. Tribble

注意本篇文章只讨论那些在c中有,但与c++不同的地方,c++独有而c中没有的如类,类的成员函数等不在讨论之列

c89与c++98不兼容之处

以下这些在c99中已经改为与c++98兼容了,不用在意
(这一部分内容基本都省略不在这写了,只有段落分隔符这一个从没见过,单列一下,实际上也没人这么写)

  • 段落分隔符(Digraph punctuation tokens)
    C++98和C99都 允许二字符分隔符(但在C89中不允许),对应关系如下
<:	      [
:>        ]
<%        {
%>        }
%:        #
%:%:      ##

这样一来就能写出这样的代码。。。

 	%:include <stdio.h>

    %:ifndef BUFSIZE
    %:define BUFSIZE  512
    %:endif

    void copy(char d<::>, const char s<::>, int len)
    <%
        while (len-- >= 0)
        <%
            d<:len:> = s<:len:>;
        %>
    %> 

C99 与 C++98不兼容之处

  • 可替换分隔符拼写Alternate punctuation token spellings
    c++支持以下的关键字和操作符互换,
and             &&
and_eq          &=
bitand          &
bitor           |
compl           ~
not             !
not_eq          !=
or              ||
or_eq           |=
xor             ^
xor_eq          ^=

c89中没有这些关键字, c语言通过头文件<iso646.h>进行支持,但如果没有包含<iso646.h>则可以任意使用这些名字,造成不兼容

  • 数组参数限定符Array parameter qualifiers

    c99 支持在数组参数前加类型限定符 const volatile restrict static, 此特性C++完全不支持

extern void  foo(int str[const]);  // 含义同  extern void  foo(int *const str);
void baz(char s[static 10]){
        // s[0] thru s[9] exist and are contiguous
} 
  • 布尔类型Boolean type

    c99支持_Bool关键字 还提供了stdbool.h来支持 bool true false,如果不包含stdbool.h,可任意使用这些名字,造成不兼容
    c++没有stdbool.h 且 bool true false 是关键字

  • 字符常量Character literals

c中’a’ 的类型是int, 而在c++中 类型是 char

  • clog 标识符clog identifier

c99中在math.h将其定义为 计算复数的自然对数 的函数名
c++98中在iostream 中定义了 std::clog 作为标准错误日志输出流 与 std::err类似。
(此条在vs2019中math.h中没有clog的定义,vs把clog定义在在complex.h中了, gcc 9.3中也math.h也没有clog定义了,只是需要知道,有clog这么个东西可能冲突,用到再注意就行了)

  • 逗号操作符的结果 (可以忽略)Comma operator results

在c中 逗号操作符 总是产生右值结果,在c++中如果逗号右侧是左值,则会返回左值结果
(i, j) = 1; // C++中有效, C 中无效 // 在gcc 9.3中实测全都是有效

  • 复数浮点类型Complex floating-point type

C99 中关键字_complex _Imaginary, 又有complex.h定义了complex imaginary以便使用。
C++ 的complex头文件中 定义了complex 模板
使用时可以按以下方法typedef一下这样怎么用都不会错了

#ifdef __cplusplus

     #include <complex>

     typedef complex<float>           complex_float;
     typedef complex<double>          complex_double;
     typedef complex<long double>     complex_long_double;

    #else

     #include <complex.h>

     typedef complex float            complex_float;
     typedef complex double           complex_double;
     typedef complex long double      complex_long_double;

     typedef imaginary float          imaginary_float;
     typedef imaginary double         imaginary_double;
     typedef imaginary long double    imaginary_long_double;

    #endif 
  • 复合字面量Compound literals
extern void  add(struct info s);
add((struct info){ "e", 0 }); // (struct info){ "e", 0 }就是一个复合字面量,前面是类型,后面是初始化值

c99允许此特性 c++不允许

  • const的链接const linkage

c中const变量不可改变,默认是extern的,除非指定static,即const int x = 1就等于extern const x = 1
c++ 中const变量默认是本文件可见,默认不带extern
建议是用的时候明确指定extern、 static

  • 结构体指定初始化Designated initializers

初始化结构体时可以用 .成员名字的方式初始化,否则要按声明顺序来初始化。

struct info
    {
        char    name[8+1];
        int     sz;
        int     typ;
    };

    struct info  arr[] =
    {
        [0] = { .sz = 20, .name = "abc" },   // 注意以.sz .name来直接指定初始化
        [9] = { .sz = -1, .name = "" }
    }; 

C++中不支持此特性

  • 冗余typedef Duplicate typedefs

c中不允许同样的typedef出现2次以上,c++中无此限制,
主要影响是在头文件中的typdef,如果被重复包含在c中会报错,解决方法是
#ifndef xxx #define xxx typdef int myint; #endif

  • 动态sizeof求值 Dynamic sizeof evaluation

c支持变长数组sizeof(VLA_arr)会在运行时求值,在c++中不支持变长数组sizeof需要在编译器求值sizeof(VLA_arr)会报错

  • 空参数列表Empty parameter lists

对于函数 void foo(void) C和C++都认为这是一个不接受任何参数的函数
对于函数 void foo() C中认为这是一个接受不定个数参数的函数,C++中认为这是与void foo(void)一样不接受任何参数的函数

  • 空预处理函数宏参数 Empty preprocessor function macro arguments
#define ADD3(a,b,c)  (+ a + b + c + 0)
ADD3(1, 2, 3)   => (+ 1 + 2 + 3 + 0)
    ADD3(1, 2, )    => (+ 1 + 2 + + 0)
    ADD3(1, , 3)    => (+ 1 + + 3 + 0)
    ADD3(1,,)       => (+ 1 + + + 0)
    ADD3(,,)        => (+ + + + 0) 

c99允许ADD()中的参数为空 , C++不支持这么干 (目前VS2019 GCC 9.3都支持这么干了)

  • 枚举常量Enumeration constants

C中枚举常量就是起了名的singed int变量,其值肯定在[INT_MIN, INT_MAX]中
C++ 中枚举常量类型可以为signed int, unsigned int, signed long, unsigned long.
(这里需要注意long类型在vs2019中4字节大小,gcc9.3中为8字节大小),依赖于size(enum_x)的代码将可能遇到问题

  • 枚举声明的尾部逗号 Enumeration declarations with trailing comma

enum Color { RED = 0, GREEN, BLUE, }; // BLUE后面的逗号在C++不允许
(vs2019 gcc9.3现在都允许了)

  • 枚举类型 Enumeration types

C中枚举的类型不同,C标准允许其对不同枚举使用不同的底层整数类型,即sizeof(enumA) 不一定与 sizeof(enumB)相同,但在使用时都会转为signed int所以可以不用显示转换,即

enum Color { RED, BLUE, GREEN }; // sizeof(RED) 也可能与 sizeof(Color)不同。。。
enum Color2 { RED2, BLUE2, GREEN2 };
    int         c = RED;   // 不用显示式转换
    enum Color  col = 1;  // 不用显示转换
    void func(enum Color x); // 会报冲突
    void func(enum Color2 x);// 会报冲突

c++中对类型区分更细,上面的func能正常编译,但枚举与整数之间需要显示转换

enum Color { RED, BLUE, GREEN };// sizeof(RED) 与 sizeof(Color)相同
enum Color2 { RED2, BLUE2, GREEN2 };
    int         c = (int)RED;   // 要显示式转换
    enum Color  col = (Color)1;  // 要显示转换
    void func(enum Color x); // ok
    void func(enum Color2 x);//ok
  • 柔性数组成员Flexible array members (FAMs)

c语言结构体后面可以跟一个不定长数组,用来传一些东西,C++不支持这么做

struct Hack
    {
        int     count;    // Fixed member(s)
        int     fam[];    // Flexible array member
    };

    struct Hack * vmake(int sz)
    {
        struct Hack *  p;

        p = malloc(sizeof(struct Hack) + sz*sizeof(int));    // 分配一个hack和一个整数数组的内存

        p->count = sz;
        for (int i = 0; i < sz; i++)
            p->fam[i] = i; // 注意这里的使用方式

        return p;
    }
  • 函数名字修饰 Function name mangling

编译时编译器会根据函数名字 参数 返回值等信息 生成唯一的名字,以便后续链接等使用,而这个名字的生成C和C++采用的策略不同。所以如果混用,会发生符号链接失败的情况。解决方案是正确使用extern “C”

而且c++mangling时会用到两个下划线,故不允许函数名字中连续两个下划线(vs2019 gcc9.3 无此限制)

  • 函数指针Function pointers

C++函数默认都是extern "c++"链接,使用C函数时必须extern “C”,但如果在C++中定义了指向函数的指针,此指针不能指向extern "C"的函数,
如果想用指向C函数的指针,需要把指针也extern “C”

  • 16进制浮点字面量 Hexadecimal floating-point literals
float  pi = 0x3.243F6A88p+03; 
printf("%9.3a", f);
printf("%12.6lA", d); 

c++中不太支持这些写法 (vs2019 gcc9.3均支持)

  • 算术支持 IEC 60559 arithmetic support

C99允许编译器提供预定义__STD_IEC_559 __STD_IEC_559_COMPLEX,以标示其对IEC 60559的支持。(vs2019 gcc9.3好像还没有这个预定义)

  • 内联函数Inline functions

C++会要求内联函数的定义完全一致,而C不会这样要求。
如果在两个c文件中定义同的内联函数不一样如inline int func(int)和inline myint func(myint),C++会报错,但实际使用中内联函数一般都在头文件中,不会有此问题。

  • 整形头文件Integer types headers

c99提供了<stdint.h> 定义了一些标准整数的宏定义,C++没有此头文件

  • 库函数原型 Library function prototypes

C++库头文件会把C里的一些函数改为类型安全的函数,可能会造成报错

// <string.h> c
    extern char *   strchr(const char *s, int c); 
    // <cstring> c++
    extern const char * strchr(const char *s, int c);
    extern char *       strchr(char *s, int c); 

在使用strchr时若想在c和c++中均正确可以加上类型强制转换char * p = (char *) strchr(s, ‘a’);

  • 库头文件Library header files

C++中包含了C89的头文件,但基本也提供了对应的C++版本头文件,如math.h --> cmath
c99增加了<complex.h><fenv.h><inttypes.h><stdbool.h><stdint.h><tgmath.h>c++并不一定包含这些头文件

  • long long integer type

c99增加了signed long long 和unsigned long long类型,并且也支持如下写法

long long int           i = -9000000000000000000LL;
    unsigned long long int  u = 18000000000000000000LLU; 
    printf("%lld", i);

c++ 不支持这些类型 (vs2019 gcc9.3已经支持了)

  • Nested structure tags
    C中如果在结构体内部声明其它结构、枚举,则其在结构体外也可见。C++内部声明只在内部可见

  • 非原型函数声明 Non-prototype function declarations

C语言还支持K&R式函数声明 int foo(a,b) int a; int b; { return a + b;}
C++不提供此种支持

  • 旧式类型转换Old-style casts

C++ 最好使用const_cast dynamic_cast reinterpret_cast static_cast,C++还支持函数式的类型转换float f = float(i),C语言不支持这些

  • 一个定义原则One definition rule

C语言允许尝试性定义,C++不允许

int i;
int i=3;

不同源文件之中的函数、结构体定义,C语言不会检查是否一致,C++要求必须完全一致(与前面inline function相同)

  • _Pragma关键字_Pragma keyword
    C99 支持_Pragma 关键字_Pragma(FLT_XXX) 与#pragma FLT_XXX相同

  • 预定义标识符 Predefined identifiers
    c99 支持__func__ ,C++不一定提供,且若在内部命名空间中的内部模板类的成员函数中(member functions within nested template classes declared within nested namespaces)使用时,其值不知是什么。

  • C99保留关键字Reserved keywords in C99

c99有几个特殊保留关键字restrict _Bool _Complex _Imaginary _Pragma c++可能不认识
extern int set_name(char *restrict n); 编译含有这些关键字代码时可能会报错

  • C++保留关键字 Reserved keywords in C++
bool			mutable		this
catch			namespace	throw
class			new			true
const_cast		operator	try
delete			private	    typeid
dynamic_cast	protected	typename
explicit		public		using
export			reinterpret_cast	virtual
false			static_cast	wchar_t
friend			template

以及c++保留asm关键字。C代码中如果使用了这些关键字,使用C++编译就会报错

  • restrict 关键字restrict keyword

C99支持restrict 允许对指针的特定优化,C++不认识此关键字

  • 返回void Returning void
    C++ 允许返回void型表达式,而在C中标识了返回值void就不允许返回任何东西

  • static的链接static linkage
    C和C++中static的链接都是internal linkage,但在c++中推荐把代码放在匿名空间中以达到相同效果

  • 字符串文本是const String literals are const
    c中字符串文档类型是 char[n] 但不能修改 c++中类型是const char[n],把字符串文本当参数传给函数时,C中转换为char* C++中转换为const char* ,

extern void  frob(char* s);// 

void foo(void)
{
	frob("abc");  // 在c++中这里会报错 (vs2019 gcc9.3)
}
  • 函数原型中声明的结构Structures declared in function prototypes

c 允许在函数原型中声明struct union enum 如
extern void foo(const struct info { int typ; int sz; } *s);
也允许返回值中声明struct 如
extern struct pt { int x; } pos(void);
但C++不允许这样。可以将以上几个声明改成一个struct指针,这样C和C++都支持

  • 通用类型数学函数Type-generic math functions

C99通过tgmath.h支持通用的数学函数如 sin(x) 实际对应的 sin(float x) sin(double x) sin(long double x)
sin(float complex x) sin(double complex x) sin(long double complex x)
C++也可能提供了,但需要注意 指向这些通用函数的指针 应会有不同的表现

  • typedef与类型tag Typedefs versus type tags

C的type tags需要和前面的strut union enum组合在一起才有效
C++ 将type tags当成隐式的typdef ,所以会有以下情况

// 这段代码C中能编译 c++中编译不过 (gcc9.3确实如此

    typedef int  type;

    struct type
    {
        type            memb;   // int
        struct type *   next;   // struct pointer
    };

    void foo(type t, int i)
    {
        int          type;
        struct type  s;

        type = i + t + sizeof(type);
        s.memb = type;
    } 

下面这段代码在c c++中都能编译但结果不同

int  sz = 80;

    int size(void)
    {
        struct sz
        { ... };

        return sizeof(sz);      // sizeof(int) in C,
                                // sizeof(struct sz) in C++
    } 
  • 变长参数函数声明Variable-argument function declarators
    C++和C都支持变长参数函数声明 但C++可以省略…前面的逗号
    int foo(int a, int b, …); // Valid C++ and C
    int bar(int a, int b …); // Valid C++, invalid C

  • 变长参数预处理函数宏 Variable-argument preprocessor function macros

C99支持宏中的…,C++不支持 (gcc 9.3已经支持了)

#define DEBUGF(f,...) \
        (fprintf(dbgf, "%s(): ", f), fprintf(dbgf, __VA_ARGS__))

    #define DEBUGL(...) \
        fprintf(dbgf, __VA_ARGS__)

    int incr(int *a)
    {
        DEBUGF("incr", "before: a=%d\n", *a);
        (*a)++;
        DEBUGL("after: a=%d\n", *a);
        return (*a);
    } 
  • 变长数组 Variable-length arrays (VLAs)
    c99支持变长数组 extern float sum_square(int n, float a[*]); float sum_cube(int n, float a[m])
    注意的是 * 只允许出现在函数声明中 不允许定义中
    C++ 并不支持VLAs

  • void指针赋值 Void pointer assignments

C允许void直接赋给其它 指针,不需要类型转换
c++ void
必须强制类型转换才能赋给其它指针。
但将一个指针 赋值给void* p,在C和C++中都不需要强转

  • 宽字符类型Wide character type
    c提供了wchar_t作为宽字符类型,它在<stddef.h>, <stdlib.h>, and <wchar.h>被定义
    c++则是将wchar_t作为一个保留关键字,不需要头文件的支持

原文地址经常打不开,可能需要翻墙。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值