600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 【3小时学会C语言】横向对比/纵向剖析 轻松学习C语言

【3小时学会C语言】横向对比/纵向剖析 轻松学习C语言

时间:2021-03-16 19:10:25

相关推荐

【3小时学会C语言】横向对比/纵向剖析 轻松学习C语言

文章目录

前言1.前置知识1.1 序1.2 编译器和解释器2.C语言技术点及应用技巧2.1 调试用IDE2.2 数据类型2.2.1 c2.2.2 java2.3 运算符2.4 条件判断2.5 循环2.6 函数2.6.1 函数的基本使用2.6.2 函数的可变参数:va_list2.6.3 函数的入参2.6.4 输出打印2.7 数组2.7.1 基本使用2.7.2 如何获得数组元素个数2.7.3 数组作为函数参数2.8 字符串2.8.1 定义字符串的三种形式2.8.2 字符串必须以[ \0 ]结尾。如果不是,就会出现乱码2.8.3 字符串的段异常2.8.4 字符串处理的头文件

前言

😃 沒有前言 😃

1.前置知识

1.1 序

计算机实际上是由底层一系列的数字电路实现的,通过输入电平的高(1)和低(0)和寄存器之间的组织关系来决定输出的结果,这个过程就是计算器的计算能力。

小学2年级的时候老师教过我们,最开始计算机是通过打孔来实现变成的,比如打了孔就代表输入1,不打孔就输入0,但是这样很麻烦,后来为了提高开发者的开发效率,就封装了一些具体的功能例如:加减乘除,映射到某些语句,只需要输入这些语句,底层的数字电路就会去实现对应的功能,汇编语言就诞生了。

但是汇编语言虽然比机器语言好用了很多,但是还是很麻烦,之后开发者们开发出了一系列语言,其中C语言就是比较火热的高级语言之一,在C的基础上又衍生出了C++,之后为了更便于开发者们的上手以及考虑到不同环境的隔离,又衍生出了Java,Python这些抽象程度更高的语言。

1.2 编译器和解释器

会出现上面这些语言迭代的根本原因,本质上是因为编译器越来越智能了,功能越来越强大,能够更加"人性化、智能化"地把我们写的高级语言最终翻译成底层的0/1机器码,其中编译又可以分为静态编译和动态编译。

📔静态编译

所谓静态编译,就是把代码编译完成0/1机器码后再去运行,比如c语言的gcc编译器,c++的g++,都是基于这个原理。

📔动态编译

所谓动态编译通过存在于解释型语言中,就是在代码运行过程中,解释器发现某段代码运行的特别频繁,他就会直接把这段代码做一个"运行时编译",直接编译成机器码,之后访问这段代码就直接执行,而不会走解释器的逻辑。

📔编译

写到这里感觉还是有必要解释一下编译和解释的区别,因为有些小伙伴对这个概念的理解不够精准。通俗易懂的说,编译就是一段代码在运行前,要先编译成机器码,然后运行时会对这段机器码进行一个直接的执行,例如windows下的.exe程序。但是这种做法显然会受到操作系统环境的影响,所以比较典型的编译型语言有:c/c++,有些语言他比较"智能"(虽然其实也很笨了),他兼具了这种特性,其实他是一种解释性语言,但是在运行时会动态判断当前运行的代码是不是很"热",如果很"热"就给他"加个缓存",这个就是动态编译,比较典型的就是java中的JIT动态编译器。

📔解释

比较典型的有python、java,都是这一类,解释器会直接把我们手敲的源代码直接解析成中间的字节码,然后在运行时把字节码解释成机器码进行执行。这种因为有个中间态,所以运行效率相对纯粹的编译型语言会低一点,可以通过兼容动态编译的方式进行优化。其中python的解释器会因为运行平台的不同而不同,java的解释器由jvm虚拟机决定。

📔运行效率

静态编译 约等于 动态编译 > jvm模板解释器 > 解释器

2.C语言技术点及应用技巧

因为博主本身对java比较熟悉,我会尽量对比着java和c的差别来讲解c语言,如果有其他语言比较熟悉的小伙伴也可以对比着来复习c语言,会有一些不一样的体会。

本文章涉及的C内容对于指针暂先不详细讲解,但是会用到也会提到。详细讲解我会再开一章,因为这个确实很重要。还有对于结构体、共用体等边边角角的部分,我也会放到下一篇讲解。下面开始进入正题(偶尔涉及的一些奇奇怪怪的知识,有兴趣的可以自行了解)。

讲解顺序:

数据结构

运算符

循环结构

条件判断

函数

数组

字符串

2.1 调试用IDE

📔 windows: visual studio,clion

📔 linux: clion

勤劳是中华民族的传统美德,这边大家自行通过搜索引擎下载一下即可,简单了解下使用就可以上手了。博主windows下用的是vs,毕竟微软亲儿子性能肯定会更高一点

2.2 数据类型

2.2.1 c

📔 c:windows(ide设置32位)

📔 c:linux(这个和不同linux-os有关)

可以通过sizeof()测试,比如:

long a = 6;printf("long型变量的长度: %d\n", sizeof(a));

📔注意:c语言中是没有原生的布尔类型的(bool),他有2种实现方式,一种是通过枚举来实现,例如:

enum Bool {FALSE(0),TRUE(1)}

另外一种是通过#define来直接定义常量,例如:

#define TRUE 1#define FALSE 0

📔c语言中所有的数据类型还又分为: 有符号数,和无符号数,无符号数需要在定义时显式添加关键字:unsigned,不添加就默认是有符号数,他们之间的区别就是有符号数的最高位代表符号位:0代表正数,1代表负数。

例如:255对应的16进制是0xff,

char a = 0xff;printf("%d\n", a);unsigned char b = 0xff;printf("%d\n", b);

补充:c语言中的不同数据类型,事实上就对应着不同的寄存器大小,反过来我们在看不同寄存器的时候,也可以理解成不同的变量。

|63..32|31..16|15-8|7-0||AH.|AL.||AX.....||EAX............||RAX...................|

2.2.2 java

2.3 运算符

不同语言之间,运算符这一块基本可以认为是相同的,没有太大差异。

📔算术运算符:

📔关系运算符:

📔逻辑运算符

📔位运算符

📔赋值运算符

其他不常用的遇到了再去搜索一下。

2.4 条件判断

不同语言之间,基本相同。

if :单独判断

if(condition) {statement(s);}

if - else if:多分支判断

if(condition1) {statement(s);}else if(condition2){statement(s);}

if - else if - else:带else的多分支判断

if(condition1) {statement(s);}else if(condition2){statement(s);}else {statement(s);}

例如:

#include <stdio.h>int main (){/* 局部变量定义 */int a = 10;/* 使用 if 语句检查布尔条件 */if( a < 20 ){/* 如果条件为真,则输出下面的语句 */printf("a 小于 20\n" );}printf("a 的值是 %d\n", a);return 0;}

输出:

a 小于 20a 的值是 10

2.5 循环

不同语言之间,基本相同。

while:先判断后执行

while(condition){statement(s);}

do while:先执行后判断

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

for循环:

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

例如:

#include <stdio.h>int main (){/* for 循环执行 */for( int a = 10; a < 20; a = a + 1 ){printf("a 的值: %d\n", a);}return 0;}

2.6 函数

2.6.1 函数的基本使用

📔函数定义格式 ,跟Java一样的

📔声明与定义:可以分开,也可以不分开

c语言中的函数如果不声明的话,必须按顺序写,调用;否则就必须要函数声明

int t1(){return t2();}int t2(){return 10;}

如果不写函数声明,因为t2()的定义在t1()之后,函数调用会报错,在开头前加一个函数声明即可int t2();

java中因为他用到了c++的动态绑定,所以不存在函数调用的顺序问题。

2.6.2 函数的可变参数:va_list

VA_LIST 是在C语言中解决变参问题的一组宏,变参问题是指参数的个数不定,可以是传入一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有实际的名称与之相对应,用起来是很灵活。

用法实例:

#include <stdarg.h> int AveInt(int,...);void main(){printf("%d/t",AveInt(2,2,3));printf("%d/t",AveInt(4,2,4,6,8));return;}int AveInt(int v,...){int ReturnValue=0;int i=v;va_list ap ;va_start(ap,v);while(i>0){ReturnValue+=va_arg(ap,int) ;i--;}va_end(ap); return ReturnValue/=v;}

可参考:va_list使用方法

2.6.3 函数的入参

📔传值调用

默认情况下,C 语言使用传值调用方法来传递参数。一般来说,这意味着函数内的代码不会改变用于调用函数的实际参数。函数 swap() 定义如下:

/* 函数定义 */void swap(int x, int y){int temp;temp = x; /* 保存 x 的值 */x = y; /* 把 y 赋值给 x */y = temp; /* 把 temp 赋值给 y */return;}

现在,让我们通过传递实际参数来调用函数 swap():

#include <stdio.h>/* 函数声明 */void swap(int x, int y);int main (){/* 局部变量定义 */int a = 100;int b = 200;printf("交换前,a 的值: %d\n", a );printf("交换前,b 的值: %d\n", b );/* 调用函数来交换值 */swap(a, b);printf("交换后,a 的值: %d\n", a );printf("交换后,b 的值: %d\n", b );return 0;}

当上面的代码被编译和执行时,它会产生下列结果:

交换前,a 的值: 100交换前,b 的值: 200交换后,a 的值: 100交换后,b 的值: 200

📔引用调用

通过引用传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

/* 函数定义 */void swap(int *x, int *y){int temp;temp = *x; /* 保存地址 x 的值 */*x = *y;/* 把 y 赋值给 x */*y = temp; /* 把 temp 赋值给 y */return;}

现在,让我们通过引用传值来调用函数 swap():

#include <stdio.h>/* 函数声明 */void swap(int *x, int *y);int main (){/* 局部变量定义 */int a = 100;int b = 200;printf("交换前,a 的值: %d\n", a );printf("交换前,b 的值: %d\n", b );/* 调用函数来交换值* &a 表示指向 a 的指针,即变量 a 的地址* &b 表示指向 b 的指针,即变量 b 的地址*/swap(&a, &b);printf("交换后,a 的值: %d\n", a );printf("交换后,b 的值: %d\n", b );return 0;}

当上面的代码被编译和执行时,它会产生下列结果:

交换前,a 的值: 100交换前,b 的值: 200交换后,a 的值: 200交换后,b 的值: 100

📔与java的比较:

很显然可以看入参如果传入的是某个变量的地址&,也就相当于变成了java中的引用数据类型,这也是c语言操作基本数据类型的一个灵活之处。

2.6.4 输出打印

c语言一般用printf函数,c++一般用cout。但是cout没有printf好用

#include <stdio.h>int main() {printf("hello world\n");return 0;}

格式化参数:

%d 十进制有符号整数(常用)%u 十进制无符号整数(常用)%f 浮点数%s 字符串(常用)%c 单个字符(常用)%p 指针的值(常用)%e 指数形式的浮点数%x, %X 无符号以十六进制表示的整数(常用,打印指针一般会用到这个)%o 无符号以八进制表示的整数%g 把输出的值按照%e或者%f类型中输出长度较小的方式输出%p 输出地址符%lu 32位无符号整数%llu 64位无符号整数可以在“%”和字母之间插进数字表示最大场宽%3d表示输出3位整型数,不够3位右对齐

其他常用函数可以查文档

2.7 数组

c中的数组一般都是配合指针用的,非常灵活

2.7.1 基本使用

int arr[3] = {1, 2, 3 };

也可以不指定初始值,不指定的时候就是初始元素个数

举例:

#include <stdio.h>int main (){int n[ 10 ]; /* n 是一个包含 10 个整数的数组 */int i,j;/* 初始化数组元素 */ for ( i = 0; i < 10; i++ ){n[ i ] = i + 100; /* 设置元素 i 为 i + 100 */}/* 输出数组中每个元素的值 */for (j = 0; j < 10; j++ ){printf("Element[%d] = %d\n", j, n[j] );}return 0;}

编译运行会输出数组中的每个元素

2.7.2 如何获得数组元素个数

c中没有类似java中的size()方法,可以直接获取数组中的元素个数,需要手动计算

int len = sizeof(arr) / sizeof(short);

2.7.3 数组作为函数参数

📔三种写法(一般前两种写法)

void myFunction(int param[]);void myFunction(int *param);void myFunction(int param[10]);

数组作为函数参数,它的本质是引用传值

void change(char* arr) {arr[0] = 'c';}int _tmain(int argc, _TCHAR* argv[]){char str[] = "cry";change(str);printf("%s\n", str);return 0;}

2.8 字符串

字符串string这个东西,事实上是不存在的,他是一串字符char组成的一个序列,比如java的String类其实用的就是c++的string,c++的string其实就是对字符功能做了一个封装

2.8.1 定义字符串的三种形式

char str1[] = {'C', 'R', 'Y'};char str2[] = "cry";char* str3 = "cry";

2.8.2 字符串必须以[ \0 ]结尾。如果不是,就会出现乱码

char str0[] = {'C', 'R', 'Y' };printf("%s\n", str0);char str1[] = {'C', 'R', 'Y','\0' };printf("%s\n", str1);

如果是以数组形式定义的字符串,一定要手动补'\0',如果是char str2[] = "cry"; char* str3 = "cry";这样,以指针或者直接""字符串的形式定义的字符串,c会直接为我们补上\0这个结尾,我们可以直接看他的内存中实际是怎么存的。

先打出这个数组的首地址:

char str0[] = {'C', 'R', 'Y' };printf("%p\n", str0);

可以看到是0x00cffdf4

然后把他丢到vs的debug模式下,

可以看到在CRY后面是cc,这个是内存中里没有分配的内存,在windows下默认初始化为cc,对应unicode码就是烫,突然有点怀念逝去的青春~

2.8.3 字符串的段异常

char str1[] = "cry";char* str2 = "cry";str1[0] = 'k';*str2 = 'k';

0xC0000005,这是个字符串使用中非常常见的异常。如果是以数组形式构建的字符串,是可以修改的。但如果是以指针形式构建,他就是一个字符串常量,只读不可改,试图修改他就会报字符串段异常。

0xC0000005,这个在c++中有奇妙的使用,c++中的安全点就是通过这个技术暂停所有线程的,很好理解,段异常会通过中断被os捕获,捕获之后就可以调用安全点的停止;等中断结束后,再把安全点恢复,线程继续执行。

这个过程是不是很熟悉,jvm发生full gc的时候,会发生stw(stop the world),暂停所有的线程,等gc完毕再把线程重启,就是这个技术点。

2.8.4 字符串处理的头文件

下面的实例使用了上述的一些函数:

#include <stdio.h>#include <string.h>int main (){char str1[14] = "runoob";char str2[14] = "google";char str3[14];int len ;/* 复制 str1 到 str3 */strcpy(str3, str1);printf("strcpy( str3, str1) : %s\n", str3 );/* 连接 str1 和 str2 */strcat( str1, str2);printf("strcat( str1, str2): %s\n", str1 );/* 连接后,str1 的总长度 */len = strlen(str1);printf("strlen(str1) : %d\n", len );return 0;}

可以在 C 标准库中找到更多字符串相关的函数。

更新不易,求求客官姥爷们留下尊贵的一键三连哈~

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。