C语言基本概念和基础语法

概述

C语言是一种通用的、面向过程式的计算机程序设计语言。

当前最新的 C 语言标准为 C18 ,在它之前的 C 语言标准有 C17、C11...C99 等。

关于C语言的一些背景:

  • C 语言是为了编写 UNIX 操作系统而被发明的。
  • C 语言是以 B 语言为基础的,B 语言大概是在 1970 年被引进的。
  • C 语言标准是于 1988 年由美国国家标准协会(ANSI,全称 American National Standard Institute)制定的。
  • 截至 1973 年,UNIX 操作系统完全使用 C 语言编写。
  • 目前,C 语言是最广泛使用的系统程序设计语言。
  • 大多数先进的软件都是使用 C 语言实现的。
  • 当今最流行的 Linux 操作系统和 RDBMS(Relational Database Management System:关系数据库管理系统) MySQL 都是使用 C 语言编写的。

C 语言最初是用于系统开发工作,特别是组成操作系统的程序。由于 C 语言所产生的代码运行速度与汇编语言编写的代码运行速度几乎一样,所以采用 C 语言作为系统开发语言。

下面列举几个使用 C 的实例:

  • 操作系统
  • 语言编译器
  • 汇编器
  • 文本编辑器
  • 打印机
  • 网络驱动器
  • 现代程序
  • 数据库
  • 语言解释器
  • 实体工具

通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。C 程序的源文件通常使用扩展名 .c。

关于编译器

写在源文件中的源代码是人类可读的源。它需要"编译",转为机器语言,这样 CPU 可以按给定指令执行程序。

C 语言编译器用于把源代码编译成最终的可执行程序。

编译器降低了编程难度。编译器的代表就是编程语言,每一种编程语言都有对应的编译器,这个编译器的作用就是把这种语言的源文件编译成可执行程序。

发明一门语言关键就在于发明它的编译器。
有了编译器(高级语言)之后,程序员就不再盯着CPU(二进制),而是改为盯着编译器(编程语言、编程语言的语法),所以我们学习编程的关键变成了学习编译器的习性,也就是编程语言的语法。

基本语法

分号 ;

在 C 程序中,分号是语句结束符。也就是说,每个语句必须以分号结束。它表明一个逻辑实体的结束。

注释:

// 单行注释

以 // 开始的单行注释,这种注释可以单独占一行。

/* 多行注释 多行注释 多行注释 */

/* */ 这种格式的注释可以单行或多行。

规则如下:

1.编译器会在编译过程删除注释,但不是简单的删除,而是用空格代替!

2.编译器认为,双引号括起来的都是字符串,双斜杠也不例外。

3.“/*…*/”这种类型的注释不能被嵌套。

4.编译器吧/*作为注释的开始,直到出现下一个*/为止。

下面来看一个问题,运用上面总结的知识点分析:

问题:下面哪个注释会引发错误:

A.int/*…*/I;

B.char *s=”abdcgsjd     //jhfadksfhk”;

C.//is it an \

    error

D.in/*…*/t i;

先看A,因为编译器把注释当做一个空格,所以解析出来之后是int i;是正确的。再看B,在字符串里面所有的都被认为是字符串,所以也么没有问题。C,这里使用了一个反斜杠,其实是接续符的意思,有时候一行写不下,要写到下一行的,就使用这个符号,所以C也没有问题。D的话,将其替换为空格,就变成了in t i;显然是错误的。

标识符:

C 标识符是用来标识变量、函数,或任何其他用户自定义项目的名称。

一个标识符以字母 A-Z 或 a-z 或下划线 _ 开始,后跟零个或多个字母、下划线和数字(0-9)。

C 标识符内不允许出现标点字符,比如 @、$ 和 %。C 是区分大小写的编程语言。因此,在 C 中,Manpower 和 manpower 是两个不同的标识符。

关键字

下表列出了 C 中的保留字。这些保留字不能作为常量名、变量名或其他标识符名称。

C语言一共有32个关键字,如下表所示:

关键字说明
auto声明自动变量
short声明短整型变量或函数
int声明整型变量或函数
long声明长整型变量或函数
float声明浮点型变量或函数
double声明双精度变量或函数
char声明字符型变量或函数
struct声明结构体变量或函数
union声明共用数据类型
enum声明枚举类型
typedef用以给数据类型取别名
const声明只读变量
unsigned声明无符号类型变量或函数
signed声明有符号类型变量或函数
extern声明变量是在其他文件正声明
register声明寄存器变量
static声明静态变量
volatile说明变量在程序执行中可被隐含地改变
void声明函数无返回值或无参数,声明无类型指针
if条件语句
else条件语句否定分支(与 if 连用)
switch用于开关语句
case开关语句分支
for一种循环语句
do循环语句的循环体
while循环语句的循环条件
goto无条件跳转语句
continue结束当前循环,开始下一轮循环
break跳出当前循环
default开关语句中的“其他”分支
sizeof计算数据类型长度
return子程序返回语句(可以带参数,也可不带参数)循环条件
注意:include和main不是关键字

include只是用来引入头文件以#include<>的形式出现;

main准确来说,多数是以int main()的形式出现,作为程序执行的入口。

转义字符

转义符\是主要用于表示无回显字符(主要用法),也用于表示常规字符(正常用单引号表示即可,这种方式其实绕了一遍),转义符必须放在单引号或者双引号里面。

什么是无回显字符?我们的键盘上有很多的符号,输入a就显示a,输入b就显示b,但是你按回车的时候,没有显示,只会换行。但是计算机在处理这个动作的时候,也是接收到了一个字符,这个字符就是换行符“\n”,也就是我们按下回车,其实是“输出了”换行符“\n”,类似的还有一些看不到输出的,都是无回显字符比如删除“\b”,横向跳到下一制表位置“\t”等等,这些叫做无回显字符。

在表示常规字符的时候,也可以代表一个asc码代码的字符,例如\ddd的1到3位表示八进制数所代表的字符,\xhh的1到2位表示十六进制所代表的字符。什么意思呢?

比如我们\x48,也就是48是十六进制的,转换成十进制是72,那么我们可以查找ascii表,发现72对应的字符是大写的H,再比如\x56,我们看1到2位,是十六进制的56,转换成十进制是86,查找ascii码表发现是V。八进制的同理,比如\122,八进制的122转换成十进制为82,查找ascii表格发现82对应的字符是R。这里表示常规字符就是这么个意思。

注意:反斜杠后面可以接八进制或者十六进制来表示字符,但是不能接十进制来表示字符。

接八进制时不要超过八进制能表示的范围,比如'\89',会出错。

附一道题:

\这个的后面最多会取3个数字作为转义的代码。比如\0123456
实际应该分为两部分 \012 和3456,而且\012这个中的012是八进制, 如果是\2413rty就分为\241和3rty,所以\的后面是要取3个数字的,不足三个的话有几个数字就转义几个数字,\02abc,就分成\02 和abc。

同理,\x后面可接最多2位十六进制数。

'\0'就表示空字符,后面的0实际上是八进制数。

char a = 0;
char b = '0';
printf("a = %d\n",a);//a = 0
printf("b = %d\n",b);//b = 48

总结来说就是,整型可以接收字符,底层是以字符对应的ASCLL码来存储的,不过你可以根据需要用数字或者字符的形式来展示(0~255范围内可互转)。

超出范围会循环,比如给int a = 304,也就是255+49,以%c形式输出时是a是0

'0'——48

'A'——65

'a'——97

ASCLL码参考:ASCII_百度百科

更多编码格式详见:常用编码格式整理_Flytiger1220的博客-CSDN博客 

ANSI码:ANSI是什么编码?_ansi编码_Liuqz2009的博客-CSDN博客 

注意:
在window环境下,\n是换行(new line)

\r是回车(return),也就是回到开头,如果只回车不换行,则一直回到同一行的开头

在window下,要想实现“换行和回车”的作用,就要用\n\r,如:

接续符

c语言中的接续符(\)是指示编译器行为的一种符号,用于告诉编译器,这一行并没有结束,下一行开始接着这一行的末尾。

用法如下:

1.编译器会将反斜杠剔除,跟在反斜杠后面的字符会自动接到前一行。

2.接续符之前和之后的空格都会被视为有效字符。

3.接续符适合在定义宏代码的时候使用,因为define的宏要在一行里完成,但是有时候代码比较长,一行很难看,所以可以使用接续符。

例如下面的程序:

#define SWAP(a,b) \

{                 \

    int temp = a; \

    a = b;        \

    b = temp;     \

}

三种程序结构

C语言程序的三种基本结构:顺序结构、选择结构(if语句)、循环结构(for、while、do-while)。

顺序结构

从第一条语句执行到最后一条语句,按顺序的执行。

选择结构(if,switch)

if 语句
一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。

if(boolean_expression){
   /* 如果布尔表达式为真将执行的语句 */
}

if…else 语句

一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为 false 时执行。

if(boolean_expression){
   /* 如果布尔表达式为真将执行的语句 */
}else{
   /* 如果布尔表达式为假将执行的语句 */
}

嵌套 if 语句
可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。

if( boolean_expression 1){
   /* 当布尔表达式 1 为真时执行 */
   if(boolean_expression 2){
      /* 当布尔表达式 2 为真时执行 */
   }
}

if…else if…else 语句
一个 if 语句后可跟一个可选的 else if…else 语句,这可用于测试多种条件。
当使用 if…else if…else 语句时,以下几点需要注意:

  • 一个 if 后可跟零个或一个 else,else 必须在所有 else if 之后。
  • 一个 if 后可跟零个或多个 else if,else if 必须在 else 之前。
  • 一旦某个 else if 匹配成功,其他的 else if 或 else 将不会被测试。
if(boolean_expression 1){
   /* 当布尔表达式 1 为真时执行 */
}else if( boolean_expression 2){
   /* 当布尔表达式 2 为真时执行 */
}else {
   /* 当上面条件都不为真时执行 */
}

附上一道题:

switch 语句

一个 switch 语句允许测试一个变量等于多个值时的情况。每个值称为一个 case,且被测试的变量会对每个 switch case 进行检查。

switch(expression){
    case constant-expression  :
       statement(s);
       break; /* 可选的 */
    case constant-expression  :
       statement(s);
       break; /* 可选的 */
  
    /* 您可以有任意数量的 case 语句 */
    default : /* 可选的 */
       statement(s);
}

switch 语句必须遵循下面的规则:

  • switch 语句中的 expression 是一个常量表达式,必须是一个整型或枚举类型。
  • 在一个 switch 中可以有任意数量的 case 语句。每个 case 后跟一个要比较的值和一个冒号。
  • case 的 constant-expression 必须与 switch 中的变量具有相同的数据类型,且必须是一个常量或字面量。
  • 当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。
  • 当遇到 break 语句时,switch 终止,控制流将跳转到 switch 语句后的下一行。
  • 不是每一个 case 都需要包含 break。如果 case 语句不包含 break,控制流将会 继续 后续的 case,直到遇到 break 为止。
  • 一个 switch 语句可以有一个可选的 default case,出现在 switch 的结尾。default case 可用于在上面所有 case 都不为真时执行一个任务。default case 中的 break 语句不是必需的。

嵌套 switch 语句

switch(ch1) {
   case 'A': 
      printf("这个 A 是外部 switch 的一部分" );
      switch(ch2) {
         case 'A':
            printf("这个 A 是内部 switch 的一部分" );
            break;
         case 'B': /* 内部 B case 代码 */
      }
      break;
   case 'B': /* 外部 B case 代码 */
}

示例:

#include <stdio.h>
#include <math.h>

int main(int argc,char* argv){
        int a = 10;
        // if
        if(a > 0){
                printf("a is bigger than 10");
        }

        // if -- else
        if( a == 11){
                printf("a is bigger than 10");
        }else{
                printf("a is smaller than 10");
        }
        // if -- else if--else
        if(a > 1){
                printf("a is bigger than 0");
        }else if(a > 2){
                printf("a is bigger than 2");
        }else{
                printf("a is smaller");
        }
        switch(a){
                case 1:
                        printf("1");
                        break;
                case 2:
                        printf("2");
                        break;
                default:
                        printf("10");
        }


        return 0;
}

关于break和default

看一道题:

明确几点:

1、default的位置可以任意;

2、default只有在其他条件都不满足的情况下才会执行;

3、不管default在哪,只要没有break,就会从上往下执行;

比如:

运行结果:

循环结构(for、while、do-while)

while 循环
只要给定的条件为真,C 语言中的 while 循环语句会重复执行一个目标语句。

while(condition)
{
   statement(s);
}

for 循环
for 循环允许您编写一个执行指定次数的循环控制结构。

for ( init; condition; increment ){
   statement(s);
}

do…while 循环
在 C 语言中,do…while 循环是在循环的尾部检查它的条件。do…while 循环与 while 循环类似,但是 do…while 循环会确保至少执行一次循环。

do{
   statement(s);
}while( condition );

示例:

#include <stdio.h>
#include <math.h>

int main(int argc,char* argv){
        int a = 10;


        /* while 循环执行 */
        while( a < 20 ){
                printf("a 的值: %d\n", a);
                a++;
         }

        /* for 循环执行 */
        for( int a = 10; a < 20; a = a + 1 ){
                printf("a 的值: %d\n", a);
        }

        /* do 循环执行 */
        do{
                printf("a 的值: %d\n", a);
                a = a + 1;
         }while( a < 20 );
        return 0;
}

有时,如果在等待某一个条件满足后才往后执行,可以用while来不断循环等待,只有满足条件才能跳出循环,比如:

不满足条件时就一直在这里循环。 

C语言中else语句的匹配规则

C语言规定在没有花括号的情况下,else优先与前面最靠近它且未配对的if成一对!

#include<stdio.h>
#include<time.h>
int main(void){
    int a, b, op;
    srand(time(NULL));
    a = rand() % 20; b = rand() % 20;
    op = rand() % 4;
    if(op == 0)
        printf("%d+%d=", a, b);
    else
        if(op == 1) printf("%d-%d=", a, b);
        else
            if(op == 2)
            {
                if(b != 0) printf("%d/%d=", a, b);
            }
            else  printf("%d*%d=", a, b);
    return 0;
}

思考:

由于C语言规定else优先与前面最靠近它且未配对的if成一对!

因此,如果把花括号删去,那么以下代码:

else  printf("%d*%d=", a, b);

就会与它最近的且未配对的if语句进行匹配,在这里就是 if(b != 0) printf("%d/%d=", a, b);

那么这样会出现的结果是:

 if(op == 2)
            
            if(b != 0) printf("%d/%d=", a, b);
            
            else  printf("%d*%d=", a, b);

显然,与源代码的逻辑就不一样了。

总结:

在写分支语句时,不论if或者else后面跟的是一条语句还是多条语句,最好都用花括号括起来,以免出现程序的逻辑匹配错误!

for循环详解

for 循环的使用更加灵活,在日常的程序开发过程中我们会使用的更多一些。

使用 while 循环来计算1加到100的值,代码如下:

#include <stdio.h>
int main(){
    int i, sum=0;
    i = 1;  //语句①
    while(i<=100 /*语句②*/ ){
        sum+=i;
        i++;  //语句③
    }
    printf("%d\n",sum);
    return 0;
}

可以看到,语句①②③被放到了不同的地方,代码结构较为松散。为了让程序更加紧凑,可以使用 for 循环来代替,如下所示:

#include <stdio.h>
int main(){
    int i, sum=0;
    for(i=1/*语句①*/; i<=100/*语句②*/; i++/*语句③*/){
        sum+=i;
    }
    printf("%d\n",sum);
    return 0;
}

在 for 循环中,语句①②③被集中到了一起,代码结构一目了然。

for 循环的一般形式为:

for(表达式1; 表达式2; 表达式3){
    语句块
}

运行过程:
1 . 先执行“表达式1”。

2 . 再执行“表达式2”,如果它的值为真(非0),则执行循环体,否则结束循环。

3 . 执行完循环体后再执行“表达式3”。

4 . 重复执行步骤 2) 和 3),直到“表达式2”的值为假,就结束循环。

上面的步骤中,2 . 和 3 . 是一次循环,会重复执行,for 语句的主要作用就是不断执行步骤 2 . 和 3 .。

“表达式1”仅在第一次循环时执行,以后都不会再执行,可以认为这是一个初始化语句。“表达式2”一般是一个关系表达式,决定了是否还要继续下次循环,称为“循环条件”。“表达式3”很多情况下是一个带有自增或自减操作的表达式,以使循环条件逐渐变得“不成立”。

for循环的执行过程可用下图表示:

在这里插入图片描述
我们再来分析一下“计算从1加到100的和”的代码:

#include <stdio.h>
int main(){
    int i, sum=0;
    for(i=1; i<=100; i++){
        sum+=i;
    }
    printf("%d\n",sum);
    return 0;
}

运行结果:

5050

程序分析:
1 . 执行到 for 语句时,先给 i 赋初值1,判断 i<=100 是否成立;因为此时 i=1,i<=100 成立,所以执行循环体。循环体执行结束后(sum的值为1),再计算 i++。

2 . 第二次循环时,i 的值为2,i<=100 成立,继续执行循环体。循环体执行结束后(sum的值为3),再计算 i++。

3 . 重复执行步骤 2 . ,直到第101次循环,此时 i 的值为101,i<=100 不成立,所以结束循环。

由此我们可以总结出for循环的一般形式:

for(初始化语句; 循环条件; 自增或自减){
    语句块
}

C语言for循环中的三个表达式
for 循环中的“表达式1(初始化条件)”、“表达式2(循环条件)”和“表达式3(自增或自减)”都是可选项,都可以省略(但分号;必须保留)。

1 . 修改“从1加到100的和”的代码,省略“表达式1(初始化条件)”:

int i = 1, sum = 0;
for( ; i<=100; i++){
    sum+=i;
}

可以看到,将i=1移到了 for 循环的外面。

2 . 省略了“表达式2(循环条件)”,如果不做其它处理就会成为死循环。例如:

for(i=1; ; i++) sum=sum+i;
相当于: 
i=1;
while(1){
    sum=sum+i;
    i++;
}

所谓死循环,就是循环条件永远成立,循环会一直进行下去,永不结束。死循环对程序的危害很大,一定要避免。

省略条件部分相当于加了个true,所以循环体内须有语句退出循环,否则循环不会终止。

3 . 省略了“表达式3(自增或自减)”,就不会修改“表达式2(循环条件)”中的变量,这时可在循环体中加入修改变量的语句。例如:

for( i=1; i<=100; ){
    sum=sum+i;
    i++;
} 

4 . 省略了“表达式1(初始化语句)”和“表达式3(自增或自减)”。例如:

for( ; i<=100 ; ){
    sum=sum+i;
    i++;
}
相当于: 
while(i<=100){
    sum=sum+i;
    i++;
}

5 . 3个表达式可以同时省略。例如:

for( ; ; )  语句

相当于:

while(1)  语句

6 . “表达式1”可以是初始化语句,也可以是其他语句。例如:

for( sum=0; i<=100; i++ )  sum=sum+i;

7 . “表达式1”和“表达式3”可以是一个简单表达式也可以是逗号表达式。

for( sum=0,i=1; i<=100; i++ )  sum=sum+i;

或:

for( i=0,j=100; i<=100; i++,j-- )  k=i+j;

8 . “表达式2”一般是关系表达式或逻辑表达式,但也可是数值或字符,只要其值非零,就执行循环体。例如:

for( i=0; (c=getchar())!='\n'; i+=c );

又如:

for( ; (c=getchar())!='\n' ; )
    printf("%c",c);

问题总结

注意,如果在main函数前一行出现了语法错误,这将会导致一系列的错误。

就是因为我在main之前的函数声明少了个分号,结果出现这一大堆错误。

关于break和continue

附上一道题:

break语句通常用在循环语句和开关语句中。当break用于开关语句switch中时,可使程序跳出switch而执行switch以后的语句;当break语句用于do…while、for、while循环语句中时,可使程序终止循环而执行循环后面的语句,即满足条件时便跳出循环。

continue语句的作用是跳过当前循环中剩余的语句而强行执行下一次循环。

B选项中break只跳出该switch语句,并不能终止循环,因此B错。C选项continue语句结束本次循环,至于大循环的终止与否与其无关,因此C错。D项中while和do…while语句都可以使用,continue语句跳出本次循环,因此D错误。因此A项正确。

我把while和do……while想成不是循环语句了。

再看一道题:

本题选B。考察对C语言跳转语句的理解。

A:break语句可以用于循环语句中,终止当前循环;也用于在switch语句中结束一个case 。A项错误。

B:continue语句只能用于循环语句,正确。

C:无条件跳转语句有return , break , continue , goto 。C项错误。

D:在switch语句中,break用来使执行流程跳出switch语句。在循环语句中,break语句用来使执行流无条件跳出本层循环体。continue的功能是:结束本次循环,接着进行下一次是否执行循环的条件判定。break和continue语句跳转范围明确。D选项错误。

综上本题选B。 

没有花括号的情况

如果循环语句或者选择语句后没有大括号,那么,第一个分号前的就是其执行部分。

以while为例:

while()

语句a;

语句b;

语句c;

当while在复合语句中没有使用花括号时 循环体只有语句a。

相当于while()后第一个分号前面的语句就是循环体。

有一种情况要注意,那就是while后面没有花括号,而且有多条语句,并且这些语句是用逗号隔开的,那么此时可以把逗号看做优先级最低的运算符,以第一个出现的分号为循环体的结束标志。

#include <stdio.h>

int main()
{
	int i = 0, k = 0;
	while(i < 3)
		i++, k++;
		
   /* 我的第一个 C 程序 */
   printf("Hello, World! %d\n", k); //Hello, World! 3
   
   return 0;
}

goto语句

关于C语言的goto语句存在很多争议,很多书籍都建议“谨慎使用,或者根本不用”。Linux之父Linus在Linux中大量使用goto,也是在启示着我们可以合理使用goto语句。

存在即合理,既然是C语言中的一个知识点,我们还是有必要学会使用。先看一些goto如何用:

其实就是在函数里定义一个位置标志,然后逻辑上直接跳转到该位置即可。

参考:

为什么Linux内核里大量使用goto,而很多书籍却不提倡使用?-CSDN博客

值得注意的一点是:goto语句与其跳转的标签处必须在同一个函数内。

当程序需要在错误处理时释放资源时,统一到goto处理最方便。这也是为什么很多大型项目,开源项目,包括Linux,都会大量的出现goto来处理错误!

如果goto没有执行,正常流程也会执行

在C语言中,goto语句是一种无条件转移语句,它允许程序直接跳转到指定的标签位置。然而,这并不意味着如果不执行goto语句,后续的正常流程就不会执行。实际上,无论是否执行goto语句,程序都会继续按照正常的流程执行,除非有其他控制语句(如return、exit等)改变了程序的执行路径。

示例如下:

由此可见,不管goto有没有执行,标签那里都会执行。

也就是说,goto改变的只是程序的运行流程,而不是获得某个专属的代码片段。

补充

C标准

C语言 | 什么是ANSI C标准?_嵌入式大杂烩的博客-CSDN博客

C编译器

主流 C 语言编译器有哪些?_闲静丶少言的博客-CSDN博客

GNU

1.GNU项目始创于1984年,旨在开发一个类似UNIX,且为自由软件的完整的操作系统。
2.GCC是GNU的一个项目,是一个用于编程开发的自由编译器。如今的GCC支持众多语言。
3.在Linux下编程最常用的C编译器就是GCC,除了支持ANSI C外,还对C语言进行了很多扩展,这些扩展对优化、目标代码布局、更安全地检查等方面提供了很强的支持。

GNU C 库

GNU C 库(GNU C Library,又称为glibc)是一种按照LGPL许可协议发布的,公开源代码的,免费的,方便从网络下载的C的编译程序。

GNU C运行期库,是一种C函数库,是程序运行时使用到的一些API集合,它们一般是已预先编译好,以二进制代码形式存在Linux类系统中,GNU C运行期库,通常作为GNU C编译程序的一个部分发布。 它最初是自由软件基金会为其GNU操作系统所写,但最主要的应用是配合Linux内核,成为GNU/Linux操作系统一个重要的组成部分。

C89、C90、ANSI C通常指的是同一个C语言标准。
1989年,美国国家标准协会(ANSI)推出C语言和C标准库的标准。该标准通常被称为ANSI C。
由于该标准是1989年推出的,因此也被称为C89。
时隔一年,1990国际标准协会ISO参照ANSI标准,推出一模一样的C语言和C标准库标准,由于该标准是1990年提出的,因此被称为C90标准。
因此,C89, C90, ANSI C是同一个标准。

1994年,ANSI/ISO联合的组织想要为ANSI C标准加入有限的改动,使C标准在国际化字符、一些明显的缺陷、数值计算上更上一层楼,推出了C99标准。

2011年,标准委员会推出了C11标准。

GNU计划,又称革奴计划;它的目标是创建一套完全自由的操作系统;
它在编写linux的时候自己制作了一个标准成为 GNU C标准;

ANSI 美国国家标准协会;ANSI C 和标准C是一个概念;

keil环境

keil的编译器是交叉编译器,是和芯片相关的,你创建过程的时候选择的芯片,其实就有编译器(当然还有其它的东西),所以有的芯片keil上没有还需要安装额外的包才行。

比如ARM芯片随下载包附带的ARMCC编译器。

keil软件本身应该是不带编译工具链和对应库的,这些应该都在芯片固件包中。

我们在使用Keil MDK编程,用到printf打印输出时,通常都会使能Keil工具自带的微库(MicroLib):

下面,我们就来讲讲关于Keil MDK中的这个MicroLib微库。

1什么是MicroLib?

MicroLib是针对以C语言编写的基于ARM嵌入式应用程序的高度优化的库。 与ARM Compiler工具链附带的标准C库相比,MicroLib具有明显的代码大小优势。

上图是一个性能基准测试代码,通过上图可以发现,微库在不同内核下代码量明显少很多。 补充:C语言标准库,我们经常都会调用。 在这些文件下还有很多各类库函数,比如:printf()、 malloc()、 memcpy()、 strncpy()等。

2微库和ARM标准C库区别

因为针对Arm的嵌入式系统,通常存储(代码)资源相对较小,为了使其能用上标准函数,工程师就针对Arm嵌入式系统,对标准C库进行了优化,以减小代码量。

优化标准库,肯定会带来一些差异,下面分享几点主要的差异:

代码(数据)量更小(对比上图)。

缺少一些不常用标准库函数,比如:文件 I/O 的库函数。

微库最大程度优化代码量,可能会导致有些库代码运行速度更慢。

可用于RTOS这类系统中,但不支持在像Windosw这类系统中。

微库函数也是一种函数,其很多库函数原型在网上都能找到,如果你有特殊需求,可以不用微库,在库函数原型的基础上进行修改。 比如:你的项目经过验证,发现微库的代码量,或者运行速度都不理想的情况下,可以自己修改原型函数。 当然,在万不得已的情况下才建议修改原型函数。通常还是建议直接使用微库,毕竟经过别人多次验证的库,出现bug的概率更小。

注意if…else…语句

使用条件语句时需要注意几个问题。

如果多个条件同时只满足一个,那就使用if…else…,该判断语句,会从上到下依次判断,直到最多满足一个条件,如果满足一个条件,那么该条件语句的剩余部分将不再执行。

要注意的是,如果有时确实同时只满足一个条件,就可以使用,效果等同于switch()case;

但是如果有多个条件判断,就不能使用if…else…,而是使用多个并列的if语句。

if(){}

if(){}

if(){}

……

另外,对于多个if语句,如果某个语句满足时就会结束当前执行,就可以在if里面加上return直接返回。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值