【嵌入式高级C语言】1:C语言类型及语句

1 关键字

1.1 数据类型相关的关键字

charshortintlongfloatdouble
structunionenumsignedunsignedvoid

在32位平台:

类型占用字节数
char字符类型1字节
short短整型2字节
int整型4字节
long长整型4字节
float单精度浮点数4字节
double双精度浮点数8字节
struct结构体~
union共用体~
enum枚举~
signed有符号数~
unsigned无符号数~

1.2 存储相关的关键字

registerstaticconstautoextern
关键字
register寄存器变量
static静态变量
const只读变量
auto自动变量
extern外部可用变量

1.2.1 const修饰变量为只读

const修饰变量为只读属性,本质为变量。

特点:只能初始化,不可以赋值。

#include <stdio.h>

int main(int argc, char **argv)
{
    const int a = 10;
    a = 20;
    return 0;
}
$ gcc 14const.c 
14const.c: In function ‘main’:
14const.c:8:7: error: assignment of read-only variable ‘a’
    8 |     a = 20;
      | 

1.2.2 register修饰寄存器变量

如果变量被频繁使用 会自动将变量存储在寄存器中。如果用户想人为将变量放入寄存器中,可以加register修饰。

目的:提高访问效率

#include <stdio.h>

int main(int argc, char **argv)
{
    // 变量用register修饰,不管变量有没有放在寄存器中,都无法取(内存)地址
    register int num = 0;

    &num;

    return 0;
}
$ gcc 15register.c 
15register.c: In function ‘main’:
15register.c:8:5: error: address of register variable ‘num’ requested
    8 |     &num;
      | 

1.3 控制语句相关的关键字

ifelsebreakcontinueforwhiledoswitchcasegotodefault

1.4 其他关键字

sizeoftypedefvolatile

sizeof:测量类型的大小
typedef:给类型取别名
volatile:防止编译器优化

1.4.1 volatile关键字

功能1:强制访问内存
image.png

功能2:防止编译器优化
image.png

1.4.2 sizeof测量类型的大小

#include <stdio.h>

int main(int argc, char **argv)
{
    int num = 0;
    printf("%lu ", sizeof(num));
    printf("%lu ", sizeof(int));

    char ch = 'a';
    printf("%lu ", sizeof(char));
    printf("%lu ", sizeof(ch));
    printf("%lu\n", sizeof('a'));     // 整型

    return 0;
}
$ ./a.out 
4 4 1 1 4

1.4.3 typedef给已有类型重新取个别名

不可以重建新类型。

将长且复杂的类型名取一个短小的名称。

typedef作用的步骤:

  1. 先用已有的类型,定义一个普通变量。
  2. 用别名代替变量名。
  3. 在整个表达式最前面加上typedef。
步骤1int num;
步骤2int INT;
步骤3typedef int INT;

INT num;   ==   int num;

2 数据类型

2.1 变量

image.png

变量名命名规则:变量由数字、字母、下划线组成,且不能以数字开头,不可以为关键字。

int auto;     // 错误,变量名不可以为关键字
int 3num_;    // 错误,变量名不可以数字开头

变量定义时请务必初始化,防止不必要报错。

int a = 10;    // 变量初始化

int b;
b = 20;        // 不是初始化,是赋值

变量的定义、声明和使用

  • 变量的定义为变量开辟空间
  • 变量的声明是说明变量的名称类型
  • 变量的使用是对变量的读写

变量获取键盘输入scanf

#include <stdio.h>

int scanf(const char *format, ...);
format 获取数据的格式

示例1:

#include <stdio.h>

int main(int argc, char **argv)
{
    int num = 0;

    printf("请输入一个int类型数据: ");
    scanf("%d", &num);
    printf("num = %d\n", num);


    return 0;
}
$ ./a.out 
请输入一个int类型数据: 100
num = 100

image.png
%d和scanf结合表只能提取数值,扫描到非数值立刻退出!!!但是剩余值还留在存储区内部,需清除缓冲区。

示例2:键盘输入两个数并且求和

#include <stdio.h>

int main(int argc, char **argv)
{
    int num1 = 0;
    int num2 = 0;

    printf("请输入两个数字:");
    scanf("%d:%d", &num1, &num2);
    printf("%d + %d = %d\n", num1, num2, num1 + num2);

    return 0;
}
$ ./a.out 
请输入两个数字:10:40
10 + 40 = 50

2.2 字符类型

image.png

字符类型数据在计算机中以ASCII码值存储。

示例:

#include <stdio.h>

int main(int argc, char **argv)
{
    printf("%c\n", 'b');
    printf("%d\n", 'b');

    return 0;
}
$ ./a.out 
b
98

单引号’'的作用

  1. 'a’描述a为字符。
  2. 取字符的ASCII码值。

字符变量的初始化

char ch;
char ch = 'a';
char ch = '\0';
char ch = 0;          // 数值0,与'\0'一样
char ch = '0';        // 字符0,对应ASCII码值48

示例1:获取字符并输出

#include <stdio.h>

int main(int argc, char **argv)
{
    char ch = '\0';

    printf("请输入一个字符:");
#if 0
    scanf("%c", &ch);       // 从缓冲区扫描一个字符
    getchar();              // 清除缓冲区中的回车
    
#else
    ch = getchar();
    getchar();

#endif

    printf("%c\n", ch);
    printf("%d\n", ch);

    return 0;
}
$ ./a.out 
请输入一个字符:p
p
112

示例2:字符大小写切换
键盘输入一个字符,如果是大写 就转换成小写,如果是小写 就转换成大写。

#include <stdio.h>

int main(int argc, char **argv)
{
    char ch = '\0';

    printf("请输入一个字符:");
    ch = getchar();
    if (ch >= 'a' && ch <= 'z')
    {
        ch -= 32;
    }
    else if (ch >= 'A' && ch <= 'Z')
    {
        ch += 32;
    }
    else 
    {
        printf("输入错误\n");
        return -1;
    }
    printf("%c\n", ch);

    return 0;
}
$ ./a.out 
请输入一个字符:Y
y

2.3 实型(浮点数)float,double

以f结尾的为float类型
不以f结尾的为double类型

#include <stdio.h>

int main(int argc, char **argv)
{
    float f = 0.0f;
    double d = 0.0;

    printf("请输入两个浮点数:");
    // 获取键盘输入
    scanf("%f %lf", &f, &d);
    // 输出
    printf("%f %lf\n", f, d);


    return 0;
}
$ ./a.out 
请输入两个浮点数:3.4 6.6
3.400000 6.600000

2.4 有符号数和无符号数

类型对应匹配符
int%d
short%hd
long%ld
unsigned int%u
unsigned short%hu
unsigned long%lu

3 进制

C语言不能直接输出二进制,但是可以输出十进制、八进制和十六进制。

输出进制输出格式匹配
十进制%d
八进制%o / %#o
十六进制%x / %#x

示例:验证输出进制数

#include <stdio.h>

int main(int argc ,char **argv)
{
    int num = 89;

    printf("%d  \n", num);      	// 以十进制输出
    printf("%o  ", num);        	// 以八进制输出
    printf("%#o  \n", num);       	// 以八进制的格式输出
    printf("%x  ", num);        	// 以十六进制输出
    printf("%#x  \n", num);       	// 以十六进制的格式输出

    

    return 0;
}
$ ./a.out 
89  
131  0131  
59  0x59  

进制的转换
image.png

image.png

image.png

image.png

4 原码、反码、补码、

  • 计算机中存储的数据为补码。
  • 反码为原码和补码之间的桥梁。
  • 原码是数据的二进制形式。

image.png

补码的意义

  1. 统一了0的编码。
+0补码:0000 0000
-0补码:0000 0000
  1. 将减法运算变为加法运算。

5 计算机对数据的输出

%d输出,对于不管是int类型存储还是unsigned int类型存储,一律看4字节的最高位:

  1. 如果4字节最高位为1,求补码输出,为负数。
  2. 如果4字节最高位为0,内存原码输出,为正数。
#include <stdio.h>

int main(int argc, char **argv)
{
    int num1 = 0xf0000009;
    unsigned int num2 = 0xf0000009;
    int num3 = 0x00000009;
    unsigned int num4 = 0x00000009;

    printf("num1 = %d\n", num1);
    printf("num2 = %d\n", num2);

    printf("num3 = %d\n", num3);
    printf("num4 = %d\n", num4);

    return 0;
}
$ ./a.out 
num1 = -268435447
num2 = -268435447
num3 = 9
num4 = 9

%d输出,对于char类型存储,看1字节最高位:

  1. 如果1字节最高位为1,求补码输出,为负数。
  2. 如果1字节最高位为0,内存原码输出,为正数。

对于unsigned char 类型存储,一律内存原码输出。

#include <stdio.h>

int main(int argc, char **argv)
{
    char ch1 = 0xf9;
    char ch2 = 0x09;
    unsigned char ch3 = 0xf9;
    unsigned char ch4 = 0x09;

    printf("ch1 = %d\n", ch1);
    printf("ch2 = %d\n", ch2);
    printf("ch3 = %d\n", ch3);
    printf("ch4 = %d\n", ch4);

    return 0;
}
$ ./a.out 
ch1 = -7
ch2 = 9
ch3 = 249
ch4 = 9

%#x输出,对于无论int还是unsigned int类型存储,内存原码输出。

%#x输出,对于char类型存储,看1字节最高位:

  1. 如果1字节最高位为1,补1至4字节输出。
  2. 如果1字节最高位为0,内存原码输出。(实际为补0至4字节)
    %#x输出,对于unsigned char类型存储,内存原码输出。
#include <stdio.h>

int main(int argc, char **argv)
{
    int num1 = 0xf0000009;
    int num2 = 0x00000009;
    unsigned int num3 = 0xf0000009;
    unsigned int num4 = 0x00000009;

    char ch1 = 0xf5;
    char ch2 = 0x05;
    unsigned char ch3 = 0xf5;
    unsigned char ch4 = 0x05;


    // 输出
    printf("num1 = %#x\n", num1);
    printf("num2 = %#x\n", num2);
    printf("num3 = %#x\n", num3);
    printf("num4 = %#x\n", num4);

    printf("ch1 = %#x\n", ch1);
    printf("ch2 = %#x\n", ch2);
    printf("ch3 = %#x\n", ch3);
    printf("ch4 = %#x\n", ch4);


    return 0;
}
$ ./a.out 
num1 = 0xf0000009
num2 = 0x9
num3 = 0xf0000009
num4 = 0x9
ch1 = 0xfffffff5
ch2 = 0x5
ch3 = 0xf5
ch4 = 0x5

%u输出,对于无论int还是unsigned int类型存储,一律内存原码输出。

%u输出,对于char类型存储,看1字节最高位:

  1. 如果1字节最高位为1,补1至4字节内存原码输出。
  2. 如果1字节最高位为0,内存原码输出。(实际为补0至4字节)
    %u输出,对于unsigned char类型存储,内存原码输出。
#include <stdio.h>

int main(int argc, char **argv)
{
    int num1 = 0xf0000009;
    int num2 = 0x00000009;
    unsigned int num3 = 0xf0000009;
    unsigned int num4 = 0x00000009;

    char ch1 = 0xf5;
    char ch2 = 0x05;
    unsigned char ch3 = 0xf5;
    unsigned char ch4 = 0x05;


    // 输出
    printf("num1 = %u\n", num1);
    printf("num2 = %u\n", num2);
    printf("num3 = %u\n", num3);
    printf("num4 = %u\n", num4);

    printf("ch1 = %u\n", ch1);
    printf("ch2 = %u\n", ch2);
    printf("ch3 = %u\n", ch3);
    printf("ch4 = %u\n", ch4);


    return 0;
}
$ ./a.out 
num1 = 4026531849
num2 = 9
num3 = 4026531849
num4 = 9
ch1 = 4294967285
ch2 = 5
ch3 = 245
ch4 = 5

6 转义字符

\和某些字符结合产生新的字符含义。

image.png

6.1 八进制转义

‘\ddd’:

  • 每个d的范围必须是0~7。
  • 3个d表示最多识别3位八进制数据。
以下字符描述正确的是 A
A:'\123'
B:'\18'          越界 2位
C:'\1234'        越界 2位
D:'\183'         越界 3

尤其注意八进制转义需要考虑溢出, 最大为255, 最大的八进制转义为'\377'

char ch = '\0';
for (; ch < 1000; ch++);    // 死循环

6.2 十六进制转义

‘\xhh’:

  • 每个h的范围是09、af。
  • 两个h表示最多识别2位十六进制。
    image.png

7 类型转换(临时转换)

7.1 自动类型转换

image.png

例1:int与unsigned int转换成unsigned int

#include <stdio.h>

int main(int argc, char **argv)
{
    int num1 = -10;
    unsigned int num2 = 6;

    if (num1 + num2 > 0)                // unsigned int > 0
    {
        printf(">0\n");
    }
    else 
    {
        printf("<0\n");
    }

    printf("%d\n", num1 + num2);        // %d输出unsigned int类型只看最高位,为1,转换为补码输出
    printf("%u\n", num1 + num2);        // 内存原码输出

    return 0;
}
$ ./a.out 
>0
-4
4294967292

例2:int和double类型参加运算,int将转换为double类型

#include <stdio.h>

int main(int argc, char **argv)
{
    int num1 = 0;
    double num2 = 0.0;

    printf("%lu\n", sizeof(num1 + num2));       // = sizeof(double) = 8


    return 0;
}
$ ./a.out 
8

例3:char和short类型只要参加运算,都会将自己转换成int类型

#include <stdio.h>

int main(int argc, char **argv)
{
    short num1 = 0;
    char ch = 'a';

    printf("%lu\n", sizeof(num1 + num1));       // 4
    printf("%lu\n", sizeof(num1 + ch));         // 4
    printf("%lu\n", sizeof(ch + ch));           // 4

    return 0;
}
$ ./a.out 
4
4
4

7.2 强制类型转换

(类型说明符)(表达式)

(int)p + 1
(int)(p + 1)

8 运算符

image.png

如果运算符需要一个运算对象,就叫单目运算符。
如果运算符需要两个运算对象,就叫双目运算符。
如果运算符需要三个运算对象,就叫三目运算符。
如果运算符需要多个运算对象,就叫多目运算符。

8.1 算数运算符

算数运算符符号
+
-
*
/
取余%
加等于+=
减等于-=
乘等于*=
除等于/=
取余等于%=

image.png

#include <stdio.h>

int main(int argc, char **argv)
{

    printf("%d\n", 9 / 4);
    printf("%lf\n", 9 / 4.0);

    return 0;
}
$ ./a.out 
2
2.250000

%取余运算符,不可以用于浮点数(实型)

例1:如果random()函数产生一个随机数>0,请使用rand()产生60~100的随机数

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char **argv)
{
    // 设置随机数种子
    srandom(time(NULL));

    for (int i = 0; i < 5; i++)
    {
        // 生成60~100之间的随机数
        printf("%d  ", random() % 41 + 60);
    }

    // 换行
    printf("\n");

    return 0;
}
$ ./a.out 
83  97  79  100  67  
$ ./a.out 
62  99  74  64  88  
$ ./a.out 
77  66  67  83  74  
$ ./a.out 
74  90  75  91  68  
$ ./a.out 
60  75  73  84  77  

例2:如果random()函数产生一个随机数>0,请使用rand()产生’a’~'z’的随机字母

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


int main(int argc, char **argv)
{
    // 设置随机数种子
    srandom(time(NULL));

    for (int i = 0; i < 5; i++)
    {
        printf("%c  ", random() % 26 + 'a');
    }
    printf("\n");

    return 0;
}
$ ./a.out 
z  p  m  u  g  
$ ./a.out 
u  g  y  e  h  
$ ./a.out 
x  s  y  h  b  
$ ./a.out 
p  a  m  l  m  
$ ./a.out 
s  t  l  w  b  

8.2 复合运算符

image.png
注意:一定要将等号的右边看做一个整体

案例1:

#include <stdio.h>

int main(int argc, char **argv)
{
    int a = 3;
    a *= 4 + 5;

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

    return 0;
}
$ ./a.out 
a = 27

案例2:

8.3 关系运算符

关系运算符符号
大于>
小于<
等于==
小于等于<=
大于等于>=
不等于!=
image.png

8.4 逻辑运算符

逻辑运算符符号
&&
||

逻辑与的短路特性:A && B,如果判断A为假,编译器不会判断B的结果。
逻辑或的短路特性:A || B,如果判断A为真,编译器不会判断B的结果。

在C语言中,除了0为假,其他都为真。
image.png

8.5 位运算(二进制位运算)

8.5.1 & 按位与

语法:只有全为1,才为1。
特点:和1相与,保持不变;和0相与,全为0。
场景:将指定位清零。

例:data为1字节,将data的第3,4位清零,其他位保持不变

#include <stdio.h>

int main(int argc, char **argv)
{
    unsigned char data = 0xff;

    printf("data = %#x\n", data);
    data &= ~(0x1 << 3 | 0x1 << 4);
    printf("data = %#x\n", data);


    return 0;
}
$ ./a.out 
data = 0xff
data = 0xe7

8.5.2 | 按位或

语法:有1为1,全0为0。
特点:和0相或,保持不变;和1相或,全为1。
场景:将指定位置1。

例:data为1字节,将data的第3,4位置1,其他位保持不变

#include <stdio.h>

int main(int argc, char **argv)
{
    unsigned char data = 0x00;

    printf("data = %#x\n", data);
    data |= (0x1 << 3 | 0x1 << 4);
    printf("data = %#x\n", data);


    return 0;
}
$ ./a.out 
data = 0
data = 0x18

8.5.3 ~ 按位取反

0变1,1变0。

8.5.4 ^ 按位异或

语法:相同为0,不同为1。
特点:和0异或,保持不变;和1异或,翻转。
场景:将指定位翻转。

8.6 左移和右移

左移<<:左边丢弃,右边补0
image.png

右移>>:右边丢弃,左边补0/1
image.png

算数右移和逻辑右移为编译器决定,用户无法确定!!!

image.png

image.png

8.7 三目运算符

image.png

9 优先级

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

USOPP冒险记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值