C04-array-string

本文最后更新于:1 小时前

C数组

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

  2. 数组长度 length 最好是整数或者常量表达式,例如 10、20*4 等,这样在所有编译器下都能运行通过

  3. 访问数组元素时,下标的取值范围为 0 ≤ index < length,过大或过小都会越界,导致数组溢出,发生不可预测的情况

数组内存是连续的。连续的内存为指针操作(通过指针来访问数组元素)和内存处理(整块内存的复制、写入等)提供了便利,这使得数组可以作为缓存(临时存储数据的一块内存)使用。

在我们没有明确数组的元素个数时,在程序中想知道数组单元个数可以使用 sizeof(a)/sizeof(a[0]), sizeof(a) 是得到数组 a 的大小,sizeof(a[0]) 是得到数组 a 中单个元素的大小(因此可以不必要是a[0],a[i]都行),例:

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>

int main(int argc,char *grgv[])
{
int a[]={1,2,3,4,5};
int b;
b=sizeof(a)/sizeof(a[0]);
printf("数组元素个数为:%d",b);
return 0;
}

数组的初始化

可以先定义数组再给数组赋值,也可以在定义数组的同时赋值,例如:
int a[4] = {20, 345, 700, 22};
数组元素的值由’{ }’包围,各个值之间以’,’分隔。

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

  1. 可以只给部分元素赋值。当{ }中值的个数少于元素个数时,只给前面部分元素赋值。例如:
    int a[10]={12, 19, 22 , 993, 344};
    表示只给 a[0]~a[4] 5个元素赋值,而后面 5 个元素自动初始化为 0。

当赋值的元素少于数组总体元素的时候,剩余的元素自动初始化为 0:
对于short、int、long,就是整数 0;
对于char,就是字符 ‘\0’;
对于float、double,就是小数 0.0。

我们可以通过下面的形式将数组的所有元素初始化为 0:
int nums[10] = {0};
char str[10] = {0};
float scores[10] = {0.0};
由于剩余的元素会自动初始化为 0,所以只需要给第 0 个元素赋值为 0 即可。

  1. 只能给元素逐个赋值,不能给数组整体赋值。例如给 10 个元素全部赋值为 1,只能写作:
    int a[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
    而不能写作:
    int a[10] = 1;

  2. 如给全部元素赋值,那么在定义数组时可以不给出数组长度。例如:
    int a[] = {1, 2, 3, 4, 5};
    等价于
    int a[5] = {1, 2, 3, 4, 5};

  3. 如果省略掉数组的大小,数组的大小则为初始化时元素的个数。例如:
    int a[] = {1, 2, 3};得到一个有三个元素的数组

二维数组

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

  1. 可以只对部分元素赋值,未赋值的元素自动取“零”值。例如:
    int a[3][3] = {{1}, {2}, {3}};
    是对每一行的第一列元素赋值,未赋值的元素的值为 0。赋值后各元素的值为:
    1 0 0
    2 0 0
    3 0 0

再如:
int a[3][3] = {{0,1}, {0,0,2}, {3}};
赋值后各元素的值为:
0 1 0
0 0 2
3 0 0

  1. 如果对全部元素赋值,那么第一维的长度可以不给出。例如:
    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};

  2. 二维数组可以看作是由一维数组嵌套而成的;如果一个数组的每个元素又是一个数组,那么它就是二维数组。当然,前提是各个元素的类型必须相同。根据这样的分析,一个二维数组也可以分解为多个一维数组,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]。

二维数组在逻辑上是方阵,由行和列组成。

但是二维数组在物理上是线性的,按行来依次进行存放,内存是连续的。

二维数组名即首行的地址,C 语言中的地址一般均是空间首地址。故二维数组名是首行首地址,该数组名加 1 表示跳过一整行,到达第二行的首地址,以此类推。

二维数组名加 1 表示跳过一个对象(一行)的空间,为下一个对象(下一行)的地址。即跳过一个对象所有属性(一行中所有列元素)对应的空间,到达下一个对象(下一行)的起始位置。

传递数组给函数

有三种方式可以向函数传递数组

方式 1
形式参数是一个指针(您可以在下一章中学习到有关指针的知识):

void myFunction(int *param){}

方式 2
形式参数是一个已定义大小的数组:

void myFunction(int param[10]){...}

方式 3
形式参数是一个未定义大小的数组:

void myFunction(int param[]){...}

二维数组传递给函数

方法1: 第一维的长度可以不指定,但必须指定第二维的长度:

void print_a(int a[][5], int n, int m)
方法2: 指向一个有5个元素一维数组的指针:

void print_b(int (*a)[5], int n, int m)
方法3: 利用数组是顺序存储的特性,通过降维来访问原数组!

void print_c(int *a, int n, int m)

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
50
51
52
53
54
#include <stdio.h>
/*********************************
* 方法1: 第一维的长度可以不指定
* 但必须指定第二维的长度
*********************************/
void print_a(int a[][5], int n, int m){
int i, j;
for(i = 0; i < n; i++) {
for(j = 0; j < m; j++)
printf("%d ", a[i][j]);
printf("\n");
}
}

/*****************************************
* 方法2: 指向一个有5个元素一维数组的指针
*****************************************/
void print_b(int (*a)[5], int n, int m) {
int i, j;
for(i = 0; i < n; i++) {
for(j = 0; j < m; j++)
printf("%d ", a[i][j]);
printf("\n");
}
}

/***********************************
* 方法3: 利用数组是顺序存储的特性,
* 通过降维来访问原数组!
***********************************/
void print_c(int *a, int n, int m) {
int i, j;
for(i = 0; i < n; i++) {
for(j = 0; j < m; j++)
printf("%d ", *(a + i*m + j));
printf("\n");
}
}
int main(void)
{
int a[5][5] = {{1, 2}, {3, 4, 5}, {6}, {7}, {0, 8}};

printf("\n方法1:\n");
print_a(a, 5, 5);

printf("\n方法2:\n");
print_b(a, 5, 5);

printf("\n方法3:\n");
print_c(&a[0][0], 5, 5);

// getch();
return 0;
}

从函数返回数组

C 语言不允许返回一个完整的数组作为函数的参数。但是可以通过指定不带索引的数组名来返回一个指向数组的指针。
如果想要从函数返回一个一维数组,必须先声明一个返回指针的函数,如下:

int * myFunction(){...}

另外,C 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。

下面的函数,它会生成 10 个随机数,并使用数组来返回它们

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
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* 要生成和返回随机数的函数 */
int * getRandom( )
{
static int r[10];
int i;

/* 设置种子 */
srand( (unsigned)time( NULL ) );
for ( i = 0; i < 10; ++i)
{
r[i] = rand();
printf( "r[%d] = %d\n", i, r[i]);

}

return r;
}

/* 要调用上面定义函数的主函数 */
int main ()
{
/* 一个指向整数的指针 */
int *p;
int i;

p = getRandom();
for ( i = 0; i < 10; i++ )
{
printf( "*(p + %d) : %d\n", i, *(p + i));
}

return 0;
}

srand((unsigned)time(NULL))是初始化随机函数种子:

1、是拿当前系统时间作为种子,由于时间是变化的,种子变化,可以产生不相同的随机数。计算机中的随机数实际上都不是真正的随机数,如果两次给的种子一样,是会生成同样的随机序列的。 所以,一般都会以当前的时间作为种子来生成随机数,这样更加的随机。
2、使用时,参数可以是unsigned型的任意数据,比如srand(10);
3、如果不使用srand,用rand()产生的随机数,在多次运行,结果是一样的。

C字符串

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

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

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

char str[10] = {"Hello"};
char str[10] = "Hello";

为了方便,也可以不指定数组长度:

char str[] = {"Hello"};
char str[] = "Hello";

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

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

用” “包围的字符串会自动在末尾添加’\0’,但逐个字符地给数组赋值并不会自动添加’\0’。

如果没有在字符数组最后增加 \0 的话输出结果有误:

1
2
3
// 初始化字符串
char str[5] = { 'H', 'e', 'l', 'l', 'o' };
printf("Greeting message: %s\n", str);

输出结果:

Greeting message: Hello烫烫烫?侵7(?╔?╚╔╔

可以手动在初始化字符串之后加上’\0’,也可以这样做:

char str[30] = {0}; //将所有元素都初始化为 0,或者说 '\0',然后再给字符串数组赋值。

字符串长度

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

sizeof 计算的是变量的大小;strlen 返回的是该字符串的长度,遇到 \0 结束,\0 本身不计算在内;前者不受字符 \0 影响,后者以 \0 作为长度判定依据。

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

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

下面是一个输出str字符串长度的例子

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <string.h> //记得引入该头文件
int main(){
char str[] = "hello world";
long len = strlen(str);
printf("The lenth of the string is %ld.\n", len);

return 0;
}

字符串的输入和输出

字符串的输出

在C语言中,有两个函数可以在控制台上输出字符串,它们分别是:
puts():输出字符串并自动换行,该函数只能输出字符串。
printf():通过格式控制符%s输出字符串,不能自动换行。除了字符串,printf() 还能输出其他类型的数据。

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main(){
char str[] = "hello world";
printf("%s\n", str); //通过字符串名字输出
printf("%s\n", "hello world"); //直接输出
puts(str); //通过字符串名字输出
puts("hello world"); //直接输出
return 0;
}

字符串的输入

在C语言中,有两个函数可以让用户从键盘上输入字符串,它们分别是:
scanf():通过格式控制符%s输入字符串。除了字符串,scanf() 还能输入其他类型的数据。
gets():直接输入字符串,并且只能输入字符串。

但是,scanf() 和 gets() 是有区别的:
scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对 gets() 来说就是一个完整的字符串。换句话说,gets() 用来读取一整行字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main(){
char str1[30] = {0};
char str2[30] = {0};
char str3[30] = {0};
//gets() 用法
printf("Input a string: ");
gets(str1);
//scanf() 用法
printf("Input a string: ");
scanf("%s", str2);
scanf("%s", str3);

printf("\nstr1: %s\n", str1);
printf("str2: %s\n", str2);
printf("str3: %s\n", str3);
return 0;
}

常用的操作字符串函数

序号 函数 & 目的
1 strcpy(s1, s2);
复制字符串 s2 到字符串 s1,strcpy() 会把 s2 中的字符串拷贝到 s1 中,字符串结束标志’\0’也一同拷贝,将 s2 复制到 s1 后,s1 中原来的内容就被覆盖了。另外,strcpy() 要求 s1 要有足够的长度,否则不能全部装入所拷贝的字符串。
2 strcat(s1, s2);
连接字符串 s2 到字符串 s1 的末尾,strcat() 将把 s2 连接到 s1 后面,并删除原来 s1 最后的结束标志’\0’。所以 s1 必须足够长,要能够同时容纳 s1 和 s2,否则会越界(超出范围)。
strcat() 的返回值为 s1 的地址。
3 strlen(s1);
返回字符串 s1 的长度。
4 strcmp(s1, s2);
返回值:如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。
5 strchr(s1, ch);
返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
6 strstr(s1, s2);
返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!