C语言基础知识(三)-程序设计结构、数组、字符串处理函数

举报
帅次 发表于 2021/12/22 23:48:31 2021/12/22
【摘要】         本文是C语言的基础知识,主要讲解三种程序设计结构、数组、字符串和字符数组、数组元素查询以及字符串处理函数。 程序结构设计 包括C语言在内的几乎任何编程语言都支持以下三种程序设计结构,它们分别是: 顺序结构程序设计 选择结构程序设计 ...

        本文是C语言的基础知识,主要讲解三种程序设计结构、数组、字符串和字符数组、数组元素查询以及字符串处理函数。

程序结构设计

包括C语言在内的几乎任何编程语言都支持以下三种程序设计结构,它们分别是:

  • 顺序结构程序设计

  • 选择结构程序设计

  • 循环结构程序设计

程序设计是什么

        程序设计就是用各种程序设计语言(C、C++、Java、VB、Pascal 等)将算法流程转化为可以被计算机执行的代码的过程。

顺序结构

        顺序结构的程序设计就是把解决问题的过程一步一步由上至下,按顺序编制成可执行代码。 顺序结构程序设计一般由三部分组成:

  • 输入部分:把已知的值输入电脑并存储在变量中。

  • 处理部分:按解决问题的次序进行计算处理。

  • 输出部分:把计算处理结果返回给用户。

        实例

        随机产生一个四位数整数。输出整数和其各位数字。

        好了咱先把代码搞出来


  
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. int main( )
  5. {
  6.     int number,a,b,c,d,s;
  7.     srand(time(NULL));
  8.     number = 1000+rand()%9000;
  9.     a = number % 10;
  10.     b = number / 10 % 10;
  11.     c = number / 100 % 10;
  12.     d = number / 1000;
  13.     s = a + b + c + d;
  14.     printf("随机生成四位整数:%d\n",number);
  15.     printf("各位数字:%d , %d , %d , %d ",d,c,b,a);
  16.     return 0;
  17. }

        运行结果


  
  1. 随机生成四位整数:8534
  2. 各位数字:8 , 5 , 3 , 4 

1.首先生成一个随机的四位数整数。

        rand()函数产生的是一个伪随机数,重复调用该函数所产生的随机数字是相同的。要想每次执行产生不同的随机数,就需要用srand()函数进行随机初始化。

        随机初始化函数srand()可以设置随机数生成器的种子,不同的种子将产生不同的随机数。在程序运行过程中时间是一直变化的,所以我们可以借助 time.h 库中的 time(NULL) 函数返回计算机当前的时间数,把它作为随机数生成器的种子,从而在每次执行 rand( ) 函数时产生一个不同的随机数。

        将当前时间设置为随机数生成器种子的代码如下:

srand(time(NULL));

 

2.拆分这个四位整数,获得其各位上的数字。

        拆分一个数可以利用%和/运算符实现。假设 a、b、c、d 分别表示四位整数 number 的个、十、百、千位上的数,则它们的值可以分别表示为:


  
  1. a = number % 10;               //个位数
  2. b = bumber / 10 % 10           //十位数
  3. c = number / 100 % 10          //百位数
  4. d = number / 1000              //千位数

        好了齐活。

选择结构

        选择结构也称分支结构,是一种根据判断条件的成立与否来确定程序执行方向的一种程序设计结构。if 和 else 是两个新的关键字,if 意为如果,else 意为否则,用来对条件进行判断,并根据判断结果执行不同的语句。

        C 语言把任何非零非空的值假定为 true,把null 假定为 false

最简单的if语句


  
  1. if(判断条件)
  2.  {
  3.   语句块
  4.  }

        前置条件:表达式必须要有,表达式外面的()也是必须的。

        功能:如果表达式为真就执行语句块,否则就向下执行。

if实例


  
  1. #include <stdio.h>
  2. int main( )
  3. {
  4.     if (9>5)
  5.  {
  6.   printf("公众号:帅次");
  7.  }
  8. return 0;

        运行结果

公众号:帅次
 

        9>5,表达式为真,所以执行语句块。

语句块

        所谓语句块(Statement Block),就是由{ }包围的一个或多个语句的集合。如果语句块中只有一个语句,也可以省略{}

if else语句


  
  1.   if (判断条件)
  2.  {
  3.      语句块1
  4.  } else {
  5.       语句块2
  6.  }

        功能:如果表达式为真就执行语句块1,否则执行语句块2 。

if else实例


  
  1. #include <stdio.h>
  2. int main( )
  3. {
  4.  int number=63;
  5.  if(number>=60
  6.   printf("及格啦!\n");
  7.  else
  8.   printf("不及格!\n");
  9. return 0;

        运行结果

及格啦!

 

多个if else语句


  
  1. #include <stdio.h>
  2. int main( )
  3. {
  4.  if (判断条件1){
  5.   语句块1
  6.  } else  if (判断条件2){
  7.   语句块2
  8.  }else  if (判断条件3){
  9.   语句块3
  10.  }else  if (判断条件m){
  11.   语句块4
  12.  }else{
  13.   语句块5
  14.  }

if else语句的嵌套


  
  1. #include <stdio.h>
  2. int main( )
  3. {
  4.  if (判断条件1){
  5.   if (判断条件2){
  6.    语句块1
  7.   }else{
  8.    语句块2
  9.   }
  10.  } else{
  11.   if (判断条件3){
  12.    语句块3
  13.   }else{
  14.    语句块4
  15.   }
  16.  }

switch case语句

        在C语言中,应用条件语句 if-else 可以很方便地使程序实现两个分支,但是如果出现多个分支的情况,虽然可以使用 if-else 语句的嵌套,但是程序会显得比较复杂,不易阅读。

        switch 是另外一种选择结构的语句,用来代替简单的、拥有多个分枝的 if else 语句。 基本格式如下:


  
  1.  switch(判断条件)  //表达式的值只能在下面的 case 值表中出现一次
  2.  {
  3.      case 值 1
  4.    语句块1
  5.    break;     //break 语句的功能是跳出 switch 语句,执行其后面的语句
  6.      case 值 2
  7.    语句块2
  8.    break;
  9.   case 值 3
  10.    语句块3 //如果没有 break 语句,则会自动继续执行后续 case 的语句序列
  11.   case 值 n:
  12.    语句块 n;
  13.    break;
  14.    default:
  15.    语句块n+1;  //default 部分是可选项
  16.  }

switch 语句的执行流程

        实例


  
  1. #include <stdio.h>
  2. int main( )
  3. {
  4.  int number = 5;
  5.  switch(number){
  6.         case 1printf("Monday\n");
  7.         case 2printf("Tuesday\n");
  8.         case 3printf("Wednesday\n");
  9.         case 4printf("Thursday\n");
  10.         case 5printf("Friday\n");
  11.         case 6printf("Saturday\n");
  12.         case 7printf("Sunday\n");
  13.         default:printf("error\n");
  14. }
  15. return 0;

        运行结果


  
  1. Friday
  2. Saturday
  3. Sunday
  4. error

循环结构

C语言的循环控制结构一般有三种基本形式:

  • while 循环语句:先判断,条件判断为真则执行循环体;

  • do-while 循环语句:先执行一次循环体,再判断条件是否为真;

  • for 循环语句:多次执行一个语句序列,简化管理循环变量的代码。 循环结构中有一个循环体,根据判断的结果来决定执行循环体的次数。

        while 语句和 do-while 语句可以相互转换。for 循环的循环处理次数比 while 和 do-while 更加简洁,for 循环的执行顺序是先初始化循环变量,再判断条件是否为真,条件为真则执行循环体;执行完毕则改变循环变量,再次对条件进行判断,循环执行直至条件不成立。

循环控制语句

循环控制语句改变你代码的执行顺序。通过它你可以实现代码的跳转。

  • break 语句:终止循环或 switch 语句,程序流将继续执行紧接着循环或 switch 的下一条语句。

  • continue 语句:告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。

  • goto 语句:将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。

注意事项:

  • 用 break 可以提前终止 for、while 和 do-while 循环。

  • 用 continue 可以强迫循环进入新的循环周期。

  • break 和 continue 前通常有 if 语句进行某种关系测试。

while 循环

        while 语句适用于当条件成立时重复操作的循环控制结构,因而常被称作当型循环。其一般格式如下:


  
  1. while (判断表达式)
  2. {
  3.     循环体;           //一条或多条 C 语句
  4. }

        它与 if 语句中的 condition 一样,其外面的括号是必需的。循环体是 while 语句的主体部分,是需要重复操作的一条或多条 C 语句,它包含多条语句时,其外侧必须加上花括号{}。

        判断表达式为真执行循环体,否则退出 while 语句,继续执行后续语句。

        实例


  
  1. #include <stdio.h>
  2. int main( )
  3. {
  4.  int number = 0;
  5.  while(number<5){//number必须小于5
  6.   printf("number:%d\n",number);
  7.   number++;//number=number+1
  8. }
  9. return 0;

        运行结果


  
  1. number:0
  2. number:1
  3. number:2
  4. number:3
  5. number:4

当number=5时,结束循环。

do while 循环

        do-while 语句适用于重复操作直到条件不成立为止的循环控制结构,因而常被称作直到型循环。其一般格式如下:


  
  1. do
  2. {
  3.     循环体;    //一条或多条 C 语句
  4. }
  5. while (判断表达式);

        特别注意: 和 while 语句不一样的是,要在 do-while 语句 判断表达式 外的括号后面加上分号。

        实例


  
  1. #include <stdio.h>
  2. int main( )
  3. {
  4.  int number = 0;
  5.  do{
  6.   printf("number:%d\n",number);
  7.   number++;//number=number+1
  8.  }
  9.  while(number<5);//number必须小于5
  10. return 0

        运行结果


  
  1. number:0
  2. number:1
  3. number:2
  4. number:3
  5. number:4

while语句和do-while语句的互换

do-while 语句和 while 语句都擅长于解决循环次数未知的重复操作,但两者在实际应用中还是有区别的:

  • do-while 语句是先执行循环体语句,后判断循环条件是否成立;while 语句是先判断循环条件是否成立,后执行循环体语句。

  • do-while 语句中,无论循环条件是否成立,总要执行一次循环体语句;while 语句中,如果循环条件不成立,则不执行循环体语句。

while和do-while小结

左图为while执行过程,右图为do-while执行过程

  • 需要重复执行一段代码时,使用 while 或 do-while 循环语句。

  • 不要在 while 语句的 condition 外的括号后面加上分号。

  • 要在 do-while 语句的 condition 外的括号后面加上分号。

  • 不要在 while 或 do-while 语句的循环体外的花括号后面加上分号。

  • 循环体内只有一条语句时,其外面的花括号可以不写。

  • 确保 while 或 do-while 语句的循环体内有语句修改了 condition 中的某个变量值。否则循环将永远重复下去,成为死循环

for循环

        如果已知重复操作的次数,可以使用 for 循环语句,其一般格式如下


  
  1. for(表达式1;表达式2;表达式3)
  2. {
  3.     循环体         //一条或多条 C 语句
  4. }                  //若循环体内只有一条语句,则花括号可以不写

        for 循环中的表达式1(初始化条件)表达式2(循环条件)表达式3(自增或自减) 都是可选项,都可以省略(但分号 ;必须保留 )。

        实例


  
  1. #include <stdio.h>
  2. int main( )
  3. {
  4.  int number;
  5.  for(number=0;number<5;number++){
  6.   printf("number:%d\n",number);
  7.  } 
  8. return 0;

        运行结果


  
  1. number:0
  2. number:1
  3. number:2
  4. number:3
  5. number:4

        结果分析

  • 1.可以将number=0移到了 for 循环的外面,省略表达式1(初始化条件),如下


  
  1. int number=0;
  2. for(;number<5;number++){
  3.  printf("number:%d\n",number);
  • 2.省略了表达式2(循环条件),如果不做其它处理就会成为死循环。如下:


  
  1. for(number=0;;number++){
  2.  printf("number:%d\n",number);

        死循环,就是循环条件永远成立,循环会一直进行下去,永不结束。死循环对程序的危害很大,一定要避免

  • 3.省略了表达式3(自增或自减),就不会修改“表达式2(循环条件)”中的变量,这时可在循环体中加入修改变量的语句。如下:


  
  1. for(number=0;number<5;){
  2.  printf("number:%d\n",number);
  3.  number++;
  4. }
  • 4.3个表达式可以同时省略。如下:


  
  1. for(;;)语句 

相当于

while(1)语句

 

for循环语句的嵌套

        如果把一个 for 循环语句放在另一个 for 循环语句的循环体中,就构成了 for 循环的嵌套。其一般格式如下:


  
  1. for(外层循环变量 i 初始化;外层循环条件;外层循环变量 i 增量)
  2. {
  3.     ……
  4.     for(内层循环变量 j 初始化;内层循环条件;内层循环变量 j 增量)  
  5.     {
  6.         内层循环体
  7.     }
  8.     ……
  9. }

        实例

        计算 1+2+3+…+100。


  
  1. #include <stdio.h>
  2. int main(){
  3.  int i, sum=0;
  4.     i = 1;  
  5.  //while循环
  6.     while(i<=100){
  7.         sum+=i;
  8.         i++; 
  9.     }
  10.     printf("总和(while):%d\n",sum);
  11.  //do-while循环
  12.     sum=0;
  13.     i = 1
  14.  do{
  15.   sum+=i;
  16.         i++; 
  17.  }while(i<=100);
  18.     printf("总和(do-while):%d\n",sum);
  19.  //for循环
  20.  sum=0;
  21.  for(i=1;i<=100;i++)
  22.   sum+=i;
  23.  printf("总和(for):%d\n",sum);
  24.     return 0;
  25. }

        运行结果


  
  1. 总和(while):5050
  2. 总和(do-while):5050
  3. 总和(for):5050

数组

        在程序设计中,为了处理方便,把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组

        在C语言中,数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。

一维数组

        数组可以看作是一行连续的数据,只有一个下标,称为一维数组

一维数组的定义

        我们知道,要想把数据放入内存,必须先要分配内存空间。放入4个整数,就得分配4个int类型的内存空间:

int a[4];

 

        这样,就在内存中分配了4个int类型的内存空间,共 4×4=16 个字节,并为它们起了一个名字,叫a

        我们把这样的一组数据的集合称为数组(Array),所包含的每一个数据叫做数组元素(Element),所包含的数据的个数称为数组长度(Length),数组中的每个元素都有一个序号,这个序号从0开始,而不是从我们熟悉的1开始,称为下标(Index)

        例如int a[4];就定义了一个长度为4的整型数组,名字是a。a[index]使用数组元素时,指明下标即可,a为数组名称,index 为下标。 接下来我们就把第一行的4个整数放入数组:


  
  1. a[0]=2021;
  2. a[1]=803;
  3. a[2]=13;
  4. a[3]=43;

        这里的0、1、2、3就是数组下标,a[0]、a[1]、a[2]、a[3] 就是数组元素。

        在学习过程中,我们经常会使用循环结构将数据放入数组中(也就是为数组元素逐个赋值),然后再使用循环结构输出(也就是依次读取数组元素的值),下面我们就来演示一下如何将 1~10 这十个数字放入数组中:


  
  1. #include <stdio.h>
  2. int main(){
  3.     int nums[10];
  4.     int i;
  5.    
  6.     //将1~10放入数组中
  7.     for(i=0; i<10; i++){
  8.         nums[i] = (i+1);
  9.     }
  10.    
  11.     //依次输出数组元素
  12.     for(i=0; i<10; i++){
  13.         printf("%d ", nums[i]);
  14.     }
  15.    
  16.     return 0;
  17. }

        运行结果

1 2 3 4 5 6 7 8 9 10 

 

        变量 i 既是数组下标,也是循环条件;将数组下标作为循环条件,达到最后一个元素时就结束循环。数组 nums 的最大下标是 9,也就是不能超过 10,所以我们规定循环的条件是 i<10,一旦 i 达到 10 就得结束循环。

        最后我们来总结一下数组的定义方式:

dataType  arrayName[length];

 

        dataType 为数据类型,arrayName 为数组名称,length 为数组长度。例如:


  
  1. float m[12];  //定义一个长度为 12 的浮点型数组
  2. char ch[9];  //定义一个长度为 9 的字符型数组

需要注意的是:

  • 1.数组中每个元素的数据类型必须相同,对于int a[4];,每个元素都必须为 int。

  • 2.数组长度 length 最好是整数或者常量表达式,例如 10、20+4 等,这样在所有编译器下都能运行通过;如果 length 中包含了变量,例如 n、4+m 等,在某些编译器下就会报错。

  • 3.访问数组元素时,下标的取值范围为 0 ≤ index < length,过大或过小都会越界导致数组溢出,发生不可预测的情况,请大家务必要引起注意。

数组内存是连续的

        数组是一个整体,它的内存是连续的;也就是说,数组元素之间是相互挨着的,彼此之间没有一点点缝隙。下图演示了int a[4];在内存中的存储情形:

        数组内存是连续的这一点很重要,所以我使用了一个大标题来强调。连续的内存为指针操作(通过指针来访问数组元素)和内存处理(整块内存的复制、写入等)提供了便利,这使得数组可以作为缓存(临时存储数据的一块内存)使用。大家暂时可能不理解这句话是什么意思,等后边学了指针和内存自然就明白了。

数组的初始化

        上面的代码是先定义数组再给数组赋值,我们也可以在定义数组的同时赋值,例如:

int a[4] = {2034570022};

 

        数组元素的值由{}包围,各个值之间以,分隔

对于数组的初始化需要注意以下几点:

  • 1.可以只给部分元素赋值。当{}中值的个数少于元素个数时,只给前面部分元素赋值。例如:

int a[10]={121922 , 993344};

 

        表示只给 a[0]~a[4] 5个元素赋值,而后面 5 个元素自动初始化为 0。

        当赋值的元素少于数组总体元素的时候,剩余的元素自动初始化为 0:

        1、对于short、int、long,就是整数 0;

        2、对于char,就是字符 '\0';

        3、 对于float、double,就是小数 0.0。

        我们可以通过下面的形式将数组的所有元素初始化为 0:


  
  1. int nums[10] = {0};
  2. char str[10] = {0};
  3. float scores[10] = {0.0};

        由于剩余的元素会自动初始化为 0,所以只需要给第 0 个元素赋值为 0 即可。

  • 2.只能给元素逐个赋值,不能给数组整体赋值。例如给 10 个元素全部赋值为 1,只能写作:

int a[10] = {1111111111};

 

        而不能写成:

int a[10] = 1;

 
  • 3.如给全部元素赋值,那么在定义数组时可以不给出数组长度。例如:

int a[] = {12345};

 

        相当于

int a[5] = {12345};

 

二维数组

        在实际问题中有很多数据是二维的或多维的,因此C语言允许构造多维数组。多维数组元素有多个下标,以确定它在数组中的位置。多维数组可由二维数组类推而得到。

二维数组的定义

        二维数组定义的一般形式是:

dataType arrayName[length1][length2];

 

        其中,dataType 为数据类型,arrayName 为数组名,length1 为第一维下标的长度,length2 为第二维下标的长度

        我们可以将二维数组看做一个 Excel 表格,有行有列,length1 表示行数,length2 表示列数,要在二维数组中定位某个元素,必须同时指明行和列。例如:

int a[3][4];

 

        定义了一个 3 行 4 列的二维数组,共有 3×4=12 个元素,数组名为 a,即:


  
  1. a[0][0]a[0][1]a[0][2]a[0][3]
  2. a[1][0]a[1][1]a[1][2]a[1][3]
  3. a[2][0]a[2][1]a[2][2]a[2][3]

        如果想表示第 2 行第 1 列的元素,应该写作 a[2][1]。

        二维数组在概念上是二维的,但在内存中是连续存放的;换句话说,二维数组的各个元素是相互挨着的,彼此之间没有缝隙。那么,如何在线性内存中存放二维数组呢?有两种方式:

  • 一种是按行排列, 即放完一行之后再放入第二行;

  • 另一种是按列排列, 即放完一列之后再放入第二列。

        在C语言中,二维数组是按行排列的。 也就是先存放 a[0] 行,再存放 a[1] 行,最后存放 a[2] 行;每行中的 4 个元素也是依次存放。数组 a 为 int 类型,每个元素占用 4 个字节,整个数组共占用 4×(3×4)=48 个字节。

        你可以这样认为,二维数组是由多个长度相同的一维数组构成的。

二维数组的初始化

        二维数组的初始化可以按行分段赋值,也可按行连续赋值。

        例如,对于数组 a[5][3],按行分段赋值应该写作:

int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };

 

        按行连续赋值应该写作:

int a[5][3]={807592616571596370858790767785};

 

        这两种赋初值的结果是完全相同的。

对于二维数组的初始化还要注意以下几点:

  • 1.可以只对部分元素赋值,未赋值的元素自动取值。例如:

int a[3][3] = {{0,1}, {0,0,2}, {3}};

 

        是对每一行的第一列元素赋值,未赋值的元素的值为 0。赋值后各元素的值为:


  
  1. 0  1  0
  2. 0  0  2
  3. 3  0  0
  • 2.如果对全部元素赋值,那么第一维的长度可以不给出。例如:

int a[3][3] = {123456789};

 

        可以写为:

int a[][3] = {123456789};

 
  • 3.二维数组可以看作是由一维数组嵌套而成的;如果一个数组的每个元素又是一个数组,那么它就是二维数组。当然,前提是各个元素的类型必须相同。根据这样的分析,一个二维数组也可以分解为多个一维数组,C语言允许这种分解。

        例如,二维数组a[3][4]可分解为三个一维数组,它们的数组名分别为 a[0]、a[1]、a[2]。

        这三个一维数组可以直接拿来使用。这三个一维数组都有 4 个元素,比如,一维数组 a[0] 的元素为 a[0][0]、a[0][1]、a[0][2]、a[0][3]。

数组小结

  • 数组用来保存大量相同类型的数据。

  • 数组是相同数据类型的变量的排列。

  • 构成数组的小格子的个数称为数组元素数。

  • 二维数组多用于存储多行多列的二维表格数据。

  • 二维数组如同多层楼房,每层有相同数量的房间。

  • 数组的数据类型就是它里面存储的数据的数据类型。

  • 二维数组的行编号和列编号都是从 0 开始的序列号。

  • 数组和变量一样,在使用前必须先定义(声明数组)。

  • 数组元素用元素编号(从 0 开始的序列号)进行管理。

  • 二维数组数组元素的引用格式:数组名 [行编号] [列编号]。

  • 元素编号(下标)可以标识指定的数组元素:数组名 [下标]。

  • 二维数组用行编号和列编号两个下标来指定和引用数组元素。

字符数组和字符串

        用来存放字符的数组称为字符数组,例如:


  
  1. char a[3];  //一维字符数组
  2. char b[3][4];  //二维字符数组
  3. char c[20]={'s''h''u''a''i''c''i'};  // 给部分数组元素赋值
  4. char d[]={'s''h''u''a''i''c''i'};  //对全体元素赋值时可以省去长度

        字符数组实际上是一系列字符的集合,也就是字符串(String)。在C语言中,没有专门的字符串变量,没有string类型,通常就用一个字符数组来存放一个字符串。

        C语言规定,可以将字符串直接赋值给字符数组,例如:


  
  1. char str[30] = {"shuaici.blog.csdn.net"};
  2. char str[30] = "shuaici.blog.csdn.net";  //这种形式更加简洁,实际开发中常用

        数组第 0 个元素为's',第 1 个元素为'h',第 2 个元素为'u',后面的元素以此类推。

        为了方便,你也可以不指定数组长度,从而写作:


  
  1. char str[] = {"shuaici.blog.csdn.net"};
  2. char str[] = "shuaici.blog.csdn.net"//这种形式更加简洁,实际开发中常用

        给字符数组赋值时,我们通常使用这种写法,将字符串一次性地赋值(可以指明数组长度,也可以不指明),而不是一个字符一个字符地赋值,那样做太麻烦了。

        这里需要留意一个坑,字符数组只有在定义时才能将整个字符串一次性地赋值给它,一旦定义完了,就只能一个字符一个字符地赋值了。请看下面的例子:


  
  1. char str[7];
  2. str = "abc123";  //错误
  3. //正确
  4. str[0] = 'a'str[1] = 'b'str[2] = 'c';
  5. str[3] = '1'str[4] = '2'str[5] = '3';

字符串结束标志(划重点)

        字符串是一系列连续的字符的组合,要想在内存中定位一个字符串,除了要知道它的开头,还要知道它的结尾。找到字符串的开头很容易,知道它的名字(字符数组名或者字符串名)就可以;然而,如何找到字符串的结尾呢?C语言的解决方案有点奇妙,或者说有点奇葩。

        在C语言中,字符串总是以 '\0' 作为结尾,所以 '\0' 也被称为字符串结束标志,或者字符串结束符。

        '\0'是 ASCII 码表中的第 0 个字符,英文称为 NUL,中文称为空字符。该字符既不能显示,也没有控制功能,输出该字符不会有任何效果,它在C语言中唯一的作用就是作为字符串结束标志。

        C语言在处理字符串时,会从前往后逐个扫描字符,一旦遇到'\0'就认为到达了字符串的末尾,就结束处理。'\0'至关重要,没有'\0'就意味着永远也到达不了字符串的结尾。

        由" "包围的字符串会自动在末尾添加'\0'。例如,"abc123"从表面看起来只包含了 6 个字符,其实不然,C语言会在最后隐式地添加一个'\0',这个过程是在后台默默地进行的,所以我们感受不到。

        需要注意的是,逐个字符地给数组赋值并不会自动添加'\0',例如:

char str[] = {'a''b''c'};

 

        数组 str 的长度为 3,而不是 4,因为最后没有'\0'。

        当用字符数组存储字符串时,要特别注意'\0',要为'\0'留个位置;这意味着,字符数组的长度至少要比字符串的长度大 1。请看下面的例子:

char str[7] = "abc123";

 

        abc123看起来只包含了 6 个字符,我们却将 str 的长度定义为 7,就是为了能够容纳最后的'\0'。如果将 str 的长度定义为 6,它就无法容纳'\0'了。

        当字符串长度大于数组长度时,有些较老或者不严格的编译器并不会报错,甚至连警告都没有,这就为以后的错误埋下了伏笔,读者自己要多多注意。

字符串长度

        所谓字符串长度,就是字符串包含了多少个字符(不包括最后的结束符'\0')。例如"abc"的长度是 3,而不是 4。

        在C语言中,我们使用string.h头文件中的 strlen() 函数来求字符串的长度,它的用法为:

length strlen(strname);

 

        strname 是字符串的名字,或者字符数组的名字;length 是使用 strlen() 后得到的字符串长度,是一个整数

        实例


  
  1. #include <stdio.h>
  2. #include <string.h>  //记得引入该头文件
  3. int main(){
  4.     char str[] = "https://shuaici.blog.csdn.net/";
  5.     long len = strlen(str);
  6.     printf("Length: %ld.\n", len);
  7.    
  8.     return 0;
  9. }

        运行结果:

Length: 30.

 

数组元素的查询

        在实际开发中,经常需要查询数组中的元素。不幸的是,C语言标准库没有提供与数组查询相关的函数,所以我们只能自己编写代码。

对无序数组的查询

        所谓无序数组,就是数组元素的排列没有规律。无序数组元素查询的思路也很简单,就是用循环遍历数组中的每个元素,把要查询的值挨个比较一遍。请看下面的代码:


  
  1. #include <stdio.h>
  2. int main(){
  3.     int nums[10] = {15104221095228034468999};
  4.     int i, num, thisindex = -1;
  5.     num = 522;
  6.     for(i=0; i<10; i++){
  7.         if(nums[i] == num){
  8.             thisindex = i;
  9.             break;
  10.         }
  11.     }
  12.     if(thisindex < 0){
  13.         printf("数组不存在元素 %d .\n", num);
  14.     }else{
  15.         printf("数组存在元素 %d , 所在位置 %d.\n", num, thisindex);
  16.     }
  17.     return 0;
  18. }

        运行结果

数组存在元素 522 , 所在位置 5.

 

        注意:数组下标的取值范围是非负数,当 thisindex >= 0 时,该数字在数组中,当 thisindex < 0 时,该数字不在数组中,所以在定义 thisindex 变量时,必须将其初始化为一个负数。

对有序数组的查询

        查询无序数组需要遍历数组中的所有元素,而查询有序数组只需要遍历其中一部分元素。例如有一个长度为 10 的整型数组,它所包含的元素按照从小到大的顺序(升序)排列,假设比较到第 4 个元素时发现它的值大于输入的数字,那么剩下的 5 个元素就没必要再比较了,肯定也大于输入的数字,这样就减少了循环的次数,提高了执行效率。

请看下面的代码:


  
  1. #include <stdio.h>
  2. int main(){
  3.  int nums[10] = {41015224468109522803999};
  4.     int i, num, thisindex = -1;
  5.     num = 522;
  6.     for(i=0; i<10; i++){
  7.         if(nums[i] == num){
  8.             thisindex = i;
  9.             break;
  10.         }else if(nums[i] > num){
  11.             break;
  12.         }
  13.     }
  14.     if(thisindex < 0){
  15.         printf("数组不存在元素 %d .\n", num);
  16.     }else{
  17.         printf("数组存在元素 %d , 所在位置 %d.\n", num, thisindex);
  18.     }
  19.     return 0;
  20. }

        与前面的代码相比,这段代码的改动很小,只增加了一个判断语句。因为数组元素是升序排列的,所以当 nums[i] > num 时,i 后边的元素也都大于 num 了,num 肯定不在数组中了,就没有必要再继续比较了,终止循环即可。

字符串处理函数

        C语言提供了丰富的字符串处理函数,可以对字符串进行输入、输出、合并、修改、比较、转换、复制、搜索等操作,使用这些现成的函数可以大大减轻我们的编程负担。

        string.h是一个专门用来处理字符串的头文件,它包含了很多字符串处理函数。

字符串连接函数 strcat()

        strcat 是 string catenate 的缩写,意思是把两个字符串拼接在一起,语法格式为:

strcat(arrayName1, arrayName2);

 

        arrayName1、arrayName2 为需要拼接的字符串。

        strcat() 将把 arrayName2 连接到 arrayName1 后面,并删除原来 arrayName1 最后的结束标志'\0'。这意味着,arrayName1 必须足够长,要能够同时容纳 arrayName1 和 arrayName2,否则会越界(超出范围)。

        strcat() 的返回值为 arrayName1 的地址。

        请看下面的例子:


  
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(){
  4.     char str1[100]="The URL is ";
  5.     char str2[60]="https://shuaici.blog.csdn.net/";
  6.     strcat(str1, str2);
  7.     puts(str1); 
  8.     return 0;
  9. }

        运行结果

The URL is https://shuaici.blog.csdn.net/

 

字符串复制函数 strcpy()

        strcpy 是 string copy 的缩写,意思是字符串复制,也即将字符串从一个地方复制到另外一个地方,语法格式为:

strcpy(arrayName1, arrayName2);

 

        strcpy() 会把 arrayName2 中的字符串拷贝到 arrayName1 中,字符串结束标志'\0'也一同拷贝。

        请看下面的例子:


  
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(){
  4.     char str1[100]="The URL is ";
  5.     char str2[60]="https://shuaici.blog.csdn.net/";
  6.     strcpy(str1, str2);
  7.     printf("str1: %s\n", str1);
  8.     return 0;
  9. }

        运行结果

str1https://shuaici.blog.csdn.net/

 

        将 str2 复制到 str1 后,str1 中原来的内容就被覆盖了。 注意: strcat() 要求 arrayName1 要有足够的长度,否则不能全部装入所拷贝的字符串。

字符串比较函数 strcmp()

        strcmp 是 string compare 的缩写,意思是字符串比较,语法格式为:

strcmp(arrayName1, arrayName2);

 

        arrayName1 和 arrayName2 是需要比较的两个字符串。

        字符本身没有大小之分,strcmp() 以各个字符对应的 ASCII 码值进行比较。strcmp() 从两个字符串的第 0 个字符开始比较,如果它们相等,就继续比较下一个字符,直到遇见不同的字符,或者到字符串的末尾。

返回值:

  • 若 arrayName1 和 arrayName2 相同,则返回0;

  • 若 arrayName1 大于 arrayName2,则返回大于 0 的值;

  • 若 arrayName1 小于 arrayName2,则返回小于0 的值。

        对4组字符串进行比较:


  
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(){
  4.     char a[] = "aBcDeF";
  5.     char b[] = "AbCdEf";
  6.     char c[] = "aacdef";
  7.     char d[] = "aBcDeF";
  8.     printf("a VS b: %d\n", strcmp(a, b));
  9.     printf("a VS c: %d\n", strcmp(a, c));
  10.     printf("a VS d: %d\n", strcmp(a, d));
  11.    
  12.     return 0;
  13. }

        运行结果


  
  1. a VS b32
  2. a VS c: -31
  3. a VS d: 0

如有错误,烦请斧正

文章来源: shuaici.blog.csdn.net,作者:帅次,版权归原作者所有,如需转载,请联系作者。

原文链接:shuaici.blog.csdn.net/article/details/119355398

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。