开坑时间2022年1月23日23:19:59
更新:2022年6月15日13:42:06,爷不转了,java真香
博文背景
本人JAVA开发工作了三年,工作的原因现在准备赴身C语言的怀抱,大学时期的C语言课上就会写个HelloWorld,然后就再也没见过C语言老师了(向老师道歉),在各大搜索引擎都尝试白嫖不到类似的博文,只好自己动手。这篇博文主要是作为学习笔记来记录JAVA和C两者之间的差异或者相似点,能力原因领悟到的还比较浅显,文笔不好文章的各个点也比较零散,如果内容有错误请在评论区指正。本文将持续更新,至于更新周期,我比较懒还没想好。
环境与工具
- OS :Windows 10
- 代码编辑器:Microsoft Visual Studio 2019(VS2019)
- 插件:Visual Assist X插件
入门
概括性的讲解一下相关概念,并不深入,后续等我学到了再说
什么是C语言
通用计算机编程语言,广泛应用于底层开发。
设计初衷是为了使编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言
C语言发布过的国际C语言标准
C89 C90 C99 C11
C99和C11并不常用 通常意义上还是指C89/C99
创建一个C语言工程
打开VS2019(企业中使用2013和2019的较多,所以我选择2019,安装VS2019自寻教程)看到界面如下图,点击【创建新项目】
进入到创建新项目,此时选择创建一个【空项目】
这时候就该为项目命名了,填写名称并选择路径,然后勾选【将解决方案和项目放在同一目录中】,然后创建就好了
这个时候就可以在左侧看到我们的项目结构了,如果看不到在下方选择【解决方案资源管理器】。
我们可以看到一个工程是包含有头文件和源文件的
然后我们新建一个源文件,也就等于是Java中的类,在源文件目录上点击右键,选择添加→新建项
然后出现了新建文件的窗口,我们选择C++文件,但是在下方文件名称将后缀改成==.c==,我暂时也不知道为啥,后面学到了再说吧。点击【创建】
现在已经可以开始我们愉快的CODING了
代码书写风格
书写风格这方面还是比较主观的,但是两者之间依然存在差异,虽然这里记录了该项,但是有Java经验,我还是会不由自主的向Java靠拢
- {} 在C语言中,左大括号一般会另起一行(在JAVA看来就是异类!)
void main()
{
//do something...
if(a == 1)
{
//do something...
}
}
后续补充
Hello World
按照传统,接触一门新语言我们应该写一个HelloWorld(就算是学书法,拿毛笔也得写!!!)
#include <stdio.h>//这里是引入打印的头文件 等价与 JAVA中的import xxxxxx
void main() {
//printf()打印的函数 等价于JAVA中的 System.out.printf() 后面不会换行
printf("Hello World");//Hello World
}
写完代码之后点击上方的执行
弹出控制台,输出Hello World,说明Hello World就写好了
基本数据类型
与JAVA不同,C的基本类型包括
int--------------整型
long-----------长整型
long long----长长整型
short----------短整型
float-----------浮点型
double-------双精度浮点型
char----------字符型
可以看到,JAVA中的boolean类型在C里是没有的,因为在c语言中true/false使用int来代替
0-false
1-true
此处引申一下,c语言中是没有String类型的,字符串变量使用char[]来接收,后面会详细介绍
我们现在来看一下各个基本类型的大小
#include <stdio.h>
void main() {
printf("%d", 100);//printf方法中用%d占位打印整型,%f对应float,%lf对应double
//获取各个基本类型的大小 sizeof() 返回值类型为int 单位为字节
printf("int size = %d\n", sizeof(int));
printf("short size = %d\n", sizeof(short));
printf("long size = %d\n", sizeof(long));
printf("long long size = %d\n", sizeof(long long));
printf("double size = %d\n", sizeof(double));
printf("float size = %d\n", sizeof(float));
printf("char size = %d\n", sizeof(char));
//输出结果
//int size = 4
//short size = 2
//long size = 4
//long long size = 8
//double size = 8
//float size = 4
//char size = 1
}
常量与变量
变量有全局变量和局部变量,这点和JAVA类似
当局部变量和全局变量命名冲突时,局部变量优先
#include <stdio.h>
int a = 100;//全局变量
void main() {
int a = 10;//局部变量
printf("%d\n", a);//10 局部变量与全局变量冲突时 局部变量优先
}
全局变量在整个工程中都是可用的,跨文件访问全局变量需要使用extern关键字声明
test1.c代码
int a = 2022
test2.c代码
#include <stdio.h>
//使用extern声明外部全局变量
extern int a;
void main() {
printf("%d", a);//输出2022
}
常量
常量有四种
- 字面常量
void main(){
124;
12.5;
'a';
//以上均为常量,虽然没有意义,但是确实是常量
}
- 由const修饰的常量,由const修饰的叫常变量,该变量不可被修改,但是从根本上讲还算是变量,但是具有常属性
int main(){
const int a = 2;
a = 3;//语法不通过,因为由const修饰的常变量的值不可变
int arr[a] = {0};//语法不通过,因为数组的长度不可以使用变量修饰
}
3.标识符常量,由#define定义的常量
#define MAX 10000;//定义一个常量
void main(){
// MAX = 20;// 语法不通过
int a = MAX;
printf("a = %d",a);//打印a = 10000
}
- 枚举常量,类似JAVA的枚举类型
枚举值没有定义初始值时,从第一个开始默认值为0,依次往下为1,2,3…
定义了初始值时,从定义初始值的枚举开始依次往下递增
#include <stdio.h>
enum gender
{
MALE,
FEMALE
};
enum status
{
BEGIN = 1,
DOING,//第一项定义了初始值为1 此时该值为2
END
};
enum blood
{
A,
B,
AB = 1,
O
};
void main() {
int a = MALE;
printf("%d\n", a);//0
printf("%d\n", DOING)//2;
printf("%d\n", A);//0
printf("%d\n", B);//1
printf("%d\n", AB);//1
printf("%d\n", O);//2
int arrr[BEGIN] = {0};//语法通过
}
字符串(与Java差异较大)
与Java不同,C语言没有String类型存放字符串,定义字符串的方式是char数组
void main(){
char str1[] = "asdf";
}
使用""双引号引起来的字符串,最后面会隐式增加一个【\0】,表示字符串结束。
\0不计入字符串长度,也可以使用字符数组的初始化方式,但是最后一个元素必须为【\0】
在使用F10调试过程中可以看到
如果使用字符数组的方式,最后一个元素如果不为【\0】,会出现乱码情况
字符串存放在内存中,指针从字符串的开始依次读取,直至读取到\0结束
如果找不到\0,指针则会一直往下读取,直至在内存空间中读取到\0为止,因此会读取到随机值,造成不良效果
void main() {
printf("%s", "asd");
char str1[] = "asdf";
char str2[] = { 'a','s','d','f'};//没有以\0结束
printf("%s\n",str1);//asdf
printf("%s\n", str2);//asdf烫烫烫烫asdf
//strlen() 返回int类型 计算字符串长度 需要引入string.h头文件
printf("%d\n", strlen(str1));//4 \0不计入总长度
printf("%d\n", strlen(str2));//16 此为随机值
}
操作符
这里挑选记录与JAVA不同的运算符
- sizeof 获取对象的大小
sizeof属于一个操作符,不是函数,获取基本类型或者变量的大小,单位为字节
int a = 10;
printf("%d\n",sizeof(int));//4 int类型 占用4字节
printf("%d\n",sizeof(a));//4 a为int类型 占用4字节
//sizeof在获取变量的大小的时候 可以不使用括号 因为它不是函数 是一个操作符
//但是为了规范语法一般还是带上括号
printf("%d\n",sizeof a);
//sizeof在获取数组的大小时 获取的是数组的总大小
int arr[10] = {0};
//40 arr为int数组,int类型为4字节,arr定义的长度为10,所有总大小为4*10
printf("%d\n",sizeof(arr));
//基于以上 可以获取数组的长度 数组的总大小/元素的大小=数组长度
printf("%d\n",sizeof(arr)/sizeof(arr[0]))//10
- ~ 按位取反
按位取反,将操作数的二进制位全部取反,1变成0,0变成1
这里补充学习到一点二进制相关知识
//一个整数的二进制有表示有三种 原码 反码 补码,内存中存储的是 补码
//负数的原码 以-1为例
//10000000 00000000 00000000 00000001 //最高位是符号位 最高位为1表示负数
//反码 原码的符号位不动 其他位全部取反
//11111111 11111111 11111111 11111110 //最高位符号位不动 其余位全部取反
//补码 反码+1
//11111111 11111111 11111111 11111111 //反码+1
//正数的原码 反码 补码均一致
int a = 0;
printf("%d\n",~a);//-1
//整数在内存中存放的是补码
//00000000 00000000 00000000 00000000 //0的二进制序列
//取反
//11111111 11111111 11111111 11111111 //此时 与-1的补码相同 所以0取反为-1
- , 逗号表达式
多个表达式以逗号分隔,从左往右依次执行,逗号表达式的执行结果为最后一个表达式的执行结果
int a = 3;
int b = 2;
int c = 1;
int d = (a = a + b,b = b + c,c = a + b);
// 从左往右依次执行
// a=3+2=5 b=2+1=3 c=5+3=8
//(a = a + b,b = b + c,c = a + b)
printf("%d\n",d);//8 最右边的执行结果为8
- -> 取出指针变量属性 略 后面指针的内容详解
- * 指针变量标识 同上
- & 指针相关 同上
关键字
这里挑选记录与JAVA不同的关键字
- extern
引用外部的全局变量和函数
test1.c
//外部定义全局变量和函数
int count = 2022;
int add(int a, int b) {
return a + b;
}
test2.c
extern int count;//引用全局变量
extern int add(int a, int b);//引用函数
void main() {
printf("%d\n", count);//2022
printf("%d\n", add(12 , 24));//36
}
-
static
修饰局部变量,使局部变量放在静态区中,生命周期与全局变量相同
修饰全局变量,使全局变量不能在外部文件引用,只能在自己所在的源文件中使用
全局变量具有外部链接属性,一旦被static修饰就会被修改为内部链接属性
修饰函数,使函数无法被外部引入,将函数的外部链接属性变成内部链接属性 -
define
用来定义常量和宏
#define int MAX = 1000;//定义一个常量
#define int ADD(X,Y) X+Y;//定义一个宏
printf("%d\n",ADD(3 + 4));//7
printf("%d\n",3 * ADD(3 + 4));//13
//因为此时调用宏 宏为替换操作
//将3*ADD(3+4)替换为3*3+4这时等于13
//如果需要按照预想的规则 3*(3+4)得到21
//应该将宏定义为
//#define int ADD(X,Y) ((X)+(Y));
- typedef
为一种数据类型定义一个新名字
typedef int yoho;//typedef 原名 新名
void main(){
yoho i = 0;
}
指针(C语言重点)
指针就是对象存放在内存中的地址
void main() {
int a = 10;
char c = 'c';
//&获取a的地址,%p为打印 地址变量 占位
printf("%p\n", &a);//00EFF974
int* pa = &a;//地址的存储变量用对象的基本类型和*来表示对象的指针
char* pc = &c;
printf("%p\n", pc);//00EFF96B
}
解引用操作,根据指针获取指向对象的值
int a = 10;
int* pa = &a;
*pa = 20; //*pa 为解引用操作,通过指针去修改变量的值
printf("%d\n",a);//20
指针变量的大小
void main() {
printf("%zd\n", sizeof(int*));
printf("%zd\n", sizeof(short*));
printf("%zd\n", sizeof(long*));
printf("%zd\n", sizeof(long long*));
printf("%zd\n", sizeof(float*));
printf("%zd\n", sizeof(double*));
printf("%zd\n", sizeof(char*));
}
//32位环境下均是4 64位环境均是8
结构体
结构体 == Java中的对象实体类Bean
struct Stu//这是一个结构体
{
char name[20];
int age;
int gender;
};
void main() {
struct Stu s = {"张无奇",25,1};//声明一个结构体的同时初始化
//通过.的方式取值
printf("name = %s,age = %d,gender = %d\n", s.name, s.age, s.gender);
//结构体也可获取指针变量
struct Stu* ps = &s;
//通过指针变量.的方式获取指针指向变量的值
printf("name = %s,age = %d,gender = %d\n", (*ps).name, (*ps).age, (*ps).gender);
//通过指针变量->操作符获取指针变量的值(推荐)
printf("name = %s,age = %d,gender = %d\n", ps->name, ps->age, ps->gender);
}
//三个输出结果相同 name = 张无奇,age = 25,gender = 1