C语言学习笔记—数组和字符串

前言 

  •  数组(Array)是一些列具有相同类型的数据的集合,这些数据在内存中依次挨着存放,彼此之间没有缝隙。
  • C语言数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。

数组

  •  一维数组定义和使用:

  1. 数组就是在内存中连续的相同类型的变量空间。把一组数据的集合称为数组(Array),它所包含的每一个数据叫做数组元素(Element),所包含的数据的个数称为数组长度(Length)。
  2. 数组中的每个元素都有一个序号,这个序号从0开始,称为下标(Index)。使用数组元素时,指明下标即可,形式为:dataType arrayName[length],如:
    int a[10];        //定义10个int,第一个变量名是a[0],第二个是a[1],最后一个是a[9]      
  3. 定义一个数组时,[ ]里面要使用常量,但使用数组时,[ ]里面可以使常量或者变量。
  • 数组在内存的存储方式:

  1. 同一个数组所有的成员都是相同的数据类型。
  2. 所有的成员在内存中的地址是连续的。
  3. 数组名是一个地址的常量,代表数组中首元素的地址。
  • 一维数组初始化:

  1. 数组元素的值由{ }包围,各个值之间以,分隔。如:
    int a[10] = {1,2,3,4,5,6,7,8,9,10};    // 定义一个数组,同时初始化所有成员变量
  2.  可以只给部分元素赋值。当{ }中值的个数少于元素个数时,只给前面部分元素赋值,后面元素为零,这里的零代表:对于short、int、long,就是整数 0;对于char,就是字符 '\0';对于float、double,就是小数 0.0。如:
    int a[10] = {1,2,3,4,5};    // 初始化前5个成员,后面所有元素都设置为0
  3. 我们也可以将数组的所有元素初始化为 0。如:
    int nums[10] = {0};
    char str[10] = {0};
    float tur[10] = {0.0};
  4. 只能给元素逐个赋值,不能给数组整体赋值。如:
    int a[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};  // 正确形式
    int a[10] = 1;                               // 错误形式
  5.  若给全部元素赋值,那么在定义数组时可以不给出数组长度。如:
    int a[] = {1,2,3,4,5};    // 定义了一个数组,有5个成员
  • 冒泡排序法:

  1. 将一个无序的数组排序成一个有序的数组。
  2. 思路:每次对相邻的两个元素进行比较,若前者大于后者则进行交换,如此一趟下来最后一趟的就是最大元素,重复以上的步骤,除了已经确定的元素。程序如下:
    #include <stdio.h>
    
    int main()
    {
       	int a[] = {32,65,31,75,15,98,20,24,46,10};
    	int i,j;
    	int index = sizeof(a)/sizeof(a[0]);
    	for(i = 0; i < index ; i++)
    	{
    		for(j = 1; j < index - i; j++)
    		{
    			if(a[j-1] > a[j])
    			{
    				int tmp = a[j-1];
    				a[j-1] = a[j];
    				a[j] = tmp;
    			}
    		}
    	}
    	for(i = 0; i < index; i++)
    	{
    		printf("%d\n",a[i]);
    	}
       return 0;
    }
  • 二维数组定义和使用:

  1. 二维数组定义的一般形式是:其中,dataType 为数据类型,arrayName 为数组名,length1 为第一维下标的长度(行),length2 为第二维下标的长度(列)。
    dataType arrayName[length1][length2];
  2. 二维数组在概念上是二维的,但在内存中是连续存放的。
  3. 在C语言中,二维数组是按行排列的。如 int a[3][4],先存放 a[0] 行,再存放 a[1] 行,最后存放 a[2] 行;每行中的 4 个元素也是依次存放。数组 a 为 int 类型,每个元素占用 4 个字节,整个数组共占用 4×(3×4)=48 个字节。
  • 二维数组初始化:

  1. 二维数组的初始化可以按行分段赋值,也可按行连续赋值。如:
    int a[3][5]={ {1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15} };    // 按行分段赋值
    int a[3][5]={ {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};    // 按行连续赋值
  2.  可以只对部分元素赋值,未赋值的元素自动取“零”值。如:
    int a[3][3] = {{1}, {2}, {3}};
    对应元素:1  0  0
             2  0  0
             3  0  0
  3. 如果对全部元素赋值,那么第一维的长度可以不给出。如:
    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};
  4. 二维数组可以看作是由一维数组嵌套而成的;如果一个数组的每个元素又是一个数组,那么它就是二维数组。当然,前提是各个元素的类型必须相同。如:二维数组a[3][4]可分解为三个一维数组,它们的数组名分别为 a[0]、a[1]、a[2],这三个一维数组都有 4 个元素,比如,一维数组 a[0] 的元素为 a[0][0]、a[0][1]、a[0][2]、a[0][3]。

字符串与字符数组

  • 字符数组定义:

  1.  用来存放字符的数组称为字符数组。如:
    char a[10];    //一维字符数组
    char b[3][4];  //二维字符数组
    char c[10]={'a', '  ', 'c', 'd', 'e', 'f', 'g', 'h'};  // 给部分数组元素赋值
    char d[]={'a', ' ', 'b', 'c', 'd', 'e', 'f', 'g' };    //对全体元素赋值时可以省去长度
  2. 字符数组实际上是一系列字符的集合,也就是字符串(String)。在C语言中,没有专门的字符串变量,没有string类型,通常就用一个字符数组来存放一个字符串。
  • 字符数组初始化:

  1. C语言规定,可以将字符串直接赋值给字符数组。如:
    char str[20] = {"Hello World"};
    char str[20] = "Hello World";  
  2. 为了方便,也可以不指定数组长度。如:
    char str[] = {"Hello World"};
    char str[] = "Hello World";  
  3. 字符数组只有在定义时才能将整个字符串一次性地赋值给它,一旦定义完了,就只能一个字符一个字符地赋值了。如:
    char str[7];
    str = "abc123";  //错误
    /****正确****/
    str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
    str[3] = '1'; str[4] = '2'; str[5] = '3';
  4. 在C语言中,字符串总是以'\0'作为结尾,所以'\0'也被称为字符串结束标志,或者字符串结束符。
  5. 有些时候,程序的逻辑要求我们必须逐个字符地为数组赋值,这个时候就很容易遗忘字符串结束标志'\0',一种方法是在字符串的最后手动添加'\0';另一种是将数组的所有元素都初始化为“零”值。如:
    char str[30];
    int i;
    for(i=0; i<10; i++)
    {
        str[i] = a;
    }
    str[i] = 0;  //此处为添加的代码,也可以写作 str[i] = '\0';
    或
    char str[20] = {0};    // 标准规范
    int i;
    for(i=0; i<10; i++)
    {
        str[i] = a;
    }
  6. C语言在处理字符串时,会从前往后逐个扫描字符,一旦遇到'\0'就认为到达了字符串的末尾,就结束处理。
  7. 需要注意的是,逐个字符地给数组赋值并不会自动添加'\0'如:
    char str[] = {'a', 'b', 'c'};     // 数组 str 的长度为 3,而不是 4,因为最后没有'\0'
  8. 当用字符数组存储字符串时,要特别注意'\0',要为'\0'留个位置;这意味着,字符数组的长度至少要比字符串的长度大 1。如:
    char str[7] = "abcdef";   // 数组 str 的长度为 7,而不是 6,因为最后自动加'\0'
  • 随机数产生函数rand与srand:

  1. 需要头文件stdlib.h。
  2.  rand()函数原型:int rand(void) 产生一个0-RAND_MAX之间的整型伪随机数。(伪随机数:顾名思义是假的随机数,rand()本质线性同余法,产生的随机数是由公式计算得到,公式为:rand=rand*const_1+c_var ,rand初始为1。
  3.  srand()函数原型:void srand(unsigned int seed) seed为种子,seed必须为一个变量,一般会用time.srand()函数初始化随机发生器。
  4.   srand()函数与rand()函数组合使用,作为rand()函数的随机数种子,每次产生不同的随机数。srand()函数中的种子值也就是上面公式中的左侧rand值。(组合使用一般用在要求每次产生不同的随机数)。
  5. 工作过程:1)首先给srand()提供一个种子,它是一个unsigned int类型,其取值范围从0~65535;2)然后调用rand(),它会根据提供给srand()的种子值返回一个随机数(在0到32767之间);3)根据需要多次调用rand(),从而不间断地得到新的随机数;4)无论什么时候,都可以给srand()提供一个新的种子,从而进一步“随机化”rand()的输出结果。如:
    /*****产生0-32767之间的随机数******/
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>                     //使用当前时钟做种子
    
    int main()
    {
        int i;
        unsigned int tm = time(NULL);
        srand(tm);                        //初始化随机数
        for( i = 0; i < 10; i++ )         //打印出10个随机数
            printf( "%d\n", rand() );
        return 0;
    }
    -------------------------------------------------------------------------------------
    /*****产生0-1之间的随机数******/
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>                     //使用当前时钟做种子
    
    int main()
    {
        int i;
        unsigned int tm = time(NULL);
        srand(tm);                        //初始化随机数
        for( i = 0; i < 10; i++ )         //打印出10个随机数
            printf( "%5.2f\n", rand()/32767.0 );
        return 0;
    }
    -------------------------------------------------------------------------------------
    /*****产生1-100之间的随机数******/
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>                     //使用当前时钟做种子
    
    int main()
    {
        int i;
        unsigned int tm = time(NULL);
        srand(tm);                        //初始化随机数
        for( i = 0; i < 10; i++ )         //打印出10个随机数
            printf( "%d\n", rand() % 100 + 1);
        return 0;
    }

字符串处理函数

  • gets输入字符串:

  1.  直接输入字符串,并且只能输入字符串。
    gets( char *s);   // 参数是一个字符数组
  2. 注意:gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对 gets() 来说就是一个完整的字符串。换句话说,gets() 用来读取一整行字符串。要注意缓冲区溢出问题。
  • scanf输入字符串:

  1. 用户可以通过键盘把一个字符串写入一个char的数组。如:
    char a[100];
    scanf("%s",a);
  2. scanf并不会直接读入用户最后输入完成的回车键,而是最后自动补个0,这样才是完整的字符串,scanf不检查参数的char数组的下标,用户输入多少,scanf就会往数组里放多少,一旦用户输入的过多,会导致内存错误,所以要注意缓冲区溢出问题。类型中,int、char、float 等类型的变量用于 scanf() 时都要在前面添加&,而数组或者字符串用于 scanf() 时不用添加&,它们本身就会转换为地址。
  3. 注意:scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
  • fgets函数:

  1. gets函数不检查预留缓冲区是否能够容纳用户实际输入的数据,多出来的字符会导致内存溢出,fgets函数改进了这个问题。
  2. fgets函数主要是为读取文件设计的,有三个参数,第一个参数是char的数组;第二个参数是标明这个数组的大小;第三个参数是FILE指针,如果是通过键盘输入可以固定写成stdin。如下:
    char *fgets (char *s, int size, FILE *stream);     // 常用形式
    char *fgets (char *s, int size, stdin)             // 键盘输入形式
    如:
    char s[100] = {0};
    fgets(s, sizeof(s), stdin);
  3. fgets在读取一个用户通过键盘输入的字符串时,同时把用户输入的回车也作为字符串的一部分。
  4. 如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。如果发生错误,返回一个空指针。
  • puts字符串输出函数:

  1. puts函数打印字符串,与printf不同,puts会在最后自动添加一个' \n ',即自动换行。
  2. puts不支持各种转义字符,比如%s,%d,只能简单的直接输出一个字符串,而不能输出char,int,double等其他类型。表现形式:
    char s[] = "hello world";
    puts(s);
  • fputs函数:

  1. fputs是puts的文件操作版本。
  2. 共2个参数,第一个参数是一个char数组,第二个参数是FILE指针,如果只用puts在屏幕输出的话,可以固定写成stdout。
    int fputs(const char *str, FILE *stream)
  3. 不会自动输出一个\n,该函数返回一个非负值,如果发生错误则返回 EOF。
  • strlen,字符串长度:

  1. 所谓字符串长度,就是字符串包含了多少个字符(不包括最后的结束符'\0')。
  2. 在C语言中,我们使用string.h头文件中的 strlen() 函数来求字符串的长度。用法:
    size_t strlen(const char *_str);
    如:
    len = strlen(strname);  // strname是字符串的名字,或字符数组的名字;len是返回的字符串长度,是一个整数。
  • strcat,字符串追加:

  1. strcat 是 string catenate 的缩写,意思是把两个字符串拼接在一起。需要头文件string.h。
  2. 函数声明如下:
    char *strcat(char *dest, const char *src) // 将参数src追加到dest
  • strncat,字符串有限追加:

  1. 需要头文件string.h。
  2. 函数声明如下:
    char *strncat(char *dest, const char *src, size_t n) // 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。
  • strcmp,字符串比较:

  1. strcmp 是 string compare 的缩写,意思是字符串比较。需要头文件string.h。
  2. 字符本身没有大小之分,strcmp() 以各个字符对应的 ASCII 码值进行比较。strcmp() 从两个字符串的第 0 个字符开始比较,如果它们相等,就继续比较下一个字符,直到遇见不同的字符,或者到字符串的末尾。
  3. 函数声明如下:
    int strcmp(const char *str1, const char *str2)
  4. 如果返回值 < 0,则表示 str1 小于 str2。如果返回值 > 0,则表示 str2 小于 str1。如果返回值 = 0,则表示 str1 等于 str2。
  • strcmp,字符串有限比较:

  1. 需要头文件string.h。
  2. 函数声明如下:
    int strncmp(const char *str1, const char *str2, size_t n) // 把 str1 和 str2 进行比较,最多比较前 n 个字节。
  3. 如果返回值 < 0,则表示 str1 小于 str2。如果返回值 > 0,则表示 str2 小于 str1。如果返回值 = 0,则表示 str1 等于 str2。
  • strcpy,字符串拷贝:

  1. strcpy 是 string copy 的缩写,意思是字符串复制,字符串结束标志‘\0’也一同拷贝。需要头文件string.h。
  2. 函数声明如下:
    char *strcpy(char *dest, const char *src) // 把 src 所指向的字符串复制到 dest。
  • strncpy,字符串有限拷贝:

  1. 需要头文件string.h。
  2. 函数声明如下:
    char *strncpy(char *dest, const char *src, size_t n); // 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
    如:
    char a[10] = "abc";
    char b[100] = "qwertttyuuu";
    strncpy(a, b, sizeof(a) -1); // 减一是为了在字符串最后留一个放零的位置
    
  • sprintf,格式化字符串:

  1. printf是向标准输出设备输出一个字符串,sprintf向一个char数组输出一个字符串。sprinf和printf用法基本一致,区别是多了个第一个参数,所有printf的转义字符对于sprintf都一样。
  2. 函数声明如下:
    int sprintf(char *str, const char *format, ...); // 发送格式化输出到 str 所指向的字符串。
  3. str -- 这是指向一个字符数组的指针,该数组存储了 C 字符串。format -- 这是字符串,包含了要被写入到字符串 str 的文本。
  4. 如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。程序例子:输出结果:Pi 的值 = 3.1415926
    #include <stdio.h>
    #define PI 3.1415926
    
    int main()
    {
       char str[80];
    
       sprintf(str, "Pi 的值 = %f", PI);
       puts(str);
       
       return(0);
    }
    
  • sscanf函数:

  1. sscanf类似于scanf函数,scanf从键盘读取用户输入,sscanf从指定格式化字符串读取输入。sscanf多了第一个参数,char的数组,sscanf会从这个char数组中读取相关内容。
  2. 函数声明如下:
    int sscanf(const char *str, const char *format, ...); // 从字符串读取格式化输入
  3. 程序例子:输出结果:Saturday, March, 16, 2019 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main()
    {
       int day, year;
       char weekday[20], month[20], dtm[100];
       strcpy( dtm, "Saturday March 16 2019" );
       sscanf( dtm, "%s %s %d  %d", weekday, month, &day, &year );
       printf("%s, %s, %d, %d\n",weekday, month, day, year );   
       return(0);
    }
  • strchr函数:

  1. 需要头文件string.h。
  2. 函数声明如下:
    char *strchr(const char *str, int c); // 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置
  3. 该函数返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL。如下程序,输出结果:world
    #include <stdio.h>
    #include <string.h>
    
    int main ()
    {
       char str[] = "hello world";
       char *s;
       s = strchr(str, 'w');
    
       printf("%s\n", s);
       return 0;
    }
  • strchr函数:

  1. 需要头文件string.h。
  2. 函数声明如下:
    char *strstr(const char *haystack, const char *needle); // 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 '\0'。
  3. 该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 NULL。
  • strtok函数:

  1. 需要头文件string.h。
  2. 函数声明i如下:
    char *strtok(char *str, const char *delim); // 分解字符串 str 为一组字符串,delim 为分隔符
  3. 该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。如下程序,输出结果:I   LOVE   YOU
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        char a[] = "I_LOVE_YOU";
        char *s1, *s2, *s3;       // 定义一个char的指针变量
        s1 = strtok(a, "_");
        s2 = strtok(NULL, "_");   // 第二次调用到时,第一个参数写NULL
        s3 = strtok(NULL, "_");
        printf("%s %s %s\n", s1,s2,s3);
    
        return 0;
    }
  4. 优化程序:输出结果一样,有换行。
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        char a[] = "I_LOVE_YOU";
        char *s;                  // 定义一个char的指针变量
        s = strtok(a, "_");
        while(s)
        {
            printf("%s\n", s);
            s = strtok(NULL, "_");
        }
    
        return 0;
    }
  • atoi转化为int:

  1. 需要头文件stdlib.h。
  2. 函数声明如下:
    int atoi(const char *str); //  把参数 str 所指向的字符串转换为一个整数(类型为 int 型)
  3. 该函数返回转换后的长整数,如果没有执行有效的转换,则返回零。
  • atof转化为float:

  1. 需要头文件stdlib.h。
  2. 函数声明如下:
    double atof(const char *str); // 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)
  3. 函数返回转换后的双精度浮点数,如果没有执行有效的转换,则返回零(0.0)。
  • atol转化为long:

  1. 需要头文件stdlib.h。
  2. 函数声明如下:
    long int atol(const char *str); // 把参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)
  3. 该函数返回转换后的长整数,如果没有执行有效的转换,则返回零。
  • 练习:已知字符串a[100]="1+2=;10-5;2*10=;30/3=",要求计算出字符串a中的各个值,并填写上。输出结果:1+2=3;10-55;2*10=20;30/3=10;

    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        char a[100] = "1+2=;10-5;2*10=;30/3=";
        char b[100] = { 0 };
        char *s = strtok(a, ";");
        while(s)
        {
            int i, j;
            char c;
            sscanf(s, "%d%c%d=", &i, &c, &j);
            int res = 0;
            switch(c)
            {
                case '+':
                    res = i + j;
                    break;
                case '-':
                    res = i - j;
                    break;
                case '*':
                    res = i * j;
                    break;
                case '/':
                    res = i / j;
                    break;
                default:
                    res = 0;
            }
            char tmp[10] ={0};
            sprintf(tmp,"%s%d;",s,res);
            strcat(b, tmp);
            s = strtok(NULL, ";");
        }
        strcpy(a, b);
        printf("%s\n", a);
        return 0;
    }

猜你喜欢

转载自blog.csdn.net/qq_34935373/article/details/88548093