C语言笔记第03章:数组(四)

简介: C语言笔记第03章:数组

11.字符数组

11.1 字符数组定义与初始化

字符数组是数组的元素类型为字符型的数组。特殊之处在于它是数组元素为字符的数组。其定义与初始化的一般形式和注意事项与之前讲解的一般数组类似,只是其中的类型说明符是char。当然,并不是说类型说明符只能是char,也可以是long、int等,但是由于char型只占用一个字节的大小,使用long型和int型来定义字符数组会造成资源的浪费,因此一般选择使用char型来定义字符数组。

//字符数组定义 - 这些都是一维数组,因此初始化的方式和一维数组是一样的
char chars1[10];
int  chars2[10];
long chars3[10];

我们来比较一下占用的空间大小:

#include<stdio.h>
int main()
{
  /*
    这里使用的一维数组初始化方式:
    对数组全部元素赋值,但不指定长度
  */
  char arr[] = { 'g','i','r','l' };
  /*
    这里使用的一维数组初始化方式:
    直接对数组中的全部元素赋值
  */
  int brr[4] = { 'g','i','r','l' };
  /*
    比较一下数组内存大小:
    char类型占一个字节,int类型占4个字节
    因此sizeof(arr)的值是 1 * 4 = 4
    因此sizeof(brr)的值是 4 * 4 = 16
  */
  printf("char类型数组占用内存:%lld\n" ,sizeof(arr));
  printf("int 类型数组占用内存:%lld" ,sizeof(brr));
  return 0;
}

在上面的代码中定义了不同类型的字符数组来存放相同的字符,可以看出,它们占用的内存大小相差很大,int型字符数组所占用内存大小是char型数组占用内存大小的4倍。从这点可以看出,选用char型作为字符数组类型避免了内存空间的浪费。

11.2 未完全初始化

#include<stdio.h>
int main() {
    int i;
    char arr[20] = { 'm','y',' ','g','i','r','l' };
    // sizeof(arr)/sizeof(arr[0] 的值为数组的长度 - 20
    for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
    {
        printf("%c", arr[i]);
    }
    return 0;
}

运行结果为“my girl”,其中有一些空字符。看看上面代码中定义的arr数组,其数组长度为20,而初始化的字符元素的个数为7,初始化的字符元素个数小于数组长度,编译器在编译过程中将后面没有初始化的数组元素赋值为‘\0’,这也正是打印输出中含有空字符的原因。

⚠️ 注意这个空字符不是空格符号。而是指的什么也不输出打印。

‘\0’代表ASCII码为0的字符,从ASCII码表中可以查到,ASCII码为0的字符不是一个可以显示的字符,而是一个“空操作符”,即它什么也不做。

#include<stdio.h>
int main() {
    int i;
    char arr[20] = { 'm','y',' ','g','i','r','l' };
    // sizeof(arr)/sizeof(arr[0] 的值为数组的长度 - 20
    for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
    {
        printf("%c", arr[i]);
    }
    printf("我爱过你");
    return 0;
}

我们看下上述代码对数组字符的打印次数:

#include<stdio.h>
int main() {
    int i;
    char arr[20] = { 'm','y',' ','g','i','r','l' };
    // sizeof(arr)/sizeof(arr[0] 的值为数组的长度 - 20
    for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
    {
        printf("打印第%d个字符:%c\n", i+1,arr[i]);
    }
    return 0;
}

但很明显,我们并不需要打印那些为’\0’的字符。因此,在打印的时候也可以将数组中的元素‘\0’视为数组结束的标志,例如:

#include<stdio.h>
int main() {
    int i;
    char arr[20] = { 'm','y',' ','g','i','r','l' };
    for (i = 0; arr[i]!='\0'; i++)
    {
        printf("打印第%d个字符:%c\n", i+1,arr[i]);
    }
    return 0;
}

11.3 字符串常量的方式来对一维字符数组进行初始化

在C语言中没有专门的数据类型来定义 字符串 ,因此我们是将字符串作为字符数组来处理的,字符串中的字符是逐个存放到数组元素中的。

11.3.1 不指定长度初始化

在对一维字符数组进行定义和初始化的过程中,可以不指定其长度:

#include<stdio.h>
int main() {
    int i;
    /*
        这里使用的一维数组初始化方式:
        对数组全部元素赋值,但不指定长度,
        但注意的是这是数组的长度不是3,而是4,
        因为我们在将字符串存入数组的时候会默认在末尾加上一个 \0 字符
        即以这种初始化方式对字符串进行存储会以 \0 作为数组的结束标志
    */
    char arr[] = "abc";
    // sizeof(arr) / sizeof(arr[0] 结果是 4
    for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
    {
        printf("打印第%d个字符:%c\n", i + 1, arr[i]);
    }
    return 0;
}

为了测定字符串的实际长度,C语言规定了一个“字符串结束标志”,以字符‘\0’作为结束标志。

我们可以进行一个对比:

#include<stdio.h>
int main()
{
  /*
    这里使用的一维数组初始化方式:
    对数组全部元素赋值,但不指定长度
  */
  char arr[] = { 'g','i','r','l' };
  /*
    1.char brr[] = { "girl" };
    2.char brr[] = "girl";
    加不加括号都可以,一样的
  */
  char brr[] = { "girl" };
  /*
    比较一下数组内存大小:
    1. sizeof(arr)的值是 1 * 4 = 4
    2.在将 "girl" 赋值给 brr 时会将这四个字母因此存入数组,
      同时在末尾还会追加一个 \0 字符,作为该数组结束的标志
      因此 sizeof(brr)的值是 1 * (4 + 1) = 5
  */
  printf("arr 数组占用内存:%lld\n", sizeof(arr));
  printf("brr 数组占用内存:%lld", sizeof(brr));
  return 0;
}

💬 采用这两种方式得到的数组长度并不相同,在采用字符串常量对字符数组进行初始化的过程中,在内存中进行存储时会自动在字符串的后面添加一个结束符‘\0’,所以得到的字符数组长度是字符串常量的长度加1;而采用字符常量列表的方式对字符数组进行初始化就不会在最后添加一个结束符,所以利用这种方式定义的字符数组的长度就是字符常量列表中字符的个数。

11.3.2 完全初始化

#include<stdio.h>
int main() {
    int i;
    /*
        数组的长度是4,
        由于规定了arr数组的长度为4,并且字符串常量的长度也是4,
        因此数组就没有空间在字符串最后追加 \0 了
    */
    char arr[4] = "girl";
    // sizeof(arr) / sizeof(arr[0] 结果是 4
    for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
    {
        printf("打印第%d个字符:%c\n", i + 1, arr[i]);
    }
    return 0;
}

11.3.3 未完全初始化

#include<stdio.h>
int main() {
    int i;
    /*
        数组的长度是10,
        由于规定了arr数组的长度为10,而字符串常量的长度也是4,
        因此数组其它部分都为 \0
    */
    char arr[10] = "girl";
    // sizeof(arr) / sizeof(arr[0] 结果是 10
    for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
    {
        printf("打印第%d个字符:%c\n", i + 1, arr[i]);
    }
    return 0;
}

11.4 C语言字符串处理函数

这些函数都是来自与 <stdio.h> 标准输入输出库,因此在使用这些函数的时候必须引入该库。

11.4.1 puts函数 - 输出字符串的函数

📍 将一个字符串输出到终端。基本语法:

puts(字符数组)
• 1

📝 演示:

#include<stdio.h>
int main() {
    char arr[] = "my girl , i love you";
    puts(arr);
    return 0;
}

11.4.2 gets函数 - 输入字符串的函数

📍 从终端输入一个字符串到字符数组,并且得到一个函数值。基本语法:

gets(字符数组)

📝 演示:

#include<stdio.h>
int main() {
    //必须声明长度
    char arr[20];
    //键盘输入
    printf("输入:");
    gets(arr);
    //输出
    printf("输出:");
    puts(arr);
    return 0;
}

⚠️ 关于使用 gets() 函数需要注意:使用 gets() 时,系统会将最后“敲”的换行符从缓冲区中取出来,然后丢弃,所以缓冲区中不会遗留换行符。这就意味着,如果前面使用过 gets(),而后面又要从键盘给字符变量赋值的话就不需要吸收回车清空缓冲区了,因为缓冲区的回车已经被 gets() 取出来扔掉了。

11.4.3 strcat函数 - 字符串连接函数

📍 把两个字符数组中的字符串连接起来,把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值——字符数组1的地址。基本语法:

strcat(字符数组1,字符数组2)

📝 演示:

#include<stdio.h>
int main() {
    //设置arr的长度为40,保证追加时有足够空间
    char arr[40] = "my girl ";
    char brr[] = "i love you";
    //将brr的字符追加到arr数组中
    strcat(arr, brr);
    puts(arr);
    return 0;
}

11.4.4 strcpy - 字符串复制函数

📍 将字符数组1拷贝到字符数组2中。基本语法:

strcpy(字符数组1,字符串2)

📝 演示:

#include<stdio.h>
int main() {
    char arr[20] = "i love you";
    char brr[] = "hate";
    //清空arr的内容,再将brr的内容逐一拷贝到arr
    strcpy(arr, brr);
    puts(arr);
    return 0;
}

11.4.5 strcmp函数 - 字符串比较函数

📍 比较字符串1和字符串2,将两个字符串自左向右逐个字符相比,直到出现不同的字符或遇到“\0”为止。如果全部字符相同,则认为两个字符串相等:若出现不相同的字符,则以第1对不相同的字符的比较结果为准。基本语法:

strcmp(字符串1,字符串2)

📝 演示:

#include<stdio.h>
int main() {
    char arr[] = "abc";
    char brr[] = "aaa";
    //两个数组的第二个字符一个为b,一个为a,
    //由于b的ASCII码值大于 a,因此此时结束比较,
    //对于 strcmp(字符串1,字符串2),
    //如果是 字符串1 大,则返回数字 1
    //如果是 字符串2 大,则返回数字 -1
    //如果完全相等,返回 0 
    printf("%d\n\n",strcmp(arr, brr));
    printf("%d\n\n", strcmp(brr, arr));
    printf("%d\n\n", strcmp(arr, "abc"));
    return 0;
}

11.4.6 strlen函数 - 测字符串长度的函数

📍 测试字符串长度的函数。函数的值为字符串中的实际(有效)长度,即在遇到 \0 以前的字符个数。基本语法:

strlen(字符数组)

📝 演示:

#include<stdio.h>
int main() {
    char arr[20] = "love";
    //有效长度,在 \0 之前的内容
    printf("数组有效长度:%d\n\n", strlen(arr));
    //数组的内存大小
    printf("数组内存大小:%lld", sizeof(arr));
    return 0;
}

11.4.7 strupr函数 - 全部转换为大写的函数

📍 将字符串中小写字母换成大写字母。基本语法:

strupr(字符串)

📝 演示:

#include<stdio.h>
int main() {
    char arr[20] = "Love";
    strupr(arr);
    puts(arr);
    return 0;
}

11.4.8 strlwr函数 - 转换为小写的函数

📍 将字符串中小写字母换成小写字母。基本语法:

strlwr(字符串)

📝 演示:

#include<stdio.h>
int main() {
    char arr[20] = "LovE";
    strlwr(arr);
    puts(arr);
    return 0;
}

11.5 VS中利用scanf_s函数输入字符串时出错问题解决

在vs中直接使用 scanf_s 输入函数会报错:

scanf_s函数还需加一个参数 :length参数,限定字符串的长度。若超过length参数将无法输入。

#include<stdio.h>
int main() {
  char arr[5];
  printf("输入:");
  // %s 是字符数组的格式占位符
  // sizeof(arr)是告诉编译器应当在缓冲区预留 sizeof(arr) 即5个字节大小的空间输入到arr
  // 同时由于这里会默认以 \0 作为字符串的结果,因此我们最多可以输入四个字符,否则报错
  scanf_s("%s", arr, sizeof(arr));
  printf("\n输出:");
  puts(arr);
  return 0;
}

11.6 vs中用scanf输入

在代码第一行加入预处理指令:#define _CRT_SECURE_NO_WARNINGS 1

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main() {
  char arr[5];
  printf("输入:");
  scanf("%s", arr);
  printf("\n输出:");
  puts(arr);
  return 0;
}

11.7 gets() 函数 与 scanf/scanf_s 函数的选择

gets() 函数不仅比 scanf 简洁,而且,就算输入的字符串中有空格也可以直接输入,不用像 scanf 那样要定义多个字符数组。我们来演示一下这个问题:

📝 使用 gets():

#include<stdio.h>
int main() {
  char arr[20];
  printf("输入:");
  gets(arr);
  printf("\n输出:");
  puts(arr);
  return 0;
}

📝 使用 scanf或者scanf_s:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main() {
  char arr[20];
  printf("输入:");
  scanf("%s", arr);
  printf("\n输出:");
  puts(arr);
  return 0;
}

12.数组总结

12.1 数组的定义

元素类型相同,大小相等。

12.2 数组的特点

  • 在内存中,数组是一块连续的区域。
  • 数组需要预留空间,在使用前要先申请占内存的大小,可能会浪费内存空间。
  • 插入数据和删除数据效率低,插入数据时,这个位置后面的数据在内存中都要向后移。
  • 随机读取效率很高。因为数组是连续的,知道每一个数据的内存地址,可以直接找到给定地址的数据。
  • 并且不利于扩展,数组定义的空间不够时要重新定义数组。

12.3 数组的优缺点

12.3.1 数组的优点

  • 随机访问性强
  • 查找速度快

12.3.2 数组的缺点

  • 插入和删除效率低
  • 可能浪费内存
  • 内存空间要求高,必须有足够的连续内存空间。
  • 数组大小固定,不能动态拓展

13.其他说明

愿你善待自己年轻的皮囊,也愿你拥有不会腐朽的有趣灵魂。我们每个人,不管是否肤白貌美,腰缠万贯,都值得有个人来好好爱你。所以你要加油,等到他或她来的时候,抱紧ta,告诉ta说,等了你好久好久,来了,就别再走了。❤️

相关文章
|
10天前
|
传感器 算法 安全
【C语言】两个数组比较详解
比较两个数组在C语言中有多种实现方法,选择合适的方法取决于具体的应用场景和性能要求。从逐元素比较到使用`memcmp`函数,再到指针优化,每种方法都有其优点和适用范围。在嵌入式系统中,考虑性能和资源限制尤为重要。通过合理选择和优化,可以有效提高程序的运行效率和可靠性。
44 6
|
13天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
32 5
|
13天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
17天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
17天前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
21天前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
43 4
|
2月前
|
存储 编译器 C语言
【c语言】数组
本文介绍了数组的基本概念及一维和二维数组的创建、初始化、使用方法及其在内存中的存储形式。一维数组通过下标访问元素,支持初始化和动态输入输出。二维数组则通过行和列的下标访问元素,同样支持初始化和动态输入输出。此外,还简要介绍了C99标准中的变长数组,允许在运行时根据变量创建数组,但不能初始化。
42 6
|
2月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
2月前
|
存储 C语言
C语言:一维数组的不初始化、部分初始化、完全初始化的不同点
C语言中一维数组的初始化有三种情况:不初始化时,数组元素的值是随机的;部分初始化时,未指定的元素会被自动赋值为0;完全初始化时,所有元素都被赋予了初始值。
|
2月前
|
C语言
C语言数组
C语言数组
21 0