02-复习C语言核心知识|基本语法、数据类型、变量、常量、存储类、基本语句(判断语句、循环语句、go to语句)和运算

2,490 阅读30分钟

一、概述

最近家里有点事,趁在家的这段时间,复习一下C语言核心知识,后的底层开发、音视频开发、跨平台开发、算法等方向的进一步研究与学习埋下伏笔

我们在前面一篇文章已经对C语言有了初步的认识,我们从本篇文章开始学习C语言的核心知识

二、C 语言核心语法|数据类型、常量与变量、存储类

1. 基本语法

上一小节我们知道了一个简单的小应用由哪些部分组成,这将有助于我们理解 C 语言的其它基本的构建块。

C 程序由各种Token组成,Token可以是 关键字、标识符、常量、字串符值、或者是一个符号。

下面我们来看一下 C 中的关键字,这些关键字不能作为常量名,变量名或者其它标识符名称:

关键字说明
auto声明自动变量
break跳出当前循环
case开关语句分支
char声明字符型变量或者函数返回值类型
const声明只具可读变量
continue结束当前循环,开始下一个循环
defaultswitch语句中的其它分支
dodo-while循环语句的循环体
double声明双进度浮点型变量或者函数返回值类型
else条件语句否定分支
enum声明枚举类型
extern声明变量或函数是在其它文件或本文件的其他位置定义
float声明浮点型变量或者函数返回值类型
for一种循环语句
goto无条件跳转语句
if条件语句
int声明整型变量或函数
long声明长整型变量或函数返回值类型
register声明寄存器变量
return子程序返回语句
short声明短整型变量或者函数
signed声明有符号类型变量或者函数
sizeof计算数据类型或者变量长度(即所占字节数)
static声明静态变量
struct声明结构体类型
switch用于开关语句
typedef用以给数据类型取别名
unsigned声明无符号类型变量或函数
union声明共用体类型
void声明函数无返回值或无参数,声明无类型指针
volatile说明变量在程序执行中可被隐含地改变
whiledo-while/while 循环语句的循环条件

2. 数据类型

在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。

C 中的类型可分为以下几种:

类型说明
基本类型它们是算术类型,包括两种类型:整数类型 和 浮点类型。
枚举类型它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值得变量。
void 类型类型说明符 void 表名没有可用的值
派生类型它们包括:指针类型、数组类型、结构类型、共用体类型 和 函数类型
(其它资料中看到相对更细致的划分:
image.png)
整数类型

下表列出了关于标准整数类型的存储大小和值范围的细节

类型32 位64 位值范围
char11-128 到 127 或 0 到 255
unsigned char110 到 255
int44-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int440 到 65,535 或 0 到 4,294,967,295
short22-32,768 到 32,767
unsigned short220 到 65,535
long48-2,147,483,648 到 2,147,483,647
unsigned long480 到 4,294,967,295

注意: 各种类型的存储大小与系统位数有关,但目前通用的以 64 为系统为主。

浮点类型

类型比特(位)数有效数字取值范围
float46~71.2E-38 到 3.4E+38
double815~162.3E-308 到 1.7E+308
long double1618~193.4E-4932 到 1.1E+4932

他们的字节,精度,取值范围都可以通过代码打印实现,如下:

//
//  main.c
//  02-数据类型
//
//  Created by VanZhang on 2022/5/16.
//

#include <stdio.h>
#include <float.h>
int main(int argc, const char * argv[]) {
    /**
     * 整数类型
    */

    printf("\n\n 整数类型 \n");

    //char 1 字节
    printf("char 存储大小: %lubyte \n", sizeof(char));
    printf("unsinged char 存储大小: %lu byte\n", sizeof(unsigned char));

    //short 2 字节
    printf("short 存储大小: %lu byte\n", sizeof(short));
    printf("unsinged short 存储大小: %lu byte\n", sizeof(unsigned short));

    //int 4 字节
    printf("int 存储大小: %lu byte\n", sizeof(int));
    printf("unsinged int 存储大小: %lu byte\n", sizeof(unsigned int));


    //long 4/8 字节
    printf("long 存储大小: %lu byte\n", sizeof(long));
    printf("unsinged long 存储大小: %lu byte\n", sizeof(unsigned long));

    /**
     * 浮点类型
    */

    printf("\n\n 浮点类型 \n");
    //float 4 字节 ,精度 6 位小数
    printf("float 存储最大字节数:%lu byte\n", sizeof(float));
    printf("float 最小值:%e \n", FLT_MIN);
    printf("float 最大值:%e \n", FLT_MAX);
    printf("float 精度值:%d \n", FLT_DIG);

    //double 8 字节
    printf("double 存储最大字节数:%lu byte\n", sizeof(double));
    printf("double 最小值:%e \n", DBL_MIN);
    printf("double 最大值:%e \n", DBL_MAX);
    printf("double 精度值:%d \n", DBL_DIG);

    //long double 16 字节
    printf("long double 存储最大字节数:%lu byte \n", sizeof(long double));
    printf("long double 最小值:%Lg \n", LDBL_MIN);
    printf("long double 最大值:%Lg \n", LDBL_MAX);
    printf("long double 精度值:%d \n", LDBL_DIG);
    return 0;
}  

image.png

可以通过 sizeof 关键字来获取数据类型占用内存的大小。上面代码可以看到了打印中出现了很多不识的 格式控制符,我总结了一个表,可以参考下:

格式控制符说明
%c读取一个单一的字符
%hd、%d、%ld读取一个十进制整数,并分别赋值给 short、int、long 类型
%ho、%o、%lo读取一个八进制整数(可带前缀也可不带),并分别赋值给 short、int、long 类型
%hx、%x、%lx读取一个十六进制整数(可带前缀也可不带),并分别赋值给 short、int、long 类型
%hu、%u、%lu读取一个无符号整数,并分别赋值给 unsigned short、unsigned int、unsigned long 类型
%f、%lf读取一个十进制形式的小数,并分别赋值给 float、double 类型
%e、%le读取一个指数形式的小数,并分别赋值给 float、double 类型
%g、%lg既可以读取一个十进制形式的小数,也可以读取一个指数形式的小数,并分别赋值给 float、double 类型
%s读取一个字符串(以空白符为结束)

3. 变量

变量其实只不过是程序可操作的存储区的名称。
C 中每个变量都有特定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。

变量的名称可以由字母、数字 和 下划线字符 组成。它必须以字母或下划线开头。大写字母和小写字母是不同的,因为 C 对大小写敏感的。

C 中的变量定义

变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。变量定义指定一个数据类型,并包含了该类型的一个或多个变量的列表,如下所示:

变量类型 变量名称;
如:
int a;
char c;
...

在这里,变量类型 必须是一个有效的 C 数据类型,可以是 char、w_char、int、float、double 或任何用户自定义的对象,变量名称 可以由一个或多个标识符名称组成,多个标识符之间用逗号分隔。下面列出几个有效的声明:

int a,b,c;
char c1,c2,c3;
float f,f1,f2;
double d1,d2,d3; 

C语言 中变量的 声明

变量声明向编译器保证变量以指定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。
变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。

变量的声明有两种情况:

  • 1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。
  • 2、另一种是不需要建立存储空间的,通过使用 extern 关键字声明变量名而不定义它。 例如:extern int a 其中变量 a 可以在别的文件中定义的。
  • 除非有 extern 关键字,否则都是变量的定义。
extern int i;//声明,不是定义
int a;//声明,也是定义

例子

#include <stdio.h>
//函数外定义变量      
//如果需要在一个源文件中引用另外一个源文件中定义的变量,我们只需在引用的文件中将变量加上 extern 关键字的声明即可
int x;
int y;

int sum() {
    //函数内声明变量 X , Y 为外部变量
    x = 10;
    y = 15;
    return x + y;
}

//入口函数
void main() {
    //打印变量相加
    int result;
    result = sum();
    printf("x + y = %d",result);
}
 

输出: image.png

4. 常量

常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。

常量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量,或字符串字面值,也有枚举常量。

整数常量

整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

212         /* 合法的 */
215u        /* 合法的 */
0xFeeL      /* 合法的 */
078         /* 非法的:8 不是八进制的数字 */
032UU       /* 非法的:不能重复后缀 */

浮点常量

浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。

当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的。

3.14159       /* 合法的 */
314159E-5L    /* 合法的 */
510E          /* 非法的:不完整的指数 */
210f          /* 非法的:没有小数或指数 */
.e55          /* 非法的:缺少整数或分数 */

定义常量

在 C 中,有两种简单的定义常量的方式:

  1. 使用 #define 预处理器。
  2. 使用 const 关键字。

下面是使用 #define 预处理器定义常量的形式:

#define identifier value

例子:

#define name 10L
#define age 27U
void main() {
    int  person;
    person = name + age;
    printf("values :%d",person); 
}

const 关键字

您可以使用 const 前缀声明指定类型的常量,如下所示:

const type variable = value; 

例子:

void main() {
    const int LEGTH = 10;
    const int WIDTH = 5;
    const char NEWLINE = '\n';
    int area;
    area = LEGTH * WIDTH;
    printf("value of area: %d", area);
}

三、C 语言核心语法|存储类

1. 存储类

存储类定义 C 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C 程序中可用的存储类:

  • auto
  • register
  • static
  • extern

2. auto 存储类

auto 存储类时所有局部变量默认的存储类。

int month;
auto int month;

上面定义了两个带有相同存储类,auto 只能用在函数内,即 auto 只能修饰局部变量。

3. register 存储类

register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 '&' 运算符(因为它没有内存位置)。

register int miles;

寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 register并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。

4. static 存储类

static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。

全局声明的一个 static 变量或方法可以被任何函数或方法调用,只要这些方法出现在跟 static 变量或方法同一个文件中。

例子:

//函数声明
void func1(void);

static int count = 10; //全局变量 - static 默认的
void main() {
    while (count--) {
        func1();
    }
}

void func1(void) {
//   'thingy' 是 'func1' 的局部变量 - 只初始化一次
// * 每次调用函数 'func1' 'thingy' 值不会被重置。 
    static int thingy = 5;
    thingy++;
    printf("thingy 为 %d, count 为 %d \n", thingy, count);
}

输出:

thingy 为 6, count 为 9 
thingy 为 7, count 为 8 
thingy 为 8, count 为 7 
thingy 为 9, count 为 6 
thingy 为 10, count 为 5 
thingy 为 11, count 为 4 
thingy 为 12, count 为 3 
thingy 为 13, count 为 2 
thingy 为 14, count 为 1 
thingy 为 15, count 为 0 

实例中 count 作为全局变量可以在函数内使用,thingy 在局部使用 static 修饰后,不会在每次调用时重置。

5. extern 存储类

extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。

extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,如下所示:

第一个文件 ndk_day1.c

/**
 *存储类的几种方式:
 * 默认:auto
 * 静态存储:static
 * 对外暴露:extern
 * (可能)存储在寄存器:register
 */
static float value = 37;
//函数声明
void func1(void);
static int count = 10; //全局变量 - static 默认的
 

#include "constant.h"

void test5(void) {
    auto int a = 10;
    register double b = 20;
    printf("a:%p\n",a);
    printf("b:%p\n",b);
    printf("extern_int_value:%d\n",extern_int_value);
    printf("value:%p\n",value);
    while (count--) {
        func1();
    }
    int res = addFunc(30, 7);
    printf("res:%d\n",res);
}

void func1(void) {
//   'thingy' 是 'func1' 的局部变量 - 只初始化一次
// * 每次调用函数 'func1' 'thingy' 值不会被重置。
    static int thingy = 5;
    thingy++;
    printf("thingy 为 %d, count 为 %d \n", thingy, count);
}

声明 constant.h 头文件

//
//  constant.h
//  02-数据类型
//
//  Created by VanZhang on 2022/5/16.
//

#ifndef constant_h
#define constant_h

#include <stdio.h>
extern int extern_int_value;
extern int addFunc(int a,int b);
#endif /* constant_h */

头文件的实现 constant.c

//
//  constant.c
//  02-数据类型
//
//  Created by VanZhang on 2022/5/16.
//

#include "constant.h"

int extern_int_value = 10;

int  addFunc(int a,int b){
    return a + b;
}

输出:

image.png

四、C 语言核心语法|运算与基础语句

计算机的基本能力就是计算,所以一门语言的计算能力十分重要。C语言之所以无所不能,很大原因在于它有强大的计算能力。
C语言一共有34种运算符,其中的大多数运算符跟在Java中的用法是一样的。下面做一个简单介绍。

1. 运算符

运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 语言内置了丰富的运算符,并提供了以下类型的运算符:

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符
  • 杂项运算符

1.1 算术运算符

下表显示了 C 语言支持的所有算术运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符描述实例
+把两个操作数相加A + B 将得到 30
-从第一个操作数中减去第二个操作数A - B 将得到 -10
*把两个操作数相乘A * B 将得到 200
/分子除以分母B / A 将得到 2
%取模运算符,整除后的余数(要求%两侧均为整型)B % A 将得到 0
++自增运算符,整数值增加 1A++ 将得到 11
--自减运算符,整数值减少 1A-- 将得到 9

例子:

void main(){
    int a = 21;
    int b = 10;
    int c;

    c = a + b;
    printf("a + b = %d \n", c);

    c = a - b;
    printf("a - b = %d \n", c);

    c = a * b;
    printf("a * b = %d \n", c);

    c = a / b;
    printf("a / b = %d \n", c);

    c = a % b;
    printf("a % b = %d \n", c);

    c = ++a;
    printf("++a = %d , %d \n", c, a);

    c = b++;
    printf("b++ = %d , %d \n", c, b);

    c = b--;
    printf("b-- = %d \n", c);
}

输出:

a + b = 31 
a - b = 11 
a * b = 210 
a / b = 2 
a b = 1 
++a = 22 , 22 
b++ = 10 , 11 
b-- = 11 

1.2 关系运算符

下表显示了 C 语言支持的所有关系运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符描述实例
==检查两个操作数的值是否相等,如果相等则条件为真。(A == B) 为假。
!=检查两个操作数的值是否相等,如果不相等则条件为真。(A != B) 为真。
检查左操作数的值是否大于右操作数的值,如果是则条件为真。(A > B) 为假。
<检查左操作数的值是否小于右操作数的值,如果是则条件为真。(A < B) 为真。
>=检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。(A >= B) 为假。
<=检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。(A <= B) 为真。
  • 关系运算的结果成立就为"真"(比如5>=4),不成立则为"假"(比如5<4)
  • 在Java中,关系运算的结果为"真"就返回true,"假"则返回false,用boolean类型的变量来接收
    boolean b1 = 5 > 4; // true
    
    boolean b2 = 5 < 4; // false
    

C语言中没有boolean类型

  • 在C语言中,关系运算的结果为"真"就返回1,"假"就返回0
    int a1 = 5 > 4; // 1
    
    int a2 = 5 < 4; // 0
    
  • 还需注意的是,在C语言中,任何非0值都为"真",只有0值才为"假"
    所以下面的做法是对的:
int a = 10;

if (a) {
    printf("条件成立");
} else {
    printf("条件不成立");
}

因为a不为0,所以为"真",输出结果是:

若是在Java中,编译器直接报错了,因为if的括号()中只能放boolean类型的值。

以此类推,下面的写法也是对的:

int a = 10;

if (a = 0) {
    printf("条件成立");
} else {
    printf("条件不成立");
}

上述代码是完全合理的,编译器不会报错,只是个警告而已。因为a为0,所以为"假",输出结果是:

这样看来,C语言似乎比Java方便很多,其实有很大的陷阱在这里:

假设你本来是想判断a是否为0,那么本应该写if (a == 0),若你误写成了if (a = 0),那将是一件非常可怕的事情,因为编译器又不报错,这样的BUG就难找了。因此,像a==0这样的表达式,最好写成0==a,若你误写成0=a,编译器会直接报错的。

// 不推荐
if (a == 0) {
}

// 推荐
if (0 == a) {
}
  • 在C语言中,可以不保存关系运算的结果
    因此,下面的写法是合法的:
    1 int a = 10;
    2 a > 10;
    3 a == 0;
    

如果是在Java中,第2、3行编译器会直接报错,但在C编译器看来是合法的,只是个警告。

所以,这里又是一个陷阱,假设你的本意是想给a赋值为0,那么本应该写a = 0; ,若你误写成a == 0; ,那将又是一个非常难找的BUG,因为编译器根本不会报错。在1993年的时候,这个BUG差点让一桩价值2000万美元的硬件产品生意告吹,因为如果这个BUG不解决,这个产品就没办法正常使用。

1.3 逻辑运算符

下表显示了 C 语言支持的所有关系逻辑运算符。假设变量 A 的值为 1,变量 B 的值为 0,则:

运算符描述实例
&&称为逻辑与运算符。如果两个操作数都非零,则条件为真。(A && B) 为假。
||称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。(A || B) 为真。
!称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。!(A && B) 为真。
//逻辑运算符
void test1(void){
    int a1 = 5;
    int b1 = 5;
    int c1;
    //如果两个操作数都非零,则条件为真。
    if (a1 && b1) {
        printf("a1 && b1  %d \n", true);
    } else {
        printf("a1 && b1  %d \n", false);
    }
    //如果两个操作数中有任意一个非零,则条件为真。
    if (a1 || b1) {
        printf("a1 || b1  %d \n", true);
    } else {
        printf("a1 || b1  %d \n", false);
    }

    //改变 a1 b1 的值
    a1 = 0;
    b1 = 10;

    //如果两个操作数都非零,则条件为真。
    if (a1 && b1) {
        printf("a1 && b1  %d \n", true);
    } else {
        printf("a1 && b1  %d \n", false);
    }

    if (!(a1 && b1)) {
        printf("!(a1 && b1)  %d \n", true);
    } else {
        printf("a1 || b1  %d \n", false);
    }
}


int main(int argc, const char * argv[]) {
//    test();
    test1();
    return 0;
}

输出:

image.png

1.4 位运算符

pqp&qp|qp^q
00000
01011
11110
10011
void main(){
   //位运算符 & | ^ ~
    int wA = 60; //0011 1100
    int wB = 13; //0000 1101
    int wC = 10;

    //都为真,才是真 0000 1100
    printf("wA & wB=?%d\n", wA & wB);

    //其中一个为真,就为真 0011 1101
    printf("wA | wB=?%d\n", wA | wB);

    //一个为真则为真,2个为真这为假 00110001
    printf("wA ^ wB=?%d\n", wA ^ wB);

    printf("~wB=?%d\n", ~wB);


    //二进制左移运算符 左 * 4 = 40
    printf("wC<<2=?%d\n", wC << 2);

    //二进制右移运算符 右 / 4
    printf("wC>>2=?%d\n", wC >> 2);
}


输出:

wA & wB=?12
wA | wB=?61
wA ^ wB=?49
~wB=?-14
wC<<2=?40
wC>>2=?2


下表显示了 C 语言支持的位运算符。假设变量 A 的值为 60,变量 B 的值为 13,则:

运算符描述实例
&按位与操作,按二进制位进行"与"运算。运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;(A & B) 将得到 12,即为 0000 1100
按位或运算符,按二进制位进行"或"运算。运算规则:`00=0; 01=1; 10=1; 11=1;`(AB) 将得到 61,即为 0011 1101
异或运算符,按二进制位进行"异或"运算。运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;(A ^ B) 将得到 49,即为 0011 0001
~取反运算符,按二进制位进行"取反"运算。运算规则:~1=0; ~0=1;(~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
<<二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。A << 2 将得到 240,即为 1111 0000
>>二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。A >> 2 将得到 15,即为 0000 1111

1.5 赋值运算符

下表列出了 C 语言支持的赋值运算符:

运算符描述实例
=简单的赋值运算符,把右边操作数的值赋给左边操作数C = A + B 将把 A + B 的值赋给 C
+=加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数C += A 相当于 C = C + A
-=减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数C -= A 相当于 C = C - A
*=乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数C *= A 相当于 C = C * A
/=除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数C /= A 相当于 C = C / A
%=求模且赋值运算符,求两个操作数的模赋值给左边操作数C %= A 相当于 C = C % A
<<=左移且赋值运算符C <<= 2 等同于 C = C << 2
>>=右移且赋值运算符C >>= 2 等同于 C = C >> 2
&=按位与且赋值运算符C &= 2 等同于 C = C & 2
^=按位异或且赋值运算符C ^= 2 等同于 C = C ^ 2
|=按位或且赋值运算符C= 2 等同于 C = C2

例子:


//赋值运算符
void test3(void){
    int wAA = 21;//0001 0101
    int wBB;

    wBB = wAA;
    printf("= %d\n", wBB);

    wBB += wAA;
    printf("+= %d\n", wBB);

    wBB -= wAA;
    printf("-= %d\n", wBB);

    wBB *= wAA;
    printf("*= %d\n", wBB);

    wBB /= wAA;
    printf("/= %d\n", wBB);

    wBB %= wAA;
    printf("%= %d\n", wBB);
    //wBB == wAA == 21
    //wBB = wBB<<wAA
    //     =  21<<21
    // 0001 0101
    // wBB = 0
    wBB <<= wAA;
    printf("<<= %d\n", wBB);
    
    // wBB = 0
    wBB >>= wAA;
    printf(">>= %d\n", wBB);
    
    // wBB = 0
    wBB &= wAA;
    printf("&= %d\n", wBB);

    wBB ^= wAA;
    printf("^= %d\n", wBB);

    wBB |= wAA;
    printf("|= %d\n", wBB);
}
int main(int argc, const char * argv[]) {
//    test();
//    test1();
//    test2();
    test3();
    return 0;
}

输出:

= 21
+= 42
-= 21
*= 441
/= 21
= 0
<<= 0
>>= 0
&= 0
^= 21
|= 21 

image.png

1.6 杂项运算符: sizeof、&、*、?:

下表列出了 C 语言支持的其他一些重要的运算符,包括 sizeof 和 ? : 。

运算符描述实例
sizeof()用来计算一个变量或者一个常量、一种数据类型所占的内存字节数。sizeof(a) 将返回 4,其中 a 是整数。
&返回变量的地址。&a; 将给出变量的实际地址。
*指向一个变量。*a; 将指向一个变量。
? :条件表达式如果条件为真 ? 则值为 X : 否则值为 Y

sizeof一共有3种形式

  • sizeof(变量\常量)
    sizeof(10);
    
    char c = 'a';
    sizeof(c);
    
  • sizeof(数据类型)
    sizeof(float);
    
    • 注意:不可以写成sizeof float;

在64bit环境下的例子:

//杂项运算符
void test4(void){
    int zxA = 4;
    short zxB;
    double zxC;
    int *ptr;//指针

    //sizeOf 运算符实例 ,lu 32位无符号整数
    printf("zxA sizeOf = %lu \n", sizeof(zxA));
    printf("zxB sizeOf = %lu \n", sizeof(zxB));
    printf("zxC sizeOf = %lu \n", sizeof(zxC));

    //& 和 * 运算符实例
    ptr = &zxA; //将 zxA 的地址值复制给 ptr 指针
    printf("zxA 的值为:%d \n", zxA);
    printf("*ptr 的值为:%d \n", *ptr);
  
    //三元运算符/三目运算符
    zxA = 10;
    zxB = (zxA == 1) ? 20 : 30;
    printf("zxb 的值为:%d \n", zxB);

    zxB = (zxA == 10) ? 20 : 30;
    printf("zxb 的值为:%d \n", zxB);
}

int main(int argc, const char * argv[]) {
//    test();
//    test1();
//    test2();
//    test3();
    test4();
    return 0;
}

输出:

zxA sizeOf = 4 
zxB sizeOf = 2 
zxC sizeOf = 8 
zxA 的值为:4 
*ptr 的值为:4 
zxb 的值为:30 
zxb 的值为:20  

image.png

2. 基础语句

2.1 判断语句

C 语言把任何非零和非空的值假定为 true,把零或 null 假定为 false。

C 语言提供了以下类型的判断语句。点击链接查看每个语句的细节。

语句描述
if 语句一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。
if...else 语句一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。
嵌套 if 语句您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。
switch 语句一个 switch 语句允许测试一个变量等于多个值时的情况。
嵌套 switch 语句您可以在一个 switch 语句内使用另一个 switch 语句。

?:运算符

跟 Java 一样

void main(){
 		int pdNumber;
    printf("输入一个数字:");
    scanf("%d", &pdNumber);
    (pdNumber % 2 == 0) ? printf("偶数") : printf("基数");
}


2.2 循环语句

C 语言提供了以下几种循环类型。点击链接查看每个类型的细节。

循环类型描述
while 循环当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for 循环多次执行一个语句序列,简化管理循环变量的代码。
do...while 循环除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
嵌套循环您可以在 while、for 或 do..while 循环内使用一个或多个循环。

2.3 循环控制语句

循环控制语句改变你代码的执行顺序。通过它你可以实现代码的跳转。

C 提供了下列的循环控制语句。点击链接查看每个语句的细节。

控制语句描述
break 语句终止循环或 switch 语句,程序流将继续执行紧接着循环或 switch 的下一条语句。
continue 语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。
goto 语句将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。

使用方法可以参考 Java ,下面给出循环的例子:

void main(){
      //限制
    for (int i = 0; i < 6; i++) {
        printf("限制循环,%d \n",i);
    }
    //无限循环
    for (;;) {
        printf("该循环会一直执行下去!\n");
    }
} 

专题系列文章

1. iOS底层原理前知识

2. 基于OC语言探索iOS底层原理

3. 基于Swift语言探索iOS底层原理

关于函数、枚举、可选项、结构体、类、闭包、属性、方法、swift多态原理、String、Array、Dictionary、引用计数、MetaData等Swift基本语法和相关的底层原理文章有如下几篇:

4. C语言核心语法

5. C++核心语法

6. Vue全家桶

7. 音视频技术核心知识

其它底层原理专题

1. 底层原理相关专题

2. iOS相关专题

3. webApp相关专题

4. 跨平台开发方案相关专题

5. 阶段性总结:Native、WebApp、跨平台开发三种方案性能比较

6. Android、HarmonyOS页面渲染专题

7. 小程序页面渲染专题