听说C语言很难?怎么不来看看我这篇(一)

1.写在前面

今天是大年初七,经过了几天的拜年,我又来开始更新我的C语言的系列了,一个Java程序员学C语言的历程,俗话说,学习一门新的语言,入门的书,比较重要,国内都是谭浩强,讲道理,真的就是垃圾,笔者今天要推荐的就是《C程序设计语言》这本书是由C语言之父:里奇写的,应该没有比这本更好的C语言的入门的书了吧!

2.第一个C语言程序

学过其他的语言的大佬,还是没有学过其他的语言的小白,相信大家学习任何一门语言的第一个程序的都是helloworld,那么我也带大家写一个helloworld,在写这个程序之前,大家的环境要准备好,我用的clion,当然有些人喜欢用vscode,都是可以,萝卜青菜各有所爱嘛!这儿我啰嗦一句,在学C语言之前,希望大家先看完前置的课程《CMake一篇就够了》这样基本前置的环境都准备了,那就让我们手写第一个程序吧!具体的代码如下:

// 包含标准库的内容
#include 

main() // 定义名为main的函数,它不接受参数值
{ //main函数的语句都被括在花括号中
    printf("hello world \n"); //main函数调用库函数printf以显示字符串序列 \n 代表换行符
}

运行的结果如下:

可以看到我们第一个C语言的程序,就写出来了,我们需要了解下,这一大串的代码的意思了,首先就是第一句#include ,从java的程序员的角度,我们可以理解为就是导包,好嘛!但是有的人说了,我是小白,我不理解怎么办,往下看,可以看到我们调用了一个printf函数,但是我们没有写这个函数,从哪儿来的呢?就是从stdio.h中引入的。程序其中一个很大的功能就是帮我们解决重复的问题,例如我们这儿的打印语句,总不能我每次要打印一些东西的时候,我都要写一次这个函数吧!所以聪明的人类,将这个打印的然后封装起来了,就只要写一次,我们只需要在使用的时候引入的这个头文件就可以了,我们可以打开这个头文件看看里面有什么东西,具体的如下:

可以看到实现了这么多的函数,都是可以调用的,就是重复的功能封装成一个函数,这就是抽象的能力,也是我们程序员需要锻炼的能力之一。

我们继续往下看,main() {}这一个整体,我们称之为函数,其中main是叫函数名,这儿我们可以理解为主函数,java的程序员应该是很好理解的,这儿就不赘述,但是对于小白来说,不太好理解,那么我就来说说看,计算机是机器,它比较蠢,所以你必须要给它一个入口,不然它不知道从什么地方开始运行你的代码,这儿main() {},我们称之为主函数,就是计算机会这个函数开始运行。

最后就是看下,我们函数体中的内容了,printf("hello world \n");就是一个打印的语句,调用的是标准库中的函数,这儿我就不多赘述了,后面等讲到这个标准库的时候,我们再来详细的讨论。

这儿还有一个需要我们注意的,就是结尾一个\n是什么意思呢?是换行的意思,在每一种有些特殊的符号,我们需要转义,\就是转义字符,常见的特殊的符合如下:

特殊符号 含义
\n 换行
\t 制表符
\b 回退符
" 双引号
\ 反斜杠

3.变量与算术表达式

我们看看一个程序,使用公式
摄 氏 度 = ( 5 / 9 ) ( 华 氏 度 − 32 ) 摄氏度=(5/9)(华氏度-32) =(5/9)(32)
打印对应华氏度与摄氏度的对照表。具体的代码的如下:

#include 

main() {
    int fahr, celsius;
    int lower, upper, step;

    lower = 0; // 温度表的下限
    upper = 300; // 温度表的上限
    step = 20; // 步长

    fahr = lower;
    while (fahr <= upper) {
        celsius = 5 * (fahr - 32) / 9;
        printf("%3d %6d\n", fahr, celsius);
        fahr = fahr + step;
    }
}

从这个程序上,我们接触一些新的概念:注释、声明、变量、算术表达式、循环、格式化输出。

  1. 注释

    为了更好解释自己的写的代码,一般程序都会有注释的,但是好的程序,一线就看懂了,不用过多的注释的,注释写多了可能会误导别人,注释有单号注释,用//表示,多行注释用/**/表示,上面的程序//温度表的下限就是注释。

  2. 声明变量

    所有的变量必须先声明后使用,声明通常放在函数起始处,在任何可执行语句之前,声明用于说明变量的属性,它由一个类型名和一个变量表组成。常见的数据类型如下:

    类型 含义
    char 字符一个字节
    short 短整型
    long 长整形
    double 双精度浮点数
  3. 循环

    while循环语句的执行方式:首先测试圆括号中的条件,如果条件为真,则执行循环体;然后再重新测试圆括号中条件,如果为真,则再执行循环体;当圆括号中的条件测试结果为假时,循环结束,并继续执行跟在while循环语句之后的下一条语句。

  4. 格式化输出

    printf函数中的%d表示一个占位符,后面输出的变量就会按照这种格式去输出。常见的C语言中的格式化输出的如下:

    格式 含义
    %d 按照十进制整数打印
    %6d 按照十进制整数打印,至少6个字符宽
    %f 按照浮点数打印
    %6f 按照浮点数打印,至少6个字符宽
    %.2f 按照浮点数打印,小数点后面有两位小数
    %6.2f 按照浮点数打印,至少6个字符宽,小数点后有两位小数
    %O 八进制数
    %x 十六进制数
    %c 字符
    %s 字符串
    %% 百分号本身

讲完了一些的前置的知识,我们来看下这个程序的运行结果吧,具体的如下:

听说C语言很难?怎么不来看看我这篇(一)_第1张图片

上面的程序有个比较严重的问题,由于我们使用了整型的运算,这样算出来的结果不是很正确,比如说我们0华氏度对应的摄氏度应该是-17.8,而不是-17。为了得到更加精确的结果,应该使用浮点算数运算。我们需要将程序进行适当的修改。具体的代码的如下:

#include 

main() {
    float fahr, celsius;
    int lower, upper, step;

    lower = 0;
    upper = 300;
    step = 20;

    fahr = lower;
    while (fahr <= upper) {
        celsius = (5.0/ 9.0) * (fahr - 32) ;
        printf("%3.0f %6.1f\n", fahr, celsius);
        fahr = fahr + step;
    }
}

运行的结果如下:

可以看到我们的计算的结果是正确的。我们这儿计算的结果比之前更加准确了。但是我这儿想引出一个很重要的计算的规则。

  • 如果某个算术运算符的所有操作数均为整型,则执行整型运算。
  • 如果一个是整型一个浮点型,那么会将整型自动转换为浮点型,然后再进行计算。

我们学的强类型的语言,所有我们要对类型很敏感,一个变量不单单有值,它还有对应的类型,一个变量只有在有值和类型才是一个整体。

4.for循环

上面的代码我们看了下,存在大量的变量的初始化和使用,那么我们能有什么办法,将对应的代码给简化掉,我们可以用我们for循环,将上面的代码给简化掉,具体的代码的如下:

#include 

main() {

    int fahr;

    for (fahr = 0; fahr <= 300; fahr = fahr + 20) {
        printf("%3d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32));
    }
}


运行的结果如下:

听说C语言很难?怎么不来看看我这篇(一)_第2张图片

我们可以看到循环主要有三部分,主要是:for (fahr = 0; fahr <= 300; fahr = fahr + 20) {}其中fahr = 0是for循环的初始化的部分,仅在进入循环前执行一次,第二部分fahr <= 300是控制循环的测试或条件的部分。循环控制将对该条件求值,如果结果为真,则执行循环体。此后执行第三部分fahr = fahr + 20,将循环变量fahr增加一个步长,并再次对条件求值。如果计算得到的条件为假,循环将终止。

这个时候可能有人会问,我们什么时候用while,什么时候用for呢?我的答案就是你开心就好。当然也有个标准:for比较适合初始化和增加步长的单条语句并且逻辑相关的情形,因为它将循环控制语句集中放在一起。

5.符号常量

我们再来看看上面的程序,我们可以看到程序中有300,20,0,这些数,这些数在我们语言中称为魔法数。这不是一个好的东西,万一有一天我们要改温度的上限,从300改成400,那么我就需要修改整个程序中的300,那么很容易改忘,改漏。那么有没有什么好的方式对这些魔法数进行集中管理呢?很明确的告诉你,有,就是符号常量。我们再改一个版本,具体的如下:

#include 
#define LOWER 0
#define UPPER 300
#define STEP 20

main() {

    int fahr;

    for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP) {
        printf("%3d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32));
    }
}

运行的结果如下:

#define指令可以把符号名(或称为符号常量)定义为一个特定的字符串:#define 名字 替换的文本替换的文本可以是任何字符序列,而不仅限于数字。

6.字符输入/输出

6.1文件复制

前面我们学了输出,今天我们来学下输入,老规矩,先看代码,具体的代码如下:

#include 

main() {
    int c;
    while ((c = getchar()) != EOF) {
        putchar(c);
    }
}

其中有getchar()函数是从键盘输入的文本流中读入下一个输入字符,并将其作为结果值返回。

putchar()读取对应的变量中的字符,打印在屏幕上。

EOF文件的结束符。

所以上面的程序可以理解为:我们输入什么,就会打印出来什么,如果输入结束符,就会结束对应的循环,也就是ctrl+D。

6.2字符计数

有了前面的基础的知识,我们可以写一个程序用来计算我们输入的字符数,具体的代码如下:

#include 

main() {
    double nc;
    for (nc = 0; getchar() != EOF; ++nc);
    printf("%.0f\n", nc);
}

运行的结果如下:

听说C语言很难?怎么不来看看我这篇(一)_第3张图片

我们输入了十个字符,其中还有一个回车,所以这儿打印出来的是11个,没有问题。

6.3行计数

有了前面的基础知识,我们可以写一个程序用来计算我们输入的字符的行数,具体的代码如下:

#include 

main() {
    double c, n1;
    n1 = 0.0;
    while ((c = getchar()) != EOF) {
        if (c == '\n')
            ++n1;
    }
    printf("%0.f \n", n1);
}

这儿我们学到了一个新的东西,就是if判断,括号中间的是条件,如果条件满足的情况下,就会执行++n1操作,如果条件不满足的情况下,就不会执行++n1操作。其中++n1 = n1=n1 + 1但是这个++ 或者–有前置的操作,还有就是后置的操作,这个等我写变量的时候,再和大家讲清楚。运行的结果如下:

听说C语言很难?怎么不来看看我这篇(一)_第4张图片

可以看我们的程序的输出的结果是对的。

6.4单词计数

经过前面的学习,我们写出如下的代码:

#include 
#define IN 1
#define OUT 0

main(){
    int c, n1, nw, nc, state;
    
    state = OUT;
    n1 = nw = nc = 0;
    while ((c = getchar()) != EOF) {
        ++nc;
        if(c == '\n')
            n1 ++;
        if(c == ' ' || c == '\n' || c == '\t')
            state = OUT;
        else if(state == OUT){
            state = IN;
            ++nw;
        }
    }
    printf("%d %d %d \n", n1, nw, nc);
}

看看上面的代码,我们又学到新的东西了,一个是 ||这个符号,是或的意思,就是所有的条件有一个成立的话,就算整个表达式是结果是真的,还有一个就是&&这个是且的意思,如果所有的条件都满足的话,才会成立,其他的情况的都是不成立的。然后就是一种新的格式:

if(表达式)
语句1;
else
语句2

上面的格式表达式成立的时候,就会执行语句1,如果表达式不成立的话,就会执行语句2。

还有一种格式,具体的如下:

if(表达式1)
语句1
else if(表达式2)
语句2
else
语句3

上面的格式表达式1成立的话,就会执行语句1,表达式2成立的话,就会执行语句2,如果都不成立的话,就会执行语句3

7.数组

我们先看一个程序,具体的代码如下:

#include 

main() {
    int c, i, nwhite, nother;
    int ndigit[10];

    nwhite = nother = 0;
    for (i = 0; i < 10; ++i) {
        ndigit[i] = 0;
    }

    while ((c = getchar()) != EOF){
        if(c >= '0' && c<='9')
            ++ndigit[c-'0'];
        else if (c==' ' || c=='\n' ||c=='\t')
            ++nwhite;
        else
            ++nother;
    }
    printf("digits=");
    for (i = 0; i < 10; ++i)
        printf(" %d", ndigit[i]);
    printf(", white space = %d, other = %d\n",nwhite, nother);
}

上面的程序,我们接触到了一个新的知识,就是数组,数组我们需要知道就是下标,数组的长度,数组的下标不能超过数组的长度。如果超过了,就会下标越界。数组我们可以理解为一连串的内存的空间,然后存同一类型的值,最后通过下标访问对应位置的值。

上面的程序就是进行计数的,我们输入的值,进行对应的计数,数组的0-9的位置分别表示对应的数字0-9的个数,运行的结果如下:

听说C语言很难?怎么不来看看我这篇(一)_第5张图片

8.函数

先看下面的程序:

#include 
// 函数定义
int power(int m, int n);

main() {
    int i;
    for (i = 0; i < 10; ++i)
        printf("%d %d %d\n", i, power(2, i), power(-3, i));
    return 0;
}

// 函数实现
int power(int base, int n) {
    int i, p;
    p = 1;
    for (i = 0; i <= n; ++i)
        p = p * base;
    return p;
}

从上面的程序我们可以得到函数的一般的类型,具体的如下:

返回值类型 函数名(0个或多个参数声明)
{
声明部分
语句序列
}

注意:

  • 函数不一定需要返回值,参数也不一定需要有(函数定义中圆括号内列表中出现的变量称为形式参数,而把函数调用中与形式参数对应的值称为实际参数,函数原型与函数声明中参数名不要求相同)
  • 返回值最多只能有一个
  • 返回值通过return 返回给调用方

上面的程序的运行结果如下:

听说C语言很难?怎么不来看看我这篇(一)_第6张图片

9.参数-传值调用

C语言中所有的参数都是通过值传递的,也就是传递的参数是值,也就是修改参数的值,不会影响原来的参数的值,但是很多人需要修改参数的值,也要影响原始参数的值。这个时候就需要传入的是指针。

计算机中所有的值都以二进制存到某个地址上,而指针就是用来存地址,既然地址给你了,就能找到这个变量,那么你的修改就会影响这个个变量的值。

10.字符数组

我们先看下面的程序,具体的如下:

#include 

#define MAXLINE 1000

int getLine(char line[], int maxline);

void copy(char to[], char from[]);

main() {
    int len;
    int max;
    char line[MAXLINE];
    char longest[MAXLINE];

    max = 0;
    while ((len = getLine(line, MAXLINE)) > 0)
        if (len > max) {
            max = len;
            copy(longest, line);
        }
    if (max > 0)
        printf("%s", longest);
    return 0;

}

int getLine(char s[], int lim) {
    int c, i;
    for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
        s[i] = c;
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

void copy(char to[], char from[]) {
    int i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

上面的程序,就是复制你输入的字符串,然后打印出来,这儿我们只要主要到一点的内容,就是在C语言中字符串一定要以\0结尾,不然打印的内容有问题的。

11.外部变量和作用域

我们再来看一个程序,具体的如下:

#include 

#define MAXLINE 1000

int max;
char line[MAXLINE];
char longest[MAXLINE];

int getLine(void);
void copy(void);

main() {
    int len;

    max = 0;
    while ((len = getLine()) > 0)
        if (len > max) {
            max = len;
            copy();
        }
    if (max > 0)
        printf("%s", longest);
    return 0;

}

int getLine(void) {
    int c, i;
    for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
        line[i] = c;
    if (c == '\n') {
        line[i] = c;
        ++i;
    }
    line[i] = '\0';
    return i;
}

void copy(void ){
    int i = 0;
    while ((longest[i] = line[i]) != '\0')
        ++i;
}

上面的程序是原来的程序的改编版,主要是为了介绍局部变量和外部变量,局部变量的生命周期是随着函数的消亡而消亡,而外部变量是随着程序的消亡而消亡的,所以局部变量是一个个体,它的修改不会影响别的,但是外部变量,只要函数修改的这个外部变量,其他函数也是可见的。

12.写在最后

本篇博客主要简单的介绍的C语言的全览,后面我打算按照书上的内容一点点的介绍对应的内容,最后祝大家开工大吉。

你可能感兴趣的:(C语言,c语言,开发语言)