C语言笔记(二)

十二、switch语句和分支嵌套

12.1、处理多分支结构,可以考虑使用语法更简便的 switch 语句

…… // 其它语句
switch (表达式)
{
        case 常量表达式 1: 语句或程序块;
        case 常量表达式 2: 语句或程序块;
        ……
        case 常量表达式 n:语句或程序块;
        default: 语句或程序块;
}
…… // 其它语句
  • 这里每个 case 后边的常量是匹配 switch 后边表达式的值
  • case 后边必须跟一个常量值,而不能是一个范围
  • 如果所有的 case 均没有匹配的,那么执行 default 的内容
  • default 是可选的,如果没有 default,并且所有的 case 均不匹配,那么 switch 语句不执行任何动作

12.2、使用 break 语句跳出

switch 语句中的 case 和 default 事实上都是“标签”,用来标志一个位置而已。当 switch 跳到某个位置之后,就会一直往下执行,所以我们这里还需要配合一个 break 语句,让代码在适当的位置跳出 switch。

12.3、分支结构的嵌套

如果在一个 if 语句中包含另一个 if 语句,我们就称之为 if 语句的嵌套,也叫分支结构的嵌套。
在这里插入图片描述

12.4、悬挂 else

……
if (x == 0)
    if (y == 0)
        error();
else
    z = x + y;
……

C 语言中有这样的规则,else 始终与同一对括号内最近的未匹配的 if 结合,上面这段程序实际上被执行的逻辑来调整代码缩进,大致是这个样子:

……
if (x == 0)
    if (y == 0)
        error();
    else
        z = x + y;
……

如果 x 不等于 0,程序将不会做任何处理,与上一个代码基本不一样,所以应该这样写:

……
if (x == 0)
{
    if (y == 0)
    {
        error();
    }
}
else
{
    z = x + y;
}
……

现在,else 与第一个 if 结合,即使它离第二个 if 更近也是如此,因为此时第二个 if 已经被括号“封装”起来了。

12.5、等于号带来的问题

在 C 语言中使用等号(=)作为赋值运算,使用连续两个等号(==)作为比较运算。一般而言,赋值运算相对于比较运算出现得更频繁,因此字符较少的 = 就被赋予了更常用的含义——赋值操作。此外,在 C 语言中赋值符号被作为一种操作符对待,因而重复进行赋值操作(如 a = b = c)可以很容易地书写,并且赋值操作还可以被嵌入到更大的表达式中。
但是,这种使用上的便利性可能导致一个潜在的问题:当程序员本意是在作比较运算时,却可能无意中误写成赋值运算。

比如下例,该语句本意似乎是要检查 x 是否等于 y :

if (x = y)
    break;

而实际上是将 y 的值赋给了 x ,然后检查该值是否为零。

再来看看下面的例子:

if (c = ' ' || c == '\t' || c == '\n')
    space = space + 1;

该程序的本意是计算空白字符(空格符、制表符和换行符)的数量,但是由于程序员在比较字符 ’ ’ 和变量 c 时,误将比较运算符 == 写成了赋值运算符 = 。因为赋值运算符 = 的优先级要低于逻辑运算符 || ,因此实际上是将以下表达式的值赋给了 c :

' ' || c == '\t' || c == '\n'

因为空格(’ ')不等于零(空格的 ASCII 码值为32),那么无论变量 c 此前为何值,上述表达式的值都是 1。

十三、while语句和do…while语句

13.1、while 语句

while (表达式)
        循环体

只要表达式的值为真,那么就会不断执行循环体里边的语句或程序块。

13.2、do…while 语句

do
    循环体
while (表达式);

while 是先判断表达式,如果表达式结果为真,才执行循环体里边的内容;
do…while 则相反,先执行循环体的内容再判断表达式是否为真。
注意:do…while 语句在 while 后边一定要用分号(;)表示语句结束。

13.3、getchar

getchar 函数从标准输入流(stdin)中获取下一个字符。

相当于调用 getc(stdin) 函数。

#include <stdio.h>
...
int getchar()(void);

如果函数调用成功,返回获取的字符(用整型表示其 ASCII 码)。

返回值如果是 EOF,表示该函数调用失败:

  • 如果标准输入流位于处于结束的位置,该函数返回 EOF,并设置标准输入流的结束标志符。
  • 如果出现其他错误,该函数同样返回 EOF,并设置错误标志符代替。
#include <stdio.h>

int main()
{
        int count = 0;

        printf("请随意一个英文句子:");

        while (getchar() != '\n')
        {
                count = count + 1;
        }

        printf("你总共输入了%d个字符!\n", count);

        return 0;
}

十四、for语句和循环嵌套

14.1、for 语句

for (表达式1; 表达式2; 表达式3)
        循环体

三个表达式用分号隔开,其中:

  • 表达式1是循环初始化表达式
  • 表达式2是循环条件表达式
  • 表达式3是循环调整表达式

14.2、灵活的 for 语句

for 语句的表达式1,表达式2和表达式3都可以按照需要进行省略(但分号不能省):

  • for ( ; 表达式2; 表达式3)
  • for (表达式1; 表达式2; )
  • for (表达式1; ; )
  • for ( ; ; )
    ……
    但是建议不要这么做,因为程序的可读性会因此而降低!

14.3、C99 允许在 for 语句的表达式1中定义变量。

#include <stdio.h>

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

        return 0;
}

**注意:**在编译时需要加上–std=c99,否则可能会出错。

[fishc@localhost s1e14]$ gcc –std=c99 test.c && ./a.out
0
1
2
3
4

增加这个新特性的原因主要是考虑到循环通常需要一个计数器,而这个计数器出了循环就没什么用了。所以在表达式1的位置定义的变量,活动范围仅限于循环中,出了循环,它就无效了。

14.4、循环嵌套

循环结构跟分支结构一样,都可以实现嵌套。

对于嵌套的循环结构,执行顺序是从内到外:先执行内层循环,再执行外层循环。

十五、break语句和continue语句

15.1、break 语句

在循环体中,如果我们想要让程序在中途跳出循环,那么我们同样可以使用 break 语句来实现。

执行 break 语句,直接跳出循环体。

有一点需要注意的是,对于嵌套循环来说,break 语句只负责跳出所在的那一层循环,要跳出外层循环则需要再布置一个 break 语句才行。

15.2、continue 语句

当满足某个条件的时候,跳过本轮循环的内容,直接开始下一轮循环。这时候我们应该使用 continue 语句。

当执行到 continue 语句的时候,循环体的剩余部分将被忽略,直接进入下一轮循环。

对于嵌套循环来说,continue 语句跟 break 语句是一样的:它们都只能作用于一层循环体。

15.3、for 语句和 while 语句执行过程的区别

for 语句和 while 语句执行过程是有区别的,它们的区别在于出现 continue 语句时。

在 for 语句中,continue 语句跳过循环的剩余部分,直接回到调整部分。

在 while 语句中,调整部分是循环体的一部分,因此 continue 语句会把它也跳过。

十六、运算符及拓展

16.1、赋值运算符

它的设计简便与否直接影响到了 C 语言的开发效率。语法很简单,就是将右边的值放到左边的变量里边,因为它的执行方向是自右向左。

int a;
a = 5;

赋值运算符的左边必须是一个 lvalue,变量名就是 lvalue,但常数就不是了,所以你把 5 写在赋值号的左边就会出错:

5 = a;

16.2、复合的赋值运算符

a = a + 1;

可以写成:

a += 1;
a -= 2;
a += 3;
a /= 4;
a %= 5;

16.3、自增自减运算符

当我们需要对一个变量加一或减一并赋值给自己的时候,我们可以写成 i++、i— 或 ++i、–i 的形式。
它们也被称为增量减量运算符,或 ++、-- 运算符。
它们的区别是:

  • i++ 是先使用变量 i 中保存的值,再对自身进行 ++ 运算;
  • ++i 是先对自身进行 ++ 运算,再使用变量 i 的值(这时候变量i的值已经加 1 了)。

另外,自增、自减运算符只能作用于变量,而不能作用于常量或表示。

16.4、逗号运算符

逗号表达式的语法是:表达式1,表达式2,表达式3,… ,表达式n

  • 逗号表达式的运算过程为:从左往右逐个计算表达式;
  • 逗号表达式作为一个整体,它的值为最后一个表达式(也即表达式n)的值。

逗号运算符在C语言的所有运算符中,是最没有地位的那个。

因为连赋值运算符的优先级都比逗号运算符要高,所以……

a = 3, 5

相当于

a = 3;
5;

注意:在 C 语言中看到的逗号,不一定就都是逗号运算符,因为在有些地方,逗号仅仅是被用作分隔符而已。

int a, b, c; 
scanf("%d%d%d", &a, &b, &c); 

16.5、条件运算符

有一个操作数的运算符称为单目运算符,有两个两个操作数称为双目运算符,然而 C 语言还有唯一的一个三目运算符,它的作用是提供一种简写的方式来表示 if-else 语句。
语法:exp1 ? exp2 : exp3;
exp1 是条件表达式,如果结果为真,返回 exp2,如果为假,返回 exp3。

if (a > b)
{
    max = a;
}
else
{
    max = b;
}

可以直接写成:

max = a > b ? a : b;

16.6、goto 语句

goto 语句的作用就是直接跳转到指定标签的位置。
语法:goto 标签;
其中标签需要被定位于某个语句的前边,比如:

#include <stdio.h>

int main()
{
        int i = 5;

        while (i++)
        {
                if (i > 10)
                {
                        goto Label;
                }
        }

Label:  printf("Here, i = %d\n", i);

        return 0;
}

开发中要尽量避免使用 goto 语句。其实就连 C 语言的作者也觉得 goto 语句非常容易被滥用,并且建议一定要谨慎使用,甚至根本不用它。
但在一种情况下使用 goto 语句是情有可原的,那就是当面临要跳出多层循环的时候,使用 goto 语句要比多个 break 语句好使。

16.7、注释

C 语言的注释有两种方式,一种是将注释写在两个连续斜杠的后边:

// 这是注释,编译器不会理会

需要写多行注释,可以使用 /* 注释的内容 */ 来实现:

/* 这是一个跨越多行的注释
   这是注释,编译器不会理会
   这是注释,编译器不会理会
   这是注释,编译器不会理会
   这是一个跨越多行的注释 */

十七、数组

17.1、定义数组

类型 数组名[常量表达式]

int a[6]; // 定义一个整型数组,总共存放 6 个元素
char b[24]; // 定义一个字符型数组,总共存放 24 个元素
double c[3]; // 定义一个双精度浮点型数组,总共存放 3 个元素

在定义数组时,需要在数组名后边紧跟着一对方括号,其中用常量表达式来指定数组中元素的个数。因为只有告诉编译器元素的个数,编译器才能申请对应大小的内存给它存放。

17.2、访问数组

数组名[下标]

a[0]; // 访问 a 数组中的第 1 个元素
b[1]; // 访问 b 数组中的第 2 个元素
c[5]; // 访问 c 数组中的第 6 个元素

// 注意:

int a[5]; // 创建一个具有 5 个元素的数组
a[0]; // 访问第 1 个元素的下标是 0,不是 1
a[5]; // 报错,因为第 5 个元素的下标是 a[4]

17.3、数组的初始化

在定义数组的同时对其各个元素进行赋值,称之为数组的初始化。

17.3.1、将数组中所有元素统一初始化为某个值

int a[10] = {0}; // 将数组中所有元素初始化为 0

17.3.2、如果是赋予不同的值,那么用逗号分隔开

int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};

17.3.3、可以只给一部分元素赋值,未被赋值的元素自动初始化为 0

int a[10] = {1, 2, 3, 4, 5, 6}; // 表示为前边 6 个元素赋值,后边 4 个元素系统自动初始化为 0

17.3.4、可以只给出各个元素的值,而不指定数组的长度(因为编译器会根据值的个数自动判断数组的长度)

int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};

17.3.5、可以只对数组中的某些指定元素进行初始化赋值,而未被赋值的元素自动初始化为 0

int a[10] = {[3] = 3, [5] = 5, [8] = [8]}; // 编译的时候记得加上 –std=c99 选项

17.4、变长数组

变长指的是数组的长度是在运行时才能决定,但一旦决定在数组的生命周期内就不会再变。

#include <stdio.h>

int main()
{
        int n, i;

        printf("请输入字符的个数:");
        scanf("%d", &n);

        char a[n+1]; 

        printf("请开始输入字符:");
        getchar(); // 将标准输入流中剩下的 '\n' 扔掉
        for (i = 0; i < n; i++)
        {
                scanf("%c", &a[i]);
        }
        a[n] = '\0';
        printf("你输入的字符串是:%s\n", a);

        return 0;
}

十八、字符串处理函数

18.1、字符数组

C 语言是没有字符串类型的,存放和表示字符串用两种方式——字符串常量以及字符类型的数组
字符串常量:“FishC”,“小甲鱼”,“鱼C工作室”

字符数组:

可以先定义指定长度的字符数组,然后再给每个元素单独赋值:

int main
{
    char str[10];

    str[0] = 'F';
    str[1] = 'i';
    str[2] = 's';
    str[3] = 'h';
    str[4] = 'C';
    str[5] = '\0';

    ……
}

还可以直接在定义的时候对字符数组进行初始化:

int main
{
    // 初始化字符数组的每个元素
    char str1[10] = {'F', 'i', 's', 'h', 'C', '\0'};
    
    // 可以不写元素的个数,因为编译器会自动计算
    char str3[] = {'F', 'i', 's', 'h', 'C', '\0'};
    
    // 使用字符串常量初始化字符数组
    char str4[] = {"FishC"};
    
    // 使用字符串常量初始化,可以省略大括号
    char str5[] = "FishC";
}

18.2、字符串处理函数

如果能用得上官方提供的现成的函数,尽量不要自己去写,这样除了能够大幅度提供工作效率外,也会使你的程序更加稳定和快速,C语言标准函数库分类

获取字符串的长度:strlen 函数

拷贝字符串:strcpy 函数和 strncpy 函数

连接字符串:strcat 函数和 strncat 函数

比较字符串:strcmp 函数和 strncmp 函数

十九、二维数组

19.1、二维数组的定义

定义二维数组的方法跟一位数组相似,使用方括号指定每个维度的元素个数:

类型 数组名[常量表达式][常量表达式]

int a[6][6]; // 6 * 6,6 行 6 列
char b[4][5]; // 4 * 5,4 行 5 列
double c[6][3]; // 6 * 3,6 行 3 列

无论是二维数组还是更多维的数组,在内存中仍然是以线性的方式存储的,二维数组事实上就是在一维数组的基础上,每个元素存放一个数组。同样道理,三维数组,四维数组都是以同样的方式实现。

19.2、二维数组的访问

数组名[下标]

a[0][0]; // 访问 a 数组中第 1 行第 1 列的元素
b[1][3]; // 访问 b 数组中第 2 行第 4 列的元素
c[3][3]; // 访问 c 数组中第 4 行第 4 列的元素

跟访问一维数组相似,同样是使用下标访问数组中的元素。同样需要注意下标的取值范围,以防止数组的越界访问。

比如 int a[3][4],其“行下标”的取值范围是 0~2,“列下标”的取值范围是 0~3,超出任何一个下标的访问都会造成越界。

19.3、二维数组的初始化

  • 由于二维数组在内存中是线性存放的,因此可以将所有的数据写在一个花括号内:
int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

这样就是先将第 1 行 4 个元素初始化,再初始化第2行的元素。

  • 为了更直观地表示元素的分布,可以用大括号将每一行的元素括起来:
int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

这样写的话表示会更加清晰:

int a[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
};
  • 二维数组也可以仅对部分元素赋初值:
int a[3][4] = {{1}, {5}, {9}};

这样写是只对各行的第 1 列元素赋初值,其余元素初始化为 0。

  • 如果希望整个二维数组初始化为 0,那么直接在大括号里写一个 0 即可:
int a[3][4] = {0};
  • 指定初始化的元素。这样就可以只对数组中的某些指定元素进行初始化赋值,而未被赋值的元素自动初始化为 0:
int a[3][4] = {[0][0] = 1, [1][1] = 2, [2][2] = 3};
  • 二维数组的初始化也能偷懒,让编译器根据元素的数量计算数组的长度。但只有第 1 维的元素个数可以不写,其他维度必须写上:
int a[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值