😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍标准C语言的32个关键字🍭
😎金句分享😎:🍭有机会一定要试试,其实试错的成本并不高,而错过的成本很高🍭
C语言32个关键字详解(1):数据类型部分(char、short、int、long、float、double、struct、union、enum、void)
C语言32个关键字详解(2):修饰类型部分(auto、signed、unsigned、static、extern、const、register、volatile)
C语言32个关键字详解(3):结构语句部分(if、else、switch、case、default、do、while、for、break、continue、return、goto)
C语言32个关键字详解(4):其他(typedef、sizeof)
C语言有多少个关键字?各个关键字有什么作用?本文将详细介绍C语言的关键字,可能一篇文章写不完 ,将在后面文章继续完善。C语言的关键字有32个,我将它主要分成下面四个方面来介绍。
功能 | 关键字 |
---|---|
类型(10个) | char、short、int、long、float、double、struct、union、enum、void |
修饰类型(8个) | auto、static、extern、register、volatile、const、signed、unsigned |
结构语句(12个) | do、for、while、break、continue、goto、if、else、switch、case、default、return |
其他(2个) | typedef、sizeof |
在学习下面的基本数据类型前,先弄清楚定义和声明有什么区别?怎么区分呢?
下面这两句代码,哪个是声明,哪个是定义?
char c;
extern int i;
extern
修饰,如果定义在本文件,则加不加extern
都可以。可以参考下面代码加深理解:
//extern.c
int extern_val;
int get_extern_val()
{
return extern_val;
}
复制上面代码保存为extern.c;
// main.c
#include
extern int extern_val; // 声明,变量定义在其他文件(extern.c)
extern int get_extern_val();// 声明,函数定义在其他文件(extern.c)
extern int val; // 声明,变量定义在本文件
extern int get_val(); // 声明,函数定义在本文件
// 下面是不加extern的声明,声明可以多次 2023-09-30 16:39:16
int val; // 不加extern的声明,变量定义在本文件
int get_val(); // 不加extern的声明,变量定义在本文件
int main()
{
extern_val = 0;
get_extern_val();
val = 1;
get_val();
return 0;
}
int val=1;
int get_val()
{
return val;
}
复制上面代码保存为main.c;
在Ubuntu 18.04系统使用版本为7.5的gcc编译器,通过命令gcc main.c extern.c
可以正常编译上面代码。
在32个关键字中,C语言的基本数据类型就占用了6个了,这些基本数据类型主要用来定义变量或者作为函数的参数或返回值类型。
类型 | 大小(32位系统) | 大小(64位系统) |
---|---|---|
char | 1个字节 | 1个字节 |
short | 2个字节 | 2个字节 |
int | 4个字节 | 4个字节 |
long | 4个字节 | 8个字节 |
float | 4个字节 | 4个字节 |
double | 8个字节 | 8个字节 |
C语言中,整型、单精度型、双精度型和字符型数据可以进行混合运算,但不同类型运算时会发生隐式类型转换:
char类型
和short类型
混合运算的结果是int类型
;long类型
和long long 类型
混合运算的结果是 long long类型
;signed类型
和unsigned类型
混合运算的结果是 unsigned类型
;赋值时类型转换:赋值时会隐式转换为=
左边的类型;
强制类型转换:在数字或变量前加(类型)
,进行强制转换;
如:char ch = (char)35;
数据类型就像模子一样,以内存为材料,打造出具有不同字节的变量。
当你定义一个char
型变量时,就像用拥有一个字节的“模子”从内存咔出来一个字节大小的“饼”;
当你定义一个int
型变量时,就像用拥有四个字节的“模子”从内存咔出来四个字节大小的“饼”;
总之,当使用数据类型定义变量,本质上就是从内存中取出一块该类型大小的内存,并给这个内存定义一个唯一的名字。
命名最好采用英文单词或其组合,别使用拼音,可望文知意,便于记忆和阅读;
尽量避免名字中出现数字编号,如 Value1,Value2 等
对在多个文件之间共同使用的全局变量或函数要加范围限定符(建议使用模块名(缩写)作为范围限定符)。 (GUI_ , etc)
由多个词组成时,每个词的第一个字母大写,其余全部小写,比如:int CurrentVal;
,或用下划线隔开,如:int current_val;
。
程序中不得出现仅靠大小写区分的相似的标识符,如:int x, X;
所有宏定义、枚举常数、只读变量全用大写字母命名,用下划线分割单词:
const int MAX_LENGTH = 100;
、#define FILE_PATH “/usr/tmp”
定义变量的同时千万千万别忘了初始化
。定义变量时编译器并不一定清空了这块内存,它的值可能是无效的数据。
不同类型数据之间的运算要注意精度扩展问题,一般低精度数据将向高精度数据扩展
命名可以参考下图规范,按照标识符前缀(后缀) + 含义标识 。非全局变量可以不用使用范围限定符前缀。
作用域前缀命名规则:
全局变量、函数(global):g
静态变量、函数(native):n
数据类型前缀命名规则:
数据类型 | 前缀命令 | 例子 |
---|---|---|
boolean 类型 | b | boolean bVariable; |
char 类型 | c | char cVariable; |
int 类型 | i | int iVariable; |
short 类型 | s | short sVariable; |
long 类型 | l | long lVariable; |
unsigned 类型 | u | unsigned int uiVariable; |
double 类型 | d | double dVariable; |
float 类型 | f | float fVariable; |
struct 关键字,会将一些相关联的数据打包成一个整体,方便使用。
程序开发过程中经常要传送的不是简单的字节流(char 型数组),而是多种数据组合起来的一个整体,其表现形式是一个结构体,如果将这样的数据放在char型数组中,再使用指针偏移去获取数据,会使编程变得很复杂,而使用struct关键字定义的结构体可以轻松解决这种问题。
另外,当函数参数超过4个时,使用起来容易出错,降低效率,可以使用结构体压缩形参。
结构体所占的内存大小是其成员所占内存之和。如果结构体里没有成员,它的大小会跟编译器有关,下面代码,在Ubuntu18.04 使用gcc编译器编译后,没有成员的结构体大小是0,使用g++编译器编译后,没有成员的结构体大小是1.
// main.c
#include
typedef struct stStudent
{
}stStu;
int main()
{
printf("%ld\n",sizeof(stStu));
return 0;
}
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少一个其他成员。柔性数组成员允许结构中包含一个大小可变的数组。 sizeof
返回的这种结构大小不包括柔性数组的内存。柔性数组的定义可以参考下面代码,虽然定义了arr数组,但它并没有使结构体占用的内存增加,整个stStu
的大小还是4个字节:
typedef struct stStudent
{
int i;
int arr[]; // 没指定数组大小
}stStu;
或
typedef struct stStudent
{
int i;
int arr[0];// 指定数组大小为0
}stStu;
柔性数组的使用可以参考下面代码,定义结构体指针pStu
指向malloc分配的一块内存,然后就可以通过pStu->arr[n]
去访问数组元素,因为这块内存是malloc分配的,如果arr
后面的空间不够的话,就可以动态去调整,这就是被成为柔性数组或可变长数组的原因:
stStu *pStu = (stStu*)malloc(sizeof(stStu)+100*sizeof(int));
完整使用例子如下:
// flexible_arr.c 2023-10-06 21:06:04
#include
#include
typedef struct stStudent
{
int i;
int arr[];
}stStu;
int main()
{
stStu *pStu = (stStu*)malloc(sizeof(stStu)+100*sizeof(int));
printf("stStu=%ld pStu=%ld\n",sizeof(stStu),sizeof(*pStu));
int i=0;
for(i=0; i<10; i++)
{
pStu->arr[i] = i;
}
printf("arr:");
for(i=0; i<10; i++)
{
printf("%d,",pStu->arr[i]);
}
printf("\n");
return 0;
}
union
关键字的用法和struct
关键字非常相似,不过union
只会分配一个空间来给所有成员共同使用,这个空间大小是所有成员中占用字节数最大成员决定的,所以所有成员具有相同的起始地址,同一时间只能存储一个成员的数据。
union主要用来压缩空间,如果某些数据在同一时间不会一起使用,可以使用union;
一般定义方式:union 类型名{ 成员列表};
union check
{
int i;
char ch;
} c;
系统的存储模式分为小端模式和大端模式:
可以使用下面代码测试系统大小端模式:
// union
#include
int checkSystem( )
{
union check
{
int i;
char ch;
} c;
c.i = 1;
return (c.ch ==1);
}
int main()
{
printf("system is %s\n",checkSystem()?"Little endian":"Big endian");
return 0;
}
enum在实际开发中很常用,也用得很简单。主要就是用enum
来定义一定范围内的常量。
一般定义方式:enum 枚举名{ 枚举值表 };
enum PictureFormat
{
JPEG,
BMP,
PNG,
};
上面代码定义之后,JPEG
、BMP
、PNG
就可以被当作常量在代码中使用了,所以同一区域内不能再定义其他枚举类型含有JPEG
、BMP
、PNG
枚举值,否则会报错:
注意:
- 定义时,枚举值(枚举常量)一般使用大写字母;
- 每个枚举值后都使用
,
隔开,而不是;
- 定义时,枚举值可以指定,指定后从那个常量开始依次加1:
enum{PIC_ONE=1, PIC_TWO,};
也可以不指定,枚举值从0开始依次加1:enum{PIC_ZERO, PIC_ONE, PIC_TWO,};
- 枚举类型定义后,枚举值可以在代码中直接使用,且同一区域内,已经定义过的枚举值不可再被其他枚举类型包含;
枚举变量可以和枚举类型一起定义:
enum PictureFormat
{
JPEG,
BMP,
PNG,
}top_pic, bottom_pic;
也可以先定义枚举类型,再定义枚举变量
enum PictureFormat
{
JPEG,
BMP,
PNG,
};
enum PictureFormat top_pic, bottom_pic;
注意:
- 不能直接将数字赋值给枚举变量,确实需要赋值,可以使用强制类型转换:
enum PictureFormat pic=3; //错误
enum PictureFormat pic=(enum PictureFormat)3; //正确
- 枚举变量在一般情况下,只允许被该类型的枚举值赋值;
sizeof
查看枚举类型、枚举变量占用的空间是4个字节。
void类型不能定义变量;
void*
指针可以接收任意类型指针的赋值;
void 真正发挥的作用在于:
(1) 对函数返回的限定;
(2) 对函数参数的限定。
限定返回值:
在 C 语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。
因此,为了避免混乱,我们在编写 C 程序时,对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值,一定要声明为 void 类型。
限定参数:
#include
fun() { return 1; } int main() { printf("%d",fun(2)); getchar(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
上面代码使用gcc编译器可以编译通过,fun函数没注明返回值和参数,调用时却可以使用参数,虽然gcc编译器可以编译通过,却不建议这样使用,会加大阅读代码的难度。如果函数定义时确定了不需要使用参数,应该使用void限定参数列表,避免歧义。
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁