MISRA C:2012 8 Rules 8.1 A Standard C environment (Part 2) 0709 0801 0803 0812 3237 inline

0562 赋值的右操作数指向更严格的类型

On the other hand, it is perfectly legitimate(合法的) to initialize an object of type "const TYPE *" or "volatile TYPE *" or even "const volatile TYPE *" with an expression of type "TYPE *".

const int * a;   指向的内容是常量,指针本身可以变

int *b;

*b = 6;

a = b ;  合法, 以后不能通过指针a 修改,a所指向的内容

b = a; 不合法,b所指向的值可以随便改,与a的定义相悖。

extern int                *gpi;
extern const int          *gpci;
extern volatile int       *gpvi;
extern const volatile int *gpcvi;

void test(void)
{
    int                *xpi;
    const int          *xpci;
    volatile int       *xpvi;
    const volatile int *xpcvi;

    xpi = gpi;                /*              */
    xpi = gpci;               /* Message 0562 */
    xpi = gpvi;               /* Message 0562 */
    xpi = gpcvi;              /* Message 0562 */

    xpci = gpi;               /*              */
    xpci = gpci;              /*              */
    xpci = gpvi;              /* Message 0562 */
    xpci = gpcvi;             /* Message 0562 */

    xpvi = gpi;               /*              */
    xpvi = gpci;              /* Message 0562 */
    xpvi = gpvi;              /*              */
    xpvi = gpcvi;             /* Message 0562 */

    xpcvi = gpi;              /*              */
    xpcvi = gpci;             /*              */
    xpcvi = gpvi;             /*              */
    xpcvi = gpcvi;            /*              */
}

0588 位域宽度必须是整数常量表达式。

#define MAX 3.2f
#define X_WIDTH (int)MAX
#define Y_WIDTH (int)(1.9f * MAX)

struct bits
{
   unsigned int bx:X_WIDTH;     /* OK           */
   unsigned int by:Y_WIDTH;     /* Message 0588 浮点数 错误 */
};

0589 枚举常量必须是整数常量表达式。

#define MAX 3.2f
#define X (int)MAX
#define Y (int)(1.9f * MAX)

enum colours
{
    RED = X,
    YELLOW = Y,          /* Message 0589 浮点数 错误*/
    GREEN,
    BLUE
};

 

0591 A 'case' label must be an integral constant expression(整数常量表达式)

Integral constant expressions:

... are used in case expressions, to specify array bounds, to specify the size of a bit-field and as enumerator constants.

They may contain:

  • Integral constants  整型常数
  • Enum constants    枚举常数
  • Character constants  字符常数
  • sizeof expressions   sizeof 表达式
  • Floating constants cast to an integer type  浮点型强转成整形

... but may not contain (except as an argument to the sizeof operator):

  • Arrays
  • * operator
  • & operator
  • Structure member operations
#define A 1
#define B (int)2.
#define C 3.5
#define D (int)(2.2 * 2.5)

void foo(int n)
{
   switch(n)
   {
   case A:           /* OK: integer constant */
      break;
   case B:           /* OK: float cast to int */
      break;
   case C:           /* Message 0591: float expression  浮点数不可以 */
      break;
   case D:           /* Message 0591: not an integral constant expression 不是整数常量表达式 */
      break;
   default:
      break;
   }
}

 

https://blog.csdn.net/ishellhub/article/details/86285957

switch在C语言中的语法如下

switch(expression) {

   case constant-expression  :
      statement(s);
      break; /* optional */

   case constant-expression  :
      statement(s);
      break; /* optional */

   /* you can have any number of case statements */
   default : /* Optional */
   statement(s);
}

switch语句中使用的表达式必须具是int或enum类型,否则如float等其他数据类型是无法通过的编译的,因为编译器需要switch后面的语句和case后面的值精确匹配,而计算机无法精确表达一个float数据类型
switch可以任意个case语句(包括没有), 值和语句之间使用:分隔
case后面的值必须是int常量值,或者返回结果为int类型的表达式,以下代码无法编译通过

当switch后面的变量值和case后面的常量值匹配相等后,case后面的代码将会被执行,直到break语句被执行后跳出switch代码块

break不是必须的,如果没有break,则执行完当前case的代码块后会继续执行后面case代码块的内容,直到执行break才可以退出

switch有一个默认的情况,我们用default关键词表示,当switch后面的变量和所有case后面的常量都不匹配的情况下,默认执行default后面的语句

/* example 1 */
#include <stdio.h> int main () { /* local variable definition */ char grade; scanf("%c", &grade); switch(grade) { case 'A' : /* char与int是一回事 */ printf("Excellent!\n" ); break; case 'B' : case 'C' : printf("Well done\n" ); break; case 'D' : printf("You passed\n" ); break; case 'F' : printf("Better try again\n" ); break; default : printf("Invalid grade\n" ); } printf("Your grade is %c\n", grade ); return 0; }
/* example 2 */

#include <stdio.h> int main() { printf("Please input your grade(1-100):"); int grade; scanf("%d", &grade); switch (grade / 10) { case 10: case 9: printf("A\n"); break; case 8: case 7: printf("B\n"); break; case 6: case 5: printf("C\n"); break; default: break; } return 0; }

 类型说明符或存储类说明符的非法组合。

void foo(void)
{
    unsigned int          xa;
    unsigned unsigned int xb;        /* Message 0616 */
    long                  xc;
    long long             xd;
    long long long        xe;        /* Message 0616 */
    float                 xf;
    double                xg;
    long double           xh;
    long float            xi;        /* Message 0616 */


    static int            ya;
    static extern int     yb;        /* Message 0616 */
    typedef extern int    yc;        /* Message 0616 */
    static register int   yd;        /* Message 0616 */
}

 0640  struct , union中的成员不能是 void 类型的

Members of a struct or union cannot be declared with type void, or indeed any incomplete type

incomplete type不完整类型: 因为缺少信息,导致无法判断一种类型的大小

A type which lacks information required to determine its size; for example an array type in which the array dimension is missing or a structure/union type for which members are not specified.

struct ST1 {int a; void b; };           /* Message 0640  无法判断b的大小 */

struct ST2 {int a; void *b; };          /* OK   指针类型默认占用4个字节        */

struct ST3 {int a; int buf[]; };        /* Message 0642  数组有多少个元素未知 */

0641 struct or union不能有函数类型,函数指针可以,但是要用对!

This struct or union member has been declared with "function" type; perhaps "pointer to function" was intended?

struct ST1 { int mode; int f(void);};        /* Message 0641 - f has function type */

struct ST2 { int mode; int (*f)(void);};     /* OK           - f has type pointer to function */

0643 'struct'或'union'类型的'%s'可能不是内容未知的'struct'或'union'。

struct ST1 { int mode; struct STX sx; };   /* Message 0643 and also 3313 - if STX has not been declared */


struct ST2 { int mode; struct ST2 sx; };   /* Message 0643 */


struct ST3 { int mode; struct ST3 *ps; };  /* OK */

0644 位域的宽度必须不大于“ int”的宽度。

struct ST
{
   unsigned int xbit: 48;     /* Message 0644 - unless int contains at least as many bits */
};

0645 宽度为零的位字段无法命名。

零宽度位字段无法命名; 其目的是指示不再将其他位域打包到放置前一个位域的单元中。 换句话说,它仅用于填充,不应尝试访问填充位

struct record
{
    unsigned int a : 5;
    unsigned int   : 0;    /* OK           - unnamed zero-width bit-field  */
    unsigned int b :10;
    unsigned int c : 0;    /* Message 0645 - zero-width bit-field is named */
    unsigned int d : 3;
};

0646 枚举常量必须具有可表示为“ int”的值。

enum list
{
   /* int is assumed to be 32 bits */
   HEAD = 2147483647,   /* OK           - maximum possible value */
   TAIL                 /* Message 0646 - too large              */
};

0649 在包含参数列表的函数头之后,参数的K&R样式声明不合法。

定义函数原型有新式和老式两种写法,不能混用两种形式

int f1(int x, int y)      /* OK - "new-style" prototype declaration */
{
    return 1;
}

int f2(x, y)              /* OK - "old-style" K&R declaration       */
int x;
int y;
{
    return 1;
}

int f3(int x)
int y;                    /* Message 0649 - neither old nor new     */
{
    return 1;
}

0650 命名函数参数上的非法存储类说明符。

除“寄存器”以外的存储类说明符已应用于命名的函数参数。 参数是局部对象,在每次调用函数时都会对其进行初始化。 它们是自动的,但不能使用“自动”存储类说明符。

void f1(typedef  int );       /* Message 0655 */
void f2(extern   int );       /* Message 0655 */
void f3(static   int );       /* Message 0655 */
void f4(auto     int );       /* Message 0655 */
void f5(register int );       /* OK */

void fa(typedef  int x);      /* Message 0650 */
void fb(extern   int x);      /* Message 0650 */
void fc(static   int x);      /* Message 0650 */
void fd(auto     int x);      /* Message 0650 */
void fe(register int x);      /* OK */

 0655 未命名的函数参数使用非法的存储类说明符

 1 void f1(typedef  int );       /* Message 0655 */
 2 void f2(extern   int );       /* Message 0655 */
 3 void f3(static   int );       /* Message 0655 */
 4 void f4(auto     int );       /* Message 0655 */
 5 void f5(register int );       /* OK */
 6 
 7 void fa(typedef  int x);      /* Message 0650 */
 8 void fb(extern   int x);      /* Message 0650 */
 9 void fc(static   int x);      /* Message 0650 */
10 void fd(auto     int x);      /* Message 0650 */
11 void fe(register int x);      /* OK */

0657 函数定义时没有指定参数名字

参数名字可以在函数声明的时候省略,但是在函数定义的时候不能省略

extern void f1(int);    /* OK - parameter names not necessary in a function declaration */

void f2(int)
{                       /* Message 0657 */

}

0664 函数参数的类型是void

函数参数列表里是void说明函数无参数

int f1(void);                   /* OK           */
int f2(void *);                 /* OK           */
int f3(void, int);              /* Message 0664  void代表无参,int 代表有参,矛盾*/

0685 任何具有静态存储时期的对象的初始化器都必须是常量表达式

static storage duration

  • An object declared with external or internal linkage, or with the storage-class specifier static has static storage duration. For such an object, storage is reserved and its stored valuye is initialized only once, prior to program startup. The object exists and retains its last-stored value throughout the execution of the entire program.
extern int ga;

int gb = ga;                 /* Message 0685 函数外定义,静态存储周期 */

void foo(int n)
{
    static int x = n;        /* Message 0685 */
   int y=n; // auto 自动存储周期
}

https://www.jianshu.com/p/f5081b30bdde

C语言-存储期(storage duration)

存储期

在函数中声明的变量,并不是从程序开始到程序结束始终有效的。变量的生存期也就是生命有两种,它们可以通过存储期(storage duration)这个概念来体现。

自动存储期

在函数中不使用存储类说明符static而定义出的对象(变量),被赋予了自动存储期(automatic storage duration), 它具有以下特性。

程序执行到对象声明的时候就创建出了相应的对象。而执行到包含该声明的程序块的结尾,也就是大括号 } 的时候,该对象就会消失。

也就是说,该对象拥有短暂的寿命,另外,如果不显式地进行初始化,则该对象会被初始化为不确定的值。

被赋予自动存储期的对象,在程序执行到int ax = 0;的时候,就被创建出来并且进行初始化。

静态存储期

在函数中使用static定义出来的对象,或者在函数外声明定义出来的对象被赋予了静态存储期(static storage duration),它具有以下特性。

在程序开始执行的时候,具体地说是在main函数执行之前的准备阶段被创建出来,在程序结束的时候消失。

也就是说,改对象拥有"永久"的寿命。另外,如果不显式地进行初始化,则该对象会自动初始化为0

被赋予了静态存储期的对象,会在main函数开始执行之前被初始化。因此,虽说程序执行的时候会经过static int sx = 0;的声明,但其实那个时候并没有进行初始化处理,也就是说该声明并未执行赋值处理。

#include <stdio.h>

int fx = 0;                  //静态存储期,文件作用域
void func(void)
{
    static int sx = 0;       //静态存储期+块作用域
    int ax = 0;              //自动存储期+块作用域
    
    printf("%3d%3d%3d\n",ax++, sx++, fx++);
}

int main(void)
{
    puts("ax  sx  fx");
    puts("-------------");
    for (int i = 0; i < 10; i++) {
        func();
    }
    puts("--------------");
    return 0;
}

autoregister

在函数通过存储类说明符autoregister声明定义出的变量,也被赋予了自动存储期。通过auto int ax = 0;进行的声明和不使用auto进行的声明在编译的时候是完全相同的。因此auto就显得有些多余了。

另外,使用register进行的声明register int ax = 0;,在源程序编译的时候,变量ax不是保存在内存中,而是保存在更高速的寄存器中,然而,由于寄存器的数量有限,所以也不是绝对的。

现在编译技术已经十分先进了,那个变量保存在寄存器中更好都是通过编译自行判断并进行最优化处理的(不仅如此,保存在寄存器中的变量在程序执行的时候也可能发生改变)。

使用register进行声明也渐渐变得没有意义了。

 0709 初始化本地声明的'extern %s'是非法的 ???
 
extern void foo(void)
{
    extern int glob = 0;        /* Message 0709 */
}

0801 “##”操作符可能不是宏替换列表中的第一个参数

#define GLUE(b) ## b           /* Message 0801 */

https://www.cnblogs.com/the-tops/p/8335897.html

C语言(C++语言)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念)。下面对常遇到的宏的使用问题做了简单总结。

关 于#和##
在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。比如下面代码中的宏:

#define WARN_IF(EXP) \

do{ if (EXP) \

fprintf(stderr, "Warning: " #EXP "\n"); 

}while(0)

那么实际使用中会出现下面所示的替换过程:
WARN_IF (divider == 0);
被替换为

do {

if (divider == 0)
fprintf(stderr, "Warning" "divider == 0" "\n");
} while(0);

这样每次divider(除数)为0的时候便会在标 准错误流上输出一个提示信息。

而##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。那么下面的代码就非常实用:
struct command
{
char * name;
void (*function) (void);
};

#define COMMAND(NAME) { NAME, NAME ## _command }

// 然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了:
struct command commands[] = {
COMMAND(quit),
COMMAND(help),
...
}
COMMAND宏在这里充当一个代码生成器的作用,这样可以在一定程度上减少代码密度,间接地也可以减少不留心所造成的错误。我们还可以n个##符号连接 n+1个Token,这个特性也是#符号所不具备的。比如:
#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d
typedef struct _record_typeLINK_MULTIPLE(name,company,position,salary);
// 这里这个语句将展开为:

// typedef struct _record_type name_company_position_salary;

关于...的使用

...在C宏中称为Variadic Macro,也就是变参宏。比如:
#define myprintf(templt,...)fprintf(stderr,templt,__VA_ARGS__)
// 或者
#define myprintf(templt,args...) fprintf(stderr,templt,args)
第一个宏中由于没有对变参起名,我们用默认的宏__VA_ARGS__来替代它。第二个宏中,我们显式地命名变参为args,那么我们在宏定义中就可以用args来代指变参了。同C语言的stdcall一样,变参必须作为参数表的最有一项出现。当上面的宏中我们只能提供第一个参数templt时,C标准要求我们必须写成:
myprintf(templt,);
的 形式。这时的替换过程为:
myprintf("Error!\n",);
替换为:
fprintf(stderr,"Error!\n",);
这是一个语法错误,不能正常编译。这个问题一般有 两个解决方法。首先,GNU CPP提供的解决方法允许上面的宏调用写成:
myprintf(templt);
而它将会 被通过替换变成:
fprintf(stderr,"Error!\n",);
很明显,这里仍然会产生编译错误(非本例的 某些情况下不会产生编译错误)。除了这种方式外,c99和GNUCPP都支持下面的宏定义方式:
#define myprintf(templt, ...) fprintf(stderr,templt,##__VAR_ARGS__)
这 时,##这个连接符号充当的作用就是当__VAR_ARGS__为空的时候,消除前面的那个逗号。那么此时的翻译过程如下:
myprintf(templt);
被转化为:
fprintf(stderr,templt);
这样如果templt合法,将不会产生 编译错误。 这里列出了一些宏使用中容易出错的地方,以及合适的使用方式。
错误的嵌套-Misnesting
宏的定义不一定要有完整的、配对的括号,但是为了避免出错并且提高可读性,最好避免这样使用。
由 操作符优先级引起的问题-Operator Precedence Problem
由于宏只是简单的替换,宏的参数如果是复合结构,那么通过替换之后可能由于各个参数之间的操作符优先级高于单个参数内部各部分之间相互作用的操作符优先级,如果我们不用括号保护各个宏参数,可能会产生预想不到的情形。比如:
#define ceil_div(x, y) (x + y - 1) / y
那么
a = ceil_div( b & c, sizeof(int) );
将被转化为:
a = ( b & c + sizeof(int) - 1) / sizeof(int);
// 由于+/-的优先级高于&的优先级,那么上面式子等同于:
a = ( b & (c + sizeof(int) - 1)) /sizeof(int);
这显然不是调用者的初衷。为了避免这种情况发生,应当多写几个括号:
#define ceil_div(x, y) (((x) + (y) - 1) / (y))
消除多余的分号-Semicolon Swallowing
通常情况下,为了使函数模样的宏在表面上看起来像一个通常的C语言调用一样,通常情况下我们在宏的后面加上一个分号,比如下面的带参宏:
MY_MACRO(x);
但是如果是下面的情况:
#define MY_MACRO(x) { \
\
\

}

//...


if (condition())
MY_MACRO(a);
else
{...}
这样会由于多出的那个分号产生编译错误。为了避免这种情况出现同时保持MY_MACRO(x);的这种写法,我们需要把宏定义为这种形式:
#define MY_MACRO(x) do {
\
\
} while(0)
这样只要保证总是使用分号,就不会有任何问题。

Duplication of Side Effects
这里的SideEffect是指宏在展开的时候对其参数可能进行多次Evaluation(也就是取值),但是如果这个宏参数是一个函数,那么就有可能被调用多次从而达到不一致的结果,甚至会发生更严重的错误。比如:
#define min(X,Y) ((X) > (Y) ? (Y) : (X))
//...
c = min(a,foo(b));
这 时foo()函数就被调用了两次。为了解决这个潜在的问题,我们应当这样写min(X,Y)这个宏:
#define min(X,Y) ({ \
typeof (X) x_ = (X); \
typeof (Y) y_ = (Y); \
(x_ < y_) ? x_ : y_; })
({...})的作用是将内部的几条语句中最后一条的值返回,它也允许在内部声明变量(因为它通过大括号组成了一个局部Scope)。
==
#define display(name) printf(""#name"")
int main() {
display(name);
}
运行结果是name,为什么不是"#name"呢?
---------------------------------------------------------------
#在这里是字符串化的意思
printf(""#name"") 相当于
printf("" "name" "")
---------------------------------------------------------------
The number-sign or "stringizing" operator (#) converts macroparameters (after expansion) to string constants
---------------------------------------------------------------
printf("" #name "") <1>
相当于printf("" "name" "") <2>
而<2>中的第2,3个“中间时空格等价于("空+name+空')
---------------------------------------------------------------
## 连接符与# 符
##连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。具体的定义在编译原理里有详尽的解释,但不知道也无所谓。同时值得注意的是#符是把传递过来的参数当成字符串进行替代。下面来看看它们是怎样工作的。这是MSDN上的一个例子。

假设程序中已经定义了这样一个带参数的宏:
#define paster( n ) printf( "token" #n " = %d", token##n )

同时又定义了一个整形变 量:
int token9 = 9;

现在在主程序中以下面的方式调用这个宏:
paster( 9 );

那 么在编译时,上面的这句话被扩展为:
printf( "token" "9" " = %d", token9 );

注意到在这个例子中,paster(9);中的这个”9”被原封不动的当成了一个字符串,与”token”连接在了一起,从而成为了token9。而#n也被”9”所替代。

可想而知,上面程序运行的结果就是在屏幕上打印出token9=9
---------------------------------------------------------------
#define display(name) printf(""#name"")
int main() {
display(name);
}
====================================
特殊性就在 于它是个宏,宏里面处理#号就如LS所说!
处理后就是一个附加的字符串!

但printf(""#name"") ;就不行了!
---------------------------------------------------------------
#define display(name) printf(""#name"")

该定义 字符串化name,得到结果其实就是 printf("name")  (前后的空字符串拿掉) 这样输出来的自然是 name。从另外一个角度讲,#是一个连接符号,参与运算了, 自然不会输出了。

0803 '#'操作符只能出现在宏参数之前

#define S1(s) # s               /* OK */
#define S2(s) #abc = s          /* Message 0803 */

0811 粘合操作符“##”只能出现在“#define”预处理指令中

#define GLUE(A,B) A##B

extern void foo(int p);
extern int  abcdef;

void ft(void)
{
    foo( GLUE( abc, def ) );        /* OK */
    foo( abc ## def );              /* Message 0811 and others */
}

0812 [C] Header name token '<text>' found outside '#include' preprocessing directive.

 

0834 类似函数的宏“%s()”被重新定义为类似对象的宏

#define G1(a ,b) a ## b
#define G2(a ,b) a ## b

#define G1 (0)                /* Message 0834 */

#undef G2    // #undef 是在后面取消以前定义的宏定义

#define G2 (0)                /* OK           */

0866 The string literal in a '#line' directive cannot be a 'wide string literal'.

#line 22 "file.h"                /* OK */

#line 45 L"file.h"               /* Message 0866 */

0873 Preprocessing token cannot be converted to an actual token.

int  1x;        /* Message 0873 and others  参数名以数组1开头,这是不允许的,应该是拼写错误 */
int  lx;        /* OK - object identifier spelled correctly */

0877 '#if' and '#elif' expressions may contain only integral constants.(#if 1 )

#define BIGINT 32767

#if (BIGINT > 35.42)          /* Message 0877 */

#endif


#if ("abc")                   /* Message 0877 */

#endif

0940 非法使用可变修改类型

void foo(int n)
{
    static int arr[n];             /* Message 0940 */
    struct st
    {
        int x;
        int y;
        int buf[n];                /* Message 0940 */
    };
}

0941 A variable length array may not be initialized.

When an array is defined with variable length, it is not permitted to specify an initializer

void foo(int n)
{
    char bc1[n] = {0};            /* Message 0941 */
    char bc2[n] = "<DEFAULT>";    /* Message 0941 */
}

1047 Function is being declared with default argument syntax after a previous call to the function. This is not allowed.

调用函数前未事先声明和定义,函数参数缺省不是C语言的特性

This is an error. A function is being declared with default argument syntax when the function has already been called.

It is generally permitted in the C language (although not recommended) to call a function without previously declaring it; but this practice is not supported when the function is defined with default argument syntax. Message 1047 is generated when a function call precedes the function declaration and the declaration specifies default arguments.

Default arguments are a C++ specific feature, which is provided as an extension to ISO:C Standard by some compilers. QA C will accept a constant expression or an object identifier as a default argument for a parameter, and use that argument whenever one is not provided explicitly in a function call.

extern int f1 (int, char);

int f2 (void)
{
  f1 (0, 1);
  f3 ();
}

extern int f1 (int a = 1, char b = 0);  /* Message 1046, 1047 */

extern int f3 (int a = 1);              /* Message 1046, 1047 */

1048  Default argument values are missing for some parameters in this function declaration. This is not allowed.

不要用函数缺省

extern int f1 (int = 0, char);        /* Message 1046, 1048 */

extern int f2 (int a = 0, char b);    /* Message 1046, 1048 */

3236 'inline' may not be applied to function 'main'.

3237  inline function '%1s' has external linkage and is defining an object, '%2s', with static storage duration.

An inline function with external linkage is defining an object with static storage duration.

If this is an external definition of the function, and the object is modifiable, this amounts to a constraint error.

If it is simply an inline definition of the function, the resulting behaviour can be unspecified (even if the object is non-modifiable), because there is no guarantee that the inline definition will be used rather than an external definition of the function in another translation unit.

Defining an inline function with external linkage is always dangerous and it is recommended that inline should only be used on functions which are declared static.

具有外部链接的内联函数正在定义具有静态存储持续时间的对象。如果这是函数的外部定义,并且对象是可修改的,则等于约束错误。

inline void f(int n)                                     /* 3240 */
{
    static const int a[5] = {10, 20, 30, 40, 50};        /* 3237 */
    static       int b[5];                               /* 3237 */
    int              c[5];                               /* OK   */

    if (n > a[n])
    {
        b[n] = n;
        c[n] = n;
    }

    return;
}

3238 inline function '%1s' has external linkage and is referring to an object, '%2s', with internal linkage.

An inline function with external linkage may not refer to an identifier with internal linkage.

Defining an inline function with external linkage is always dangerous and it is recommended that inline should only be used on functions which are declared static.

static const int a[5] = {10, 20, 30, 40, 50};

inline int f1(int n)
{
    return a[n];                                /* 3238 */
}

static inline int f2(int n)
{
    return a[n];                                /* OK   */
}

3244 'inline' may only be used in the declaration of a function identifier.

inline int x;                           /* 3244 */

 https://www.cnblogs.com/thrillerz/p/5208579.html

https://zh.wikipedia.org/wiki/%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0

C 语法中static 和inline联合使用

 

最近在学习阶段,翻阅代码。发现有一个用法比较让我奇怪,就上网查了一下

1
static inline void somefunction(void);

这里是举例说明,这行代码是放在.h文件中的。

在此之前,如果对inline概念不了解的请自行wiki

http://zh.wikipedia.org/wiki/%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0

但是因为自己用得比较少,所以没有太关注,

当然也没有inline修饰符前面要加static的概念

 

仔细想想:

1、首先,inline函数是不能像传统的函数那样放在.c中然后在.h中给出接口在其余文件中调用的,

因为inline函数其实是跟宏定义类似,不存在所谓的函数入口。

2、因为第一点,会出现一个问题,就是说如果inline函数在两个不同的文件中出现,也就是说

一个.h被两个不同的文件包含,则会出现重名,链接失败

所以static inline 的用法就能很好的解决这个问题,

使用static修饰符,函数仅在文件内部可见,不会污染命名空间。

可以理解为一个inline在不同的.C里面生成了不同的实例,而且名字是完全相同的

 

PS: inline修饰符不同的编译器不一样,下次有空再补充 

IAR中需要手动开发 并直接可以使用,keil中直接__inline即可 只要不是O0就会有内联处理@20140417

 

内联函数[编辑]

维基百科,自由的百科全书
 
 
跳到导航跳到搜索

计算机科学中,内联函数(有时称作在线函数编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展);也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。但在选择使用内联函数时,必须在程序占用空间和程序执行效率之间进行权衡,因为过多的比较复杂的函数进行内联扩展将带来很大的存储资源开支。另外还需要特别注意的是对递归函数的内联扩展可能引起部分编译器的无穷编译。

设计内联函数的动机[编辑]

内联扩展是一种特别的用于消除调用函数时所造成的固有的时间消耗方法。一般用于能够快速执行的函数,因为在这种情况下函数调用的时间消耗显得更为突出。这种方法对于很小的函数也有空间上的益处,并且它也使得一些其他的优化成为可能。

没有了内联函式,程式员难以控制哪些函数内联哪些不内联;由编译器自行决定是否内联。加上这种控制维度准许特定于应用的知识,诸如执行函式的频繁程度,被利用于选择哪些函数要内联。

此外,在一些语言中,内联函数与编译模型联系紧密:如在C++中,有必要在每个使用它的模块中定义一个内联函数;与之相对应的,普通函数必须定义在单个模块中。这使得模块编译独立于其他的模块。

的比较[编辑]

通常,在C语言中,内联展开的功能由带参宏(Macros)在源码级实现。内联提供了几个更好的方法:

  • 宏调用并不执行类型检查,甚至连正常参数也不检查,但是函数调用却要检查。
  • C语言的宏使用的是文本替换,可能导致无法预料的后果,因为需要重新计算参数和操作顺序
  • 在宏中的编译错误很难发现,因为它们引用的是扩展的代码,而不是程序员键入的。
  • 许多结构体使用宏或者使用不同的语法来表达很难理解。内联函数使用与普通函数相同的语言,可以随意的内联和不内联。
  • 内联代码的调试信息通常比扩展的宏代码更有用。

语言支持[编辑]

C++C99C11GNU C都支持内联函数,然而1989 ANSI C,这个最被广泛使用的C标准却不支持。在Ada中,关键字“pragma”可以用来声明内联。其他的大部分编程语言,包括Java和函数式语言,不支持内联函数,但他们的编译器常常进行强制性的内联扩展。不同的编译器在内联扩展上有处理不同复杂程度函数的能力。主流的C++编译器如Visual C++GCC提供了一个选项来自动内联任何一个合适的函数,即使它们没有被声明为内联函数。

内联函数在C++中的写法如下:

inline int max (int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}
a = max (x, y); // 等价于 "a = (x > y ? x : y);"

内联函数的不足[编辑]

除了通常使用内联扩展可能带来的问题,作为一种编程语言特性的内联函数也可能并没有看起来那么有效,原因如下:

  • 通常,编译器比程序设计者更清楚对于一个特定的函数是否合适进行内联扩展;一些情况下,对于程序员指定的某些内联函数,编译器可能更倾向于不使用内联甚至根本无法完成内联。
  • 对于一些开发中的函数,它们可能从原来的不适合内联扩展变得适合或者倒过来。尽管内联函数或者非内联函数的转换易于宏的转换,但增加的维护开支还是使得它的优点显得更不突出了。
  • 对于基于C的编译系统,内联函数的使用可能大大增加编译时间,因为每个调用该函数的地方都需要替换成函数体,代码量的增加也同时带来了潜在的编译时间的增加。

参见[编辑]

外部链接[编辑]

 

posted @ 2019-09-17 22:18  清风oo  阅读(625)  评论(0编辑  收藏  举报