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

简介: 数组一维数组一维数组的定义数组内存是连续的数组的初始化二维数组二维数组的定义二维数组的初始化数组小结字符数组和字符串字符串结束标志(划重点)字符串长度实例数组元素的查询对无序数组的查询对有序数组的查询字符串处理函数字符串连接函数 strcat()字符串复制函数 strcpy()字符串比较函数 strcmp()

数组


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


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


微信图片_20220521163153.png


一维数组


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


一维数组的定义


       我们知道,要想把数据放入内存,必须先要分配内存空间。放入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个整数放入数组:


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


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


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


#include <stdio.h>
int main(){
    int nums[10];
    int i;
    //将1~10放入数组中
    for(i=0; i<10; i++){
        nums[i] = (i+1);
    }
    //依次输出数组元素
    for(i=0; i<10; i++){
        printf("%d ", nums[i]);
    }
    return 0;
}


运行结果


1 2 3 4 5 6 7 8 9 10 


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


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


dataType  arrayName[length];


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


float m[12];  //定义一个长度为 12 的浮点型数组
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];在内存中的存储情形:


微信图片_20220521163712.png


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


数组的初始化


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


int a[4] = {20, 345, 700, 22};


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


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


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


int a[10]={12, 19, 22 , 993, 344};


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


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


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


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


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


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


int nums[10] = {0};
char str[10] = {0};
float scores[10] = {0.0};


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


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


int a[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};

   

而不能写成:


int a[10] = 1;


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


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

       相当于


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


二维数组


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


二维数组的定义


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


dataType arrayName[length1][length2];


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


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


int a[3][4];


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


a[0][0], a[0][1], a[0][2], a[0][3]
a[1][0], a[1][1], a[1][2], a[1][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]={80, 75, 92, 61, 65, 71, 59, 63, 70, 85, 87, 90, 76, 77, 85};

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


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


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


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


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


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


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

       可以写为:


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


  • 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 开始的序列号)进行管理。


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


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


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


字符数组和字符串


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


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


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


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


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


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


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


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


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


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


char str[7];
str = "abc123";  //错误
//正确
str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
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() 后得到的字符串长度,是一个整数。


实例

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

   

运行结果:


Length: 30.


数组元素的查询


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


对无序数组的查询


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


#include <stdio.h>
int main(){
    int nums[10] = {15, 10, 4, 22, 109, 522, 803, 44, 68, 999};
    int i, num, thisindex = -1;
    num = 522;
    for(i=0; i<10; i++){
        if(nums[i] == num){
            thisindex = i;
            break;
        }
    }
    if(thisindex < 0){
        printf("数组不存在元素 %d .\n", num);
    }else{
        printf("数组存在元素 %d , 所在位置 %d.\n", num, thisindex);
    }
    return 0;
}

 

  运行结果


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


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


对有序数组的查询


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


请看下面的代码:


#include <stdio.h>
int main(){
 int nums[10] = {4, 10, 15, 22, 44, 68, 109, 522, 803, 999};
    int i, num, thisindex = -1;
    num = 522;
    for(i=0; i<10; i++){
        if(nums[i] == num){
            thisindex = i;
            break;
        }else if(nums[i] > num){
            break;
        }
    }
    if(thisindex < 0){
        printf("数组不存在元素 %d .\n", num);
    }else{
        printf("数组存在元素 %d , 所在位置 %d.\n", num, thisindex);
    }
    return 0;
}

   

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


字符串处理函数


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


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

微信图片_20220521165243.png


字符串连接函数 strcat()


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


strcat(arrayName1, arrayName2);


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


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


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


       请看下面的例子:


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

       运行结果


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


字符串复制函数 strcpy()


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


strcpy(arrayName1, arrayName2);


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


       请看下面的例子:


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

       运行结果


str1: https://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组字符串进行比较:


#include <stdio.h>
#include <string.h>
int main(){
    char a[] = "aBcDeF";
    char b[] = "AbCdEf";
    char c[] = "aacdef";
    char d[] = "aBcDeF";
    printf("a VS b: %d\n", strcmp(a, b));
    printf("a VS c: %d\n", strcmp(a, c));
    printf("a VS d: %d\n", strcmp(a, d));
    return 0;
}

   

 运行结果


a VS b: 32
a VS c: -31
a VS d: 0


如有错误,烦请斧正


相关文章
|
16天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
39 10
|
16天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
37 9
|
16天前
|
存储 Unix Serverless
【C语言】常用函数汇总表
本文总结了C语言中常用的函数,涵盖输入/输出、字符串操作、内存管理、数学运算、时间处理、文件操作及布尔类型等多个方面。每类函数均以表格形式列出其功能和使用示例,便于快速查阅和学习。通过综合示例代码,展示了这些函数的实际应用,帮助读者更好地理解和掌握C语言的基本功能和标准库函数的使用方法。感谢阅读,希望对你有所帮助!
31 8
|
16天前
|
C语言 开发者
【C语言】数学函数详解
在C语言中,数学函数是由标准库 `math.h` 提供的。使用这些函数时,需要包含 `#include <math.h>` 头文件。以下是一些常用的数学函数的详细讲解,包括函数原型、参数说明、返回值说明以及示例代码和表格汇总。
39 6
|
16天前
|
存储 C语言
【C语言】输入/输出函数详解
在C语言中,输入/输出操作是通过标准库函数来实现的。这些函数分为两类:标准输入输出函数和文件输入输出函数。
93 6
|
16天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
47 6
|
16天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
25 5
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
38 3
|
27天前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
50 4