最最最详细的C语言教程笔记零起步(9)进阶必备 同笔者一起学习

本文主要是介绍最最最详细的C语言教程笔记零起步(9)进阶必备 同笔者一起学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C语言教程笔记

  • 十九. 初识数组
    • 1. 什么是数组?
    • 2. 如何声明数组
    • 3. 数组内容的初始化
      • 3.1 初始化与赋值的区别
      • 3.2 数组的初始化
      • 3.3 初始化列表
        • 3.3.1 初始化列表短于数组长度
        • 3.3.2 初始化列表长于数组长度
        • 3.3.3 让初始化列表决定数组长度
    • 4. 访问数组的元素
      • 4.1 访问数组元素的公式
      • 4.2 使用循环遍历数组元素
      • 4.3 修改数组元素的值
      • 4.4 小心数组越界
      • 4.5 如果不初始化会怎样
    • 5. 数组所占用的空间大小
    • 6. 数组能整体赋值吗
      • 6.1 使用循环,单个为元素赋值
      • 6.2 内存复制
  • 二十. 多维数组
    • 1. 数组作为数组的元素
    • 2. 二维数组
      • 2.1 二维数组初始化
      • 2.2 访问或修改二维数组元素
        • 2.2.1 访问二维数组元素
        • 2.2.2 修改二维数组元素
    • 3. 更高维度的数组
  • 二十一. 字符串与字符数组
    • 1. 字符串常量
    • 2. 用字符数组存储字符串
      • 2.1 初始化字符数组
      • 2.2 省略数组大小
      • 2.3 打印字符数组
    • 3. 字符串结尾标记'\0'
      • 3.1 初始化列表长度小于数组长度
      • 3.2 初始化列表长度等于数组长度
      • 3.3 初始化列表长度大于数组长度
      • 3.4 省略数组大小的情况
    • 4. 字符数组的大小及长度
      • 4.1 使用循环测量字符串长度
      • 4.2 使用strlen测量字符串长度
      • 4.3 strlen与sizeof的区别
    • 5. 字符数组可以被修改
    • 6. 从键盘输入一串字符串到字符数组
    • 7. putchar与getchar函数

十九. 初识数组

1. 什么是数组?

数组由一系列类型相同的数据对象依次排列组成。 组成数组的数据对象被称作数组的元素。

数组的两个重要注意点:

  1. 依次排列:每个数组元素都是相邻的,从第一个数组元素到最后一个数组元素依次摆放。
  2. 类型相同:数组的元素都是类型相同的数据对象,不同的数据对象不能组成数组。

2. 如何声明数组

我们可以用如下的代码,声明各种类型的单个变量。

char c; 
int n; 
long l; 
float f; 
double df;

既然数组是由一系列类型相同的数据对象依次排列组成。那么声明数组至少要提供三类参数。

  1. 数组名。
  2. 数组元素的类型。
  3. 数组元素的数量。

数组声明的公式:

在这里插入图片描述数组的声明由数组名、元素类型、元素数量组成,例如:

char c[5]; 
int n[10]; 
long l[3]; 
float f[2]; 
double df[1];

3. 数组内容的初始化

3.1 初始化与赋值的区别

在变量声明的时候,我们讨论过初始化和赋值的区别。

int n = 100;    //  初始化为100 
n = 100;        //  赋值为100

第一行代码为声明int类型的变量n,并初始化为100。第二行代码为n赋值为100。

初始化与赋值的区别在于:
初始化时,等号的左边为变量的声明,等号的右边为值。
赋值时,等号的左边为变量,等号的右边为值。

初始化中的等号并不是赋值运算符,只是写作等号而已。

不能对一个变量多次初始化,会被认为变量被重复定义。

int n =100; 
int n = 123;    //  错误,会引发变量重定义

对一个变量重复赋值却没有问题。

int n; 
n = 123; 
n = 456;

对于基础类型,似乎赋值与初始化都是将一个值装入变量,没有特别明显的差异。而对于我们接下来要讨论的数组,却有一些差异。

3.2 数组的初始化

在这里插入图片描述
数组初始化如同基础数据类型初始化一样,也是在变量声明时加上等号,在等号右边写需要为数组初始化的值。
例如:

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

上面的代码能声明一个由 10 个 int 数据对象组成的数组,并且将它们分别初始化为1,2,3,4,5, 6,7,8,9,0。

3.3 初始化列表

在这里插入图片描述
数组初始化时,等号右边被称作初始化列表。 初始化列表写明数组的元素分别需要被初始化为何值,用逗号隔开,最后用花括号包括起来。

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

上面这样将10个元素分别初始化为1,2,3,4,5,6,7,8,9,0。

3.3.1 初始化列表短于数组长度
int arr[10] = {1, 2 ,3 ,4 ,5};

现在数组有10个元素,而初始化列表只指定了5个元素的值。那么剩下的元素将被填0。

int arr[10] = {};

初始化列表什么都没有,所有数组元素将被填0。

3.3.2 初始化列表长于数组长度
int arr[10] = {1, 2 ,3 ,4 ,5 ,6, 7, 8, 9, 10, 11};

初始化列表长于数组长度,剩下的11将不知道初始化给哪个元素,代码将无法编译通过。

3.3.3 让初始化列表决定数组长度
int arr[] = {1726, 838, 938, 138, 58, 82, 83, 343, 456, 534, 645, 8938, 9382, 83, 343};

如果你有多个数值需要初始化给数组,但是你懒得数清楚到底有多少个。可以在数组声明的方括号里什么都不填,这样将会让初始化列表决定数组的长度。

4. 访问数组的元素

4.1 访问数组元素的公式

数组被初始化好了,现在我们想访问这个数组的各个元素了。
访问数组指定元素的公式如下:
在这里插入图片描述

使用 数组名[下标] 的形式可以访问数组内的元素。
请特别注意,下标从0开始。
0为下标,访问到的是数组的第一个元素。目前请先把下标从0开始看做一个规则。

#include <stdio.h>int main()
{int arr[10] = { 1, 2 ,3 ,4 ,5 ,6, 7, 8, 9, 0 };printf("%d\n", arr[0]); //  第1个元素 printf("%d\n", arr[1]); //  第2个元素 printf("%d\n", arr[2]); //  第3个元素 printf("%d\n", arr[3]); //  第4个元素 printf("%d\n", arr[4]); //  第5个元素 printf("%d\n", arr[5]); //  第6个元素 printf("%d\n", arr[6]); //  第7个元素 printf("%d\n", arr[7]); //  第8个元素 printf("%d\n", arr[8]); //  第9个元素 printf("%d\n", arr[9]); //  第10个元素return 0;
}

在这里插入图片描述

4.2 使用循环遍历数组元素

由于数组下标是递增的,使用 for 循环将更加便利地访问每一个元素。

for(int i = 0; i < 10; i++) 
{ printf("%d\n", arr[i]); //  访问下标为i的元素 
}

在for循环中, i 将从0递增到9。使用 arr[i] ,即可访问下标从0到9的元素。

当然,while循环也同样能做到,不过for循环代码将更加紧凑一些

int i = 0; 
while(i < 10) 
{ printf("%d\n", arr[i++]); //  访问下标为i的元素 
}

这里特别将 i++ 放入了函数调用当中。表达式 i++ 的结果为 i 当前的值,所以第一次求 i++ 的结果为 0。而后缀自增表达式会在稍后为 i 加1。这样写也能达到访问下标0到9的元素的效果。

如果你想, i++ 写在单独的一行也没问题的。

int i = 0; 
while(i < 10) 
{  printf("%d\n", arr[i]); //  访问下标为i的元素     i++; 
}

4.3 修改数组元素的值

怎样修改数组元素呢?

int arr[10] = {};       //  所有元素均初始化为0 
printf("%d\n", arr[5]); //  打印第6个元素的值 
arr[5] = 123;           //  将第6个元素赋值为123 
printf("%d\n", arr[5]); //  打印第6个元素的值

在这里插入图片描述使用赋值表达式:
数组名[下标] = 表达式;
可以为数组元素赋值为一个新值。

4.4 小心数组越界

如果数组只有10个元素,我们访问或修改了这10个元素以外的元素,那么结果是未定义的。这意味着程序看上去可以运行,但是运行的结果很怪,或异常终止

警告:千万不要越界访问或修改数组元素。

C语言编译器不会检查数组是否越界了,所以在编译时无法发现这样的问题。

int arr[10] = {}; //  所有元素均初始化为0 
printf("%d\n", arr[10]);    //  下标为0到9,访问下标10的元素属于越界访问。

如果使用循环来遍历数组,请特别注意循环条件。

for(int i = 0; i <= 10; i++) 
{     printf("%d\n", arr[i]); //  访问下标为i的元素 
}

循环条件为 i <= 10 ,这意味着 i 可以为10。这样也将导致数组越界访问。

4.5 如果不初始化会怎样

#include <stdio.h> int main() {  int arr[10];     for(int i = 0; i < 10; i++)     {    printf("%d\n", arr[i]);     }     return 0; }

程序打印的结果均是一些奇怪的值,未初始化的数组里面都是一些无意义的数值。

数组不一定要初始化,就像不初始化变量一样。如果不初始化变量,我们往往会在其后为其赋值。数组也是一样的,其后为其元素赋值,避免使用到无意义的数值。

#include <stdio.h>int main() {  int arr[10];     int n = 0;     for(int i = 0; i < 10; i++)     {  arr[i] = n;         n = n + 2;     }for(int i = 0; i < 10; i++)     {    printf("%d ", arr[i]);     }     return 0; }

在这里插入图片描述

5. 数组所占用的空间大小

char arr1[10]; 
short arr2[10]; 
int arr3[10]; 
long long arr4[10]; 
float arr5[10]; 
double arr6[10]; 
printf("%d\n", sizeof(arr1)); 
printf("%d\n", sizeof(arr2)); 
printf("%d\n", sizeof(arr3)); 
printf("%d\n", sizeof(arr4)); 
printf("%d\n", sizeof(arr5)); 
printf("%d\n", sizeof(arr6));

在这里插入图片描述

数组所占的空间 = 单个元素所占空间大小 * 数组元素个数

sizeof(arr1) = sizeof(char) * 10 = 10 
sizeof(arr2) = sizeof(short) * 10 = 20 
sizeof(arr3) = sizeof(int) * 10 = 40 
sizeof(arr4) = sizeof(long long) * 10 = 80 
sizeof(arr5) = sizeof(float) * 10 = 40 
sizeof(arr6) = sizeof(double) * 10 = 80

6. 数组能整体赋值吗

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

第一个数组元素全部初始化为0,第二数组元素初始化为1,2,3,4,5。 我们可以使用赋值运算符让arr1赋值为arr2吗?
答案是不行的,这种写法无法通过编译。
那么重新初始化呢?
arr1 = {1, 2, 3, 4, 5};
这样也是不行的,初始化列表只能存在于初始化中。
这里也是赋值与初始化区别的一个体现,在数组初始化中可以使用初始化列表,而赋值不行。

6.1 使用循环,单个为元素赋值

虽然无法整体为数组赋值,但是使用循环,单个为元素赋值确是可以的。

int arr1[5] = {}; 
int arr2[5] = {1, 2, 3, 4, 5}; 
for (int i = 0; i < 5; i++) 
{ arr1[i] = arr2[i]; }

6.2 内存复制

#include <stdio.h> 
#include <memory.h> 
int main() 
{  int arr1[5] = {};     int arr2[5] = {1, 2, 3, 4, 5};     memcpy(arr1, arr2, sizeof(arr1));     for (int i = 0; i < 5; i++)         printf("%d ", arr1[i]);     return 0; 
}

另外,我们还可以借助 memcpy函数 (memory copy的简写),将arr2数组的数据复制到arr1数组。
memcpy的第一个参数是目标数组名。
memcpy的第二个参数是原始数组名。
memcpy的第三个参数指明需要复制多少字节数据。
memcpy函数将会把arr2数组的数据复制到arr1数组,复制多少字节数据取决于第三个参数。

注意事项:设第三个参数,需要复制的字节数为N。

  1. sizeof(arr2)不能小于N,否则复制完arr2的数据后,将复制到无意义内容。
  2. sizeof(arr1)不能小于N,否则将没有足够空间存放数据。
  3. 要使用memcpy函数,需要包含头文件 memory.h 。
#include <stdio.h> 
#include <memory.h> 
int main() 
{  int arr1[3] = {};     int arr2[5] = {1, 2, 3, 4, 5};     memcpy(arr1, arr2, sizeof(int) * 2);     for (int i = 0; i < 3; i++)         printf("%d ", arr1[i]);     return 0; 
}

在这里插入图片描述

上面的代码没有问题,将仅复制arr2中的sizeof(int) * 2字节数据到arr1。 也就是说,仅会将arr2中前2个int元素的数据复制到arr1。 而arr1可以容纳3个int,有足够的空间存放数据。

二十. 多维数组

1. 数组作为数组的元素

有一个数组A,它的元素为 int 类型,元素数量为10个。 另外有一个数组B,它的元素类型为包含10个int元素的数组,元素数量为5个。

在这里插入图片描述
数组A已经在上一节中充分讨论过了, int A[10] 可以声明一个这种类型的数组。 但是,怎样声明数组B这种类型的数组呢?

让我们再回顾一下数组声明的公式:
在这里插入图片描述数组的声明由数组名、元素类型、元素数量组成。 下面我们来尝试写一个数组B的声明。

  1. 数组名:B
  2. 元素类型:int[10]
  3. 元素数量: 5 组合这3个要素,写出如下声明
int[10] B[5];

数组名左边的方括号都移到最右边来。

int B[5][10];

这样就是B数组的声明了。B数组含有5个元素,而每个元素都是含有10个int元素的数组。

2. 二维数组

访问数组指定元素的公式如下:
在这里插入图片描述
使用 数组名[下标] 的形式可以访问数组内的元素。

例如: B[0] ,而数组B的每一个元素都是一个 int[10] 类型的数组。
在这里插入图片描述
再进一步访问,访问数组元素中的int元素。 例如: B[0][0] ,访问了第一个元素中的第一个元素。

在这里插入图片描述
可以发现,数组构成了一个二维的矩阵。并且,可以通过下标轻松地访问到它的每一个元素。

2.1 二维数组初始化

我们这样初始化一个一维数组

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

而对于一个二维数组,它的每一个元素都是一个数组。

int B[5][10] = {  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},     {10, 11, 12, 13, 14, 15, 16, 17, 18, 19},     {20, 21, 22, 23, 24, 25, 26, 27, 28, 29},     {30, 31, 32, 33, 34, 35, 36, 37, 38, 39},     {40, 41, 42, 43, 44, 45, 46, 47, 48, 49} }

类似于一维数组,初始化列表中的常量个数少于元素个数,将使用0初始化元素。

int B[5][10] = {  {0, 1, 2, 3, 4, 5},     {10, 11, 12, 13, 14, 15},     {20, 21, 22},     {30},     {} }

另外,也可以省略里面的花括号。

int B[5][10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,     10, 11, 12, 13, 14, 15, 16, 17, 18, 19,     20, 21, 22, 23, 24, 25, 26, 27, 28, 29,    30, 31, 32, 33, 34, 35, 36, 37, 38, 39,     40, 41, 42, 43, 44, 45, 46, 47, 48, 49 }

省略内层花括号时,如果元素的个数不足。那么后续的将元素将使用0初始化。

int B[5][10] = {  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,     10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }

对于一维数组代码:

#include <stdio.h>int main()
{
int B[5][10] = {{0},{1},{2},{3},{4}};
for(int i = 0; i<5; i++){for(int j = 0; j<10;j++){printf( "%d ",B[i][j]);}printf( "\n");}
return 0;
}

在这里插入图片描述

2.2 访问或修改二维数组元素

2.2.1 访问二维数组元素

对于二维数组代码:

因为有两个下标,所以使用嵌套循环来遍历二维数组

#include <stdio.h>
int main()
{
int B[5][10] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},{10, 11, 12, 13, 14, 15, 16, 17, 18, 19},{20, 21, 22, 23, 24, 25, 26, 27, 28, 29},{30, 31, 32, 33, 34, 35, 36, 37, 38, 39},{40, 41, 42, 43, 44, 45, 46, 47, 48, 49}
};
for (int i = 0; i < 5; i++)           //遍历二维数组 查看所有元素
{for (int j = 0; j < 10; j++){printf("%d ", B[i][j]);}printf("\n");
}
return 0;
}

在这里插入图片描述

2.2.2 修改二维数组元素

可以使用赋值运算符修改二维数组元素

B[i][j] = B[i][j] * 2;  //  修改二维数组值

下面的代码将二维数组每个元素都设置为原来的2倍。

#include <stdio.h> 
int main() 
{ int B[5][10] = {     {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},     {10, 11, 12, 13, 14, 15, 16, 17, 18, 19},     {20, 21, 22, 23, 24, 25, 26, 27, 28, 29},     {30, 31, 32, 33, 34, 35, 36, 37, 38, 39},     {40, 41, 42, 43, 44, 45, 46, 47, 48, 49}   };for(int i = 0; i < 5; i++)   { for(int j = 0; j < 10; j++)     {   B[i][j] = B[i][j] * 2;  //  修改二维数组值     }   }for(int i = 0; i < 5; i++)   {for(int j = 0; j < 10; j++)     {   printf("%d ", B[i][j]);     }     printf("\n");   }   
return 0; 
}

在这里插入图片描述

3. 更高维度的数组

如果将二维数组作为数组的元素,那么可以实现三位数组。依次类推,可以实现更高维度的数组。

int C[2][5][10];
数组C,有两个元素,每个元素是一个int[5][10]类型的二维数组。 对于三维数组,我们会使用三重循环来访问或修改元素值。

#include <stdio.h> 
int main() 
{ int B[2][5][10] = {     {       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},       {10, 11, 12, 13, 14, 15, 16, 17, 18, 19},       {20, 21, 22, 23, 24, 25, 26, 27, 28, 29},       {30, 31, 32, 33, 34, 35, 36, 37, 38, 39},       {40, 41, 42, 43, 44, 45, 46, 47, 48, 49}     },     {    {-0, -1, -2, 3, -4, -5, -6, -7, -8, -9},       {-10, -11, -12, -13, -14, -15, -16, -17, -18, -19},       {-20, -21, -22, -23, -24, -25, -26, -27, -28, -29},       {-30, -31, -32, -33, -34, -35, -36, -37, -38, -39},       {-40, -41, -42, -43, -44, -45, -46, -47, -48, -49}     }   };
for(int i = 0; i < 2; i++)   
{ for(int j = 0; j < 5; j++)     {   for(int k = 0; k < 10; k++)       {     printf("%d ", B[i][j][k]);       }  printf("\n");     }  printf("\n");   }   
return 0; 
}

在这里插入图片描述

二十一. 字符串与字符数组

在探究字符数组之前,来复习一下字符串相关的知识点吧。

1. 字符串常量

#include <stdio.h>int main() {  printf("sizeof HelloWorld = %d\n", sizeof("HelloWorld"));     return 0; }

字符串常量 “HelloWorld” 占用11个字节。

字符串常量在内存中,由每个字符的ASCII码按照顺序排列构成,每个字符仅占一个字节,并且末尾会附上一个数值0,指示字符串结尾。

字符’0’对应的ASCII码为十进制48。而标记字符串结尾的数值0,为了不与字符’0’冲突,使用转义序列’\0’表示。

在这里插入图片描述字符常量

2. 用字符数组存储字符串

既然字符串满足数组的类型相同且顺序排列的特点。那么元素为 char 的数组可以用于存储字符串。

2.1 初始化字符数组

我们声明一个数组,且把它初始化为"HelloWorld"

char str[20] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};

由于数组有20个元素,而初始化列表中仅有10个元素。数组大小大于初始化列表长度,数组后10个将会被初始化为0。也就相当于已经帮我们标记了字符串结尾了。

还有一种更加便捷的字符数组初始化方式。将初始化列表直接写成一个字符串常量,可以用于初始化字符数组。

char str[20] = "HelloWorld";

由于字符串常量末尾会自动添加’\0’为字符串结尾标识,所以当用字符串常量初始化字符数组时,代码等价于:

char str[20] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0'};

2.2 省略数组大小

有时候我们希望一个数组被初始化为某个字符串,但是又不想数清楚到底有多少个字符。我们可以在数组声明时,省略数组大小,初始化列表中的元素个数即为数组的大小。

char str1[] = "HelloWorld"; 
char str2[] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0'};

上面两种写法是等价的,数组的大小为初始化列表中元素的个数,即11。

2.3 打印字符数组

接下来,我们想让这串字符串被打印在控制台上。 printf的第一个参数可以接收一串字符串。所以我们可以直接把数组作为printf的第一个参数。

printf("HelloWorld"); //使用数组 printf(str);

另外,转换规范 %s ,可以作为字符串的占位符。

printf("%s", str);

将字符数组打印在控制台:

#include <stdio.h>int main() { char str[20] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};     printf(str);     printf("\n");     printf("%s", str);     printf("\n");     return 0; 
}

在这里插入图片描述

3. 字符串结尾标记’\0’

在使用字符串常量时,系统会自动为我们在字符串末尾添加 ‘\0’ 标记字符串结束。 而在使用字符数组时,有些情况不能保证字符串末尾有 ‘\0’,需要格外注意。

3.1 初始化列表长度小于数组长度

char str[20] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};

初始化列表长度小于数组长度,数组前10个元素被初始化为HelloWorld,后10个元素和普通数组一样后,被填充为0。
既然第11个元素为0,因此字符数组中的字符串正常结尾。

3.2 初始化列表长度等于数组长度

char str[10] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};

初始化列表长度小于数组长度,数组前10个元素被初始化为HelloWorld。第11个元素在数组外,也不能预知这个元素的值。字符数组中的字符串无法结尾。
在这里插入图片描述
程序运行后,正常打印出了数组中的10个元素HelloWorld。但是,由于数组中的字符串缺少结尾标 记 ‘\0’。因此,printf将继续打印数组外的元素,这样将导致数组越界访问。直到遇到一个 ‘\0’ ,才停止打印。

3.3 初始化列表长度大于数组长度

char str[5] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};

初始化列表长度大于数组长度将无法通过编译。
在这里插入图片描述

3.4 省略数组大小的情况

char str[] = "HelloWorld";

使用字符串常量初始化数组,由于系统会为字符串常量结尾添加’\0’。所以字符串常量大小为11。 使用该字符串初始化数组,数组大小为11,且最后一个字符为’\0’。字符数组中的字符串正常结尾。

char str[] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0'};

使用初始化列表初始化数组,初始化列表中有11个字符常量,且最后一个字符常量为’\0’。 使用该初始化列表初始化数组,数组大小为11,且最后一个字符为’\0’。字符数组中的字符串正常结尾。

char str[] = {'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};

使用初始化列表初始化数组,初始化列表中有10个字符常量,最后一个字符常量为’d’。 使用该初始化列表初始化数组,数组大小为10,且最后一个字符为’d’。字符数组中的字符串无法结尾。

4. 字符数组的大小及长度

char str[20] = "HelloWorld";

我们声明了一个长度为20的字符数组str,并将它使用字符串常量"HelloWorld"进行初始化。 这样,字符数组的前10个元素为HelloWorld,第11个元素为’\0’。

我们使用sizeof关键词,测试一下str的大小。
在这里插入图片描述结果为20个字节,和我们声明的大小一致。

4.1 使用循环测量字符串长度

那么怎样知道这个字符数组中存储的字符串的长度呢。
事实上,一个字符串用 ‘\0’ 来标记结尾,只要知道在 ‘\0’ 之前,有多少个字符,就能知道字符数组中的字符串的长度了。

#include <stdio.h>int main() {  char str[20] = "HelloWorld";     int len = 0;     while(str[len] != '\0')     {    len++;     }     printf("%d", len);     return 0; 
}

在这里插入图片描述
声明一个len变量,用于统计字符串长度。 while循环从第一个元素开始,检查元素是否为 ‘\0’ ,如果不是长度len加一,直到元素为 ‘\0’ 为止。 循环结束时, len 的值即为字符串的长度。

4.2 使用strlen测量字符串长度

另外,还可以使用 strlen 函数来计算字符串长度。 strlen为 string(字符串)与 length(长度)的合成词。 strlen 的使用范式:

  1. strlen可以接受一个字符串作为参数。
  2. strlen返回值为这个字符串的长度。
  3. 使用strlen函数,需包含头文件string.h。
#include <stdio.h> 
#include <string.h>int main() { char str[20] = "HelloWorld";     int len1;     len1 = strlen(str);     printf("len1 = %d\n", len1);int len2;     len2 = strlen("HelloWorld");     printf("len2 = %d\n", len2);printf("sizeof str %d\n", sizeof(str));     printf("sizeof helloworld %d\n", sizeof("HelloWorld"));     return 0; 
}

在这里插入图片描述
len1为strlen函数测量字符数组 str 内字符串的长度。
len2为strlen函数测量字符串常量 “HelloWorld” 的长度。
后续,再用sizeof分别测量字符数组str和字符串常量"HelloWorld"所占空间大小。

HelloWorld有10个字符,所以字符串长度为10,len1、len2均为10。
而字符数组有20个元素,所以占用20字节空间。
字符串常量为字符串长度加结尾标记,所以占用11字节空间。

4.3 strlen与sizeof的区别

strlen(str)测量从第一个元素开始直到元素值为’\0’的字符串长度。 sizeof(str)测量数组本身占用空间的大小。

5. 字符数组可以被修改

字符串常量不能被修改,但是字符数组却是可以被修改的。 下面的代码中,数组被初始化为"abcde",我们让小写字符变为大写。小写字母ASCII码减32即为对应的大写字母。

#include <stdio.h>
#include <string.h> 
int main() 
{ char str[20] = "abcde";//  修改前     printf(str);     printf("\n");//  每个元素减32     for(int i = 0; i < strlen(str); i++)     {    str[i] = str[i] - 32;     }//  修改后     printf(str);     return 0; 
}

在这里插入图片描述

6. 从键盘输入一串字符串到字符数组

对应于printf,scanf中字符串的占位符为"%s"。使用scanf配合"%s"占位符,可以将键盘输入的一串字符串存储到字符数组。

char str[20]; scanf("%s", str);   //  将键盘输入的一串字符串存储到字符数组str

下面的代码使用scanf将一串字符串存储到字符数组,其后将小写字母转为大写字母再输出。

#include <stdio.h> 
#include <string.h>int main() {     char str[20];//  输入一串字符到str     scanf("%s", str);//  修改前     printf(str);     printf("\n");//  每个元素减32     for(int i = 0; i < strlen(str); i++)     {     str[i] = str[i] - 32;     }//  修改后     printf(str);    return 0; }

scanf会帮我们在输入的字符串后加上’\0’,输入的字符串可以正常结尾。
在这里插入图片描述

7. putchar与getchar函数

putchar 与 getchar 函数可以单独输出或输入一个字符。 putchar 输出单个字符:

putchar('A');

getchar输入单个字符:

char c; c = getchar();

我们使用getchar与putchar,输入一个小写字符,将它转换为大写再输出。 小写字母ASCII码减32即为对应的大写字母。

#include <stdio.h>int main() {  char c;     c = getchar();     c = c - 32;     putchar(c);     return 0; 
}

使用getchar函数输入小写字符’a’,将它转换为大写后,使用putchar函数输出。

在这里插入图片描述

结合上循环,使用putchar输出一串字符串。

#include <stdio.h>int main() {  char str[] = "HelloWorld";     int i = 0;    while(str[i] != '\0')     {   putchar(str[i++]);     }    return 0; 
}

在这里插入图片描述
点个赞吧 😃

这篇关于最最最详细的C语言教程笔记零起步(9)进阶必备 同笔者一起学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/392127

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss