程序员自学c语言:程序员教你学C语言
程序员自学c语言:程序员教你学C语言函数体语句块{函数:函数就算不是C语言里最重要的概念,也是最重要的概念之一,事实上我前面讲的好几章内容,但是为了函数做铺垫的。在讲函数之前,我要介绍从数学里提取出来的八种解题方法,分解、重组、普通、特殊、类比、引入、归纳和逆推,当然我不可能跟大家全部讲,因为这是要写给我徒弟的教程。在《程序员的自我修养》一书中提到“计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决”,一看这句话是不是特别高端、特别牛逼、特别哇塞,这个其实说白了就是“引入”辅助元素,早在好多年前就由数学家阐述了这种方法,往小点说我们要交换a、b两个变量的值的时候,我不能直接a=b;b=a这样换,因为当a=b发生时,a原来的值就被b的值给冲掉了,当你用b=a时其实是b=b,所以我们想达到这个目的就要引入一个变量如temp,这样temp=a;a=b;b=temp就可以了;往大点说从整个计算机发展的过程来看,计算机发展过
很多小伙伴都老是会碰到疑问,其实还是基础没打扎实,这些题如果你不看答案你能知道多少呢?如果还有很多不知道就证明基础没打扎实,如果你还在入门纠结,如果你还在苦恼怎么入门!小编有个建议,可以加小编弄的一个C语言交流基地,大家可以进入交流基地:565122788,里面新手入门资料,可以说从零到项目实战,都是可以免费获取的,还有程序员大牛为各位免费解答问题,热心肠的小伙伴也是蛮多的。不失为是一个交流的的好地方,小编在这里邀请大家加入我的大家庭。欢迎你的到来。一起交流学习!共同进步!小编等你!还有前面没有看的同学最好从程序员教你学C语言(一)开始看哦,尤其是基础还没打扎实的同学!
字符数组
前面讲的都是int类型的数组,我们现在看看字符数组,其实就是字符串,在C语言里没有提供字符串数据类型,而是使用char类型的数组来存储字符串。什么是字符串呢,我们经常见到的其实是字符串常量,字符串常量是放在一堆双引号中的一串字符或符号,一堆双引号之间的任何内容都会被编译器视为字符串,我们经常使用的printf函数的第一个参数就是一个字符串,printf("hello world")里面"hello world"就是字符串常量。当然,有些字符是没法直接在字符串中使用的,这时候要用到转义字符,比如"引号本身,如果你写成"hello "world",放在printf里面是肯定要报错的,这时候要在"前面加反斜杠\,"hello \"world",同样的还有反斜杠本身,如果要使用\要在双引号里面加\\,这样才代表一个\,还有一条默认的规则是,每个字符串的末尾都会被自动填上一个空字符(C中写为\0)表示字符串的结束,所以字符串的长度永远比字符串中的字符数多1,不信的话你可以试着输出sizeof("abc"),答案是4而不是3。下面我们来看看字符数组的简单应用:
这个程序的作用就是我们输入若干行字符串,我这里是输入了5行,如果是空行就会结束输入,然后程序会把我们输入的字符串全部打印出来。其中涉及到三个新知识点:1是define宏定义的应用,宏定义其实就是一种简单的替换,#define 标识符 常量数据,就是把前面的标识符替换成后面的数据,我们在这里用到了#define MAX 100,后面所有出现的MAX的地方编译后都会被替换成100,给常量取名有时候会方便我们的理解,当然宏定义还有其他的用途我们暂时用不到;2是register关键字,我们知道我们平时定义的变量是存放在内存里的,如果加上register就变成了直接存放在cpu中寄存器里的变量,使用寄存器变量要比内存变量要快很多,但是寄存器的个数是很有限的,所以我们很少会使用寄存器变量,这里就当是个新知识点来讲了;3是gets函数和putchar函数,gets从标准输入设备读字符串,可以无限读取,以回车结束读取,说白了就是读取一行,而putchar会将一个字符输出
函数:
函数就算不是C语言里最重要的概念,也是最重要的概念之一,事实上我前面讲的好几章内容,但是为了函数做铺垫的。在讲函数之前,我要介绍从数学里提取出来的八种解题方法,分解、重组、普通、特殊、类比、引入、归纳和逆推,当然我不可能跟大家全部讲,因为这是要写给我徒弟的教程。在《程序员的自我修养》一书中提到“计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决”,一看这句话是不是特别高端、特别牛逼、特别哇塞,这个其实说白了就是“引入”辅助元素,早在好多年前就由数学家阐述了这种方法,往小点说我们要交换a、b两个变量的值的时候,我不能直接a=b;b=a这样换,因为当a=b发生时,a原来的值就被b的值给冲掉了,当你用b=a时其实是b=b,所以我们想达到这个目的就要引入一个变量如temp,这样temp=a;a=b;b=temp就可以了;往大点说从整个计算机发展的过程来看,计算机发展过程不就是进行层的“引入”么,从机器指令到汇编语言到高级编程语言,中间层屏蔽底层细节,向上层提供更加简便的操作接口,我们现在学习C可比以前的程序员要学机器指令和汇编要简单多了,当然我在这里只是简单的提下“引入”这种方法。C语言这类结构化语言用到的最主要的方法是“分解”,C采用的是自顶向下的设计,逐步细化,把一个问题分解成几个小问题,最终得到一个个容易编码的小问题组成的集合,在C中,用函数编写解决由分解而来的小问题的代码,当然,这些函数要和其他函数结合,最终用在解决最初问题的main函数里,所以我们前面说main是唯一不可或缺的。除了main之外,我们来看一般函数的定义:
返回值类型 函数名(参数1类型 参数1,参数2类型 参数2,,,,,,)
{
函数体语句块
}
函数由函数头(包括返回值类型、函数名、参数列表)和包含函数体的语句块组成,函数体语句块就是我们前面为了解决分解而来的小问题写的代码,那为什么要有函数头呢,因为我们前面说了用C语言解决问题是把一个问题分解成很多小问题,然后每个小问题编写一个函数,在这种情况下,要想函数之间彼此能实现访问,我们就要提供一种方式能够在一个函数里使用另一个函数,这个就叫函数的调用,需要通过函数名来实现调用,而参数列表是调用者传递给被调用函数的信息,返回值则是被调用函数返回给调用者的信息。还记得我们的第一个程序吗?
#include <stdio.h>
int main(int argc char *argv[])
{
printf("Hello C-Free!\n");
return 0;
}
在main函数里面就有调用printf("hello world!\n);这样的语句,其实我们可以加上一点代码:
#include <stdio.h>
int main(int argc char *argv[])
{
int i = printf("Hello C-Free!\n");
printf("i = %d\n" i);
return 0;
}
我们可以发现打印出了i的值为14,这个值其实就是被调用函数printf返回给调用者main函数的值,我们用i把这个值给保存了起来,printf函数的返回值代表了被打印的字符数。
下面我们来看一个包含有完整的自定义函数的程序:
#include <stdio.h>
int add(int a int b)
{
return a b;
}
int main(int argc char *argv[])
{
int i j;
printf("请输出两个整数:\n");
scanf("%d %d" &i &j);
printf("i j=%d\n" add(i j));
return 0;
}
当我们输入5 6的时候,回车就会打印出11,在观察我们的函数和上面说的一般函数的定义,我们定义了一个函数,函数名叫add,函数的参数有两个,整数a和b,返回值也是整数型,函数里函数体只有一行语句,就是返回了a b的值,看看我们在调用的时候用到了add(i j),我们的函数相当于定义了一个加工厂,当客户(调用者)传递进来符合条件的数据(参数)后,加工厂就会按照提前设定好的步骤(函数体语句块)对数据进行加工,然后把加工后的数据返回(返回值)给客户。
当然,前面的例子我们大可不必定义成函数,直接在打印函数里写printf("i j=%d\n" i j);就好了,但是现实中却往往有很多复杂的问题,不是我们简单的一两句代码就能搞定的。比如大家都用过压缩软件吧,很多文件经过压缩软件处理后体积就小很多,当我们再次要使用文件时,只需要解压就可以正常使用了,确实很方便,但是有多少人关心过它究竟是如何实现的呢?然后下面我们来写一个最简单的压缩函数,用它来压缩字符串,比如我们输出的是“eeeeeaaaff" ,那我们压缩为 "e5a3f2",把相同的字符去掉,仅在原来的字符后面跟一个数字表示共有多少个相同的连续字符。
学习交流群(565122788)
可以看到,我们把压缩字符串的代码打包成了函数Compess,它有三个参数,参数1是转换后的字符串,也是我们最后的返回值,参数2是要转换的字符串,参数3是参数2的长度,注意strlen函数就是求字符串长度(不包括结束符'\0')的函数。主要的代码逻辑就是Compess函数里面的for循环,进入循环后,我们首先if(oldString[iOld] == oldString[iOld 1])比较当前字符和它的下一个字符是否相等,如果相等就把字符数量count加1,如果不相等,就把当前字符赋值给newString[iNew ],然后把当前字符的个数赋值给newString[iNew ],想想这里iNew有加1的操作是为什么,还有我们的数字count之所以要加48,是因为数字ascii码值比数字对应的字符要小48,比如数字48对应的ascii码值是字符'0',数字49对应的ascii码是字符'1',这里我们要把count数组加48转换成它对于的字符。我们拿oldString1来讲,"eeeeeaaaff"的iOld从0~4对应的字符都是e,直到oldString[5]='a',这时if判断不成立,就会执行到else语句,在这里把newString[0]赋值为e,并把newString[1]赋值为5;然后接着走循环一直到字符串oldString被读完。这个程序虽然短,但是要好好理解下。
当然程序写到这里还不算结束,因为我们只是把字符串进行了压缩,当用户想要再使用原来的字符串时,我们还需要把字符串给还原(这就是解压过程),于是我们可以这样写:
可以看到解压过程我们也用了一个函数UnCompess,解压过程相当于压缩的逆运算,不过有点要注意的是,我们的这一行if(oldString[iOld 2]>='0'&&oldString[iOld 2]<='9')是为了判断数字字符oldString[iOld 1]后面的oldString[iOld 2]是否也是数字字符,如果是e35这种,那代表有35个重复的字符e,所以我们就进行了,count = (oldString[iOld 1]-48)*10 (oldString[iOld 2]-48);这样的一步操作,相当于把字符'3'转换成了整数3并且乘以10,然后加上个位数。最后的newString[iNew] = '\0';最好是加上,不然可能出现意外的情况。这就是我们平时使用的压缩软件的最简单实现了,当然我们最后要把它们和在一个程序里面,事实上我在这里只是把上的Compess函数注释掉了,把注释去掉后就是一个完整的压缩与解压程序了。
前两天有人在群里问我,编译器自动生成的代码int main(int argc char *argv[]),这里面的参数argc和argv究竟是什么?因为前面没有讲到函数,所以一直不好解答。今天说一下,我们先编写下面的程序运行:
我们可以看到打印结果,这里我们记录下argv[0]的值C:\Users\Administrator\Desktop\main\mingw5\main.exe,然后我们点击电脑左下角的windows图标,并在弹出框中输入cmd
点击回车后弹出来下面的黑框,这个就叫命令行cmd,我们可以看到标题上它的路径是在C:\Windows\system32\cmd.exe,命令行是属于操作系统的一部分,它最开始是古老的Dos操作系统下的东西,但是微软为了兼容以前在Dos下运行的软件,就把cmd也并入了windows中,我们在用编译器新建工程的时候选择的是控制台程序,其实就是在cmd上运行的程序,我们点击编译器上的运行出来的界面其实和cmd是一样的黑框框。这里我们不使用编译器的运行,而是直接在cmd中输入前面记录的argv[0]的值C:\Users\Administrator\Desktop\main\mingw5\main.exe,点击回车我们也可以发现其实这和点击编译器上的运行时完全一回事。
现在我们输入C:\Users\Administrator\Desktop\main\mingw5\main.exe a b c,再点击回车我们看看结果:
C/C 学习交流群,欢迎大家一起来交流提升。565122788进群就能获取C语言新手学习大礼包
看到没有,argc和argv都打印出来了,有什么发现没有,argc表示是操作系统(其实这里就是cmd)传递给我们的程序的参数个数,argv则是操作系统传递给我们程序的参数列表(都是字符串,其中argv[0]是我们的程序路径),现在我们应该很清楚了main函数的这两个参数的作用了吧,对于我们的main函数来讲,操作系统(操作系统也是由代码组成的,而且操作系统的绝大部分都是使用C语言编写的,也就是在操作系统这个程序的代码中调用了我们程序的main函数)就是调用者,main是被调用者,所以这两个参数都是操作系统传递给我们程序的。
今天就到这里,欲知后事如何且听下回分解(手动滑稽)~