L1D5 C语言数组和字符串

数组和字符串

一、一维数组的定义、引用及初始化

1.1 数组的定义

数组是具有一定顺序关系的若干个变量的集合,组成数组的各个变量称为数组的元素 数组中各元素的数据类型要求相同,用数组名和下标确定。数组可以是一维的,也可以是多维的。

1.2 一维数组的定义

一维数组是指只有一个下标的数组。它在计算机的内存中是连续存储的
说明形式为:<存储类型> <数据类型 > <数组名>[<表达式>]

例如:int a[6]; 数组名a表示内存首地址,sizeof(a)是数组占用的总内存空间,
C语言数组编译时,不会对数组做越界检查,自己要注意。
a[6]表示a有6个元素,分别为a[0] a[1] a[2] a[3] a[4] a[5]

1.3 一维数组的引用

C语言中规定数组必须逐个元素引用,而不能整体引用,比如下面这样是错误的,数组的引用实际上是数组元素的引用,数组元素需要用数组名[下标]表示:

int a[10];
printf("%d\n",a);

如下演示是正确的,计算斐波那契数列前10项并逆序输出结果:

#include <stdio.h>
int main(int argc, char const *argv[])
{
    int i = 0 , a[10] = {0};
    a[0] = a[1] = 1;
    for(i = 2 ; i < 10 ; i++)
    {
        a[i] = a[i-2] + a[i-1];
    }
    printf("fibonacci numbers...\n");
    for(i = 9 ; i > 0 ; i--)
        printf("a[%d] is %d\n",i , a[i]);
    return 0;
}

在这里插入图片描述

1.4 一维数组的初始化

我们定义的任何形式的变量尽量都初始化,防止后续遇到问题无法快速定位,因为一个变量如果没有初始化及赋值就直接拿来使用的话,这个值是随机的,因为局部变量来说我们是去栈上开辟了空间,但是栈上原来可能是有数据的,不初始化的话就会直接把栈上的数据拿过来。如下演示所示(没对a[10]初始化):

#include <stdio.h>
int main(int argc, char const *argv[])
{
    int a[10];
    for (int i = 0; i < 10; i++)
    {
        printf("a[%d] is %d\n", i , a[i]);
    }
    return 0;
}

在这里插入图片描述

数组初始化有七种情况:
1、局部变量不初始化:对于局部变量数组,若定义时未初始化,则数组元素值为随机值。
2、static数组不初始化:对于static修饰的数组,在内存上是存在全局与静态区,这里会进行清0处理(清bss段),因此元素都为0
3、全局数组不初始化:对于全局数组,在内存上是存在全局与静态区,这里会进行清0处理(清bss段),因此元素都为0
4、全部初始化:所有元素给一个初值

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

5、部分初始化:数组如果部分元素赋值,其他元素自动赋值0

int a[5] = {1 , 2};    //此时a[0] = 1 a[1] = 2  a[3] = 0 a[4] = 0   

6、数组全部初始化为0(特殊写法)

int a[5] = {0};

二、一维数组的内存分配

在内存中,数组中的元素占用连续空间,并且根据单个元素所占存储空间来进行内存分配。

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int i = 0;
    char a[6] = {0};
    int b[6] = {0};
    for ( i = 0; i < 6; i++)
    {
        printf("a[%d] address is : %p\n", i, &a[i]);
    }
    for ( i = 0; i < 6; i++)
    {
        printf("b[%d] address is : %p\n", i, &b[i]);
    }
    return 0;
}

在这里插入图片描述
从上面可以看出,char型数组元素地址连续,且相差一个字节。int型数组元素地址连续且相差4字节。

三、二维数组的定义、初始化

3.1 二维数组的定义

二维数组是指有两个下标的数组。但是它在计算机的内存中是连续存储的,二维数组在内存上实际是一维的。
说明形式为:<存储类型> <数据类型 > <数组名>[<表达式1>][<表达式2>] 表达式1可省略 2不可省略

int a[2][3]   //2代表行  3代表列
// a[0][0]  a[0][1]   a[0][2]
// a[1][0]  a[1][1]   a[1][2]

3.2 二维数组的初始化

二维数组初始化与一维数组类似,主要是如下几种:
1、降维初始化

int a[2][3]  = {{ 1 , 2 , 3 } , { 4 ,5 , 6 }};    //完全初始化
int a[2][3]  = {{1} , {4}};    //部分初始化   第一行 1 0 0 第二行 4 0 0 

2、按线性存储形式给二维数组赋值初始化

int a[2][3]  = {1 , 2 , 3 , 4 , 5 , 6};
int a[2][3] = { 1 , 2};   //第一行 1 2 0  第二行 0 0 0

3、省略左边(行)下标范围方式初始化

int a[][3] = {1 , 2 , 3 , 4 , 5 , 6};  /第一行1 2 3  第二行 4 5 6 
//不能写int a[2][]这种形式

四、二维数组的内存分配

由于内存是一维的,C语言中,二维数组采取了和一维数组类似的存储方式,按行优先存,存储第一行的元素后,再存第二行的。
在这里插入图片描述
a[0]代表&a[0][0] ,a[0]+2代表&a[0][2]
(注意:在数组a[2][3]中,a代表二维数组名,a[0]代表一维数组名,只要是数组名就是地址常量,不可以直接写a++ a[0]++)
有如下例子,有6元素的二维数组,在内存中的组织形式可用代码验证:

#include <stdio.h>
int main(int argc, char *argv[])
{
	int a[2][3] = {{1,6}};
	int i = 0,j = 0;
	for(i = 0; i < 2; i++)
	{
		for(j = 0; j < 3; j++)
		{
			printf("%p\t",&a[i][j]);
			printf("%d\n",a[i][j]);
			
		}	
	}
	printf("%p %ld %ld\r\n",a,sizeof(a),sizeof(a[1][1]));
	
	return 0;
}

在这里插入图片描述

五、字符数组

5.1 字符数组定义

字符数组是元素的数据类型为字符类型的数组 char c[10],ch[3][4],diamond[][5];

5.2 字符数组初始化

①逐个字符元素赋值初始化

char ch[6] = {'a' , 'b' , 'c' , 'd' , 'e' , '\0'};

②使用字符串常量为数组元素赋值

char ch[6] = {"abcde"};   //要给\0留位置  会自动补\0
char ch[6] = "abcde";
char ch[] = "abcde";
char ch[5] = "abcde";  //会报错,因为字符串隐含'\0',5个位置不够放

六、字符串的定义、输入输出

6.1 字符串定义

字符串指的是以’\0’作为结束字符的一组字符,在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。

6.2 字符串输入输出

使用scanf和printf进行字符串输入输出,此时格式化符号使用’%s’,见下面的代码:

#include <stdio.h>

#define N 20
int main(int argc, char const *argv[])
{
    int i = 0;
    char s[N] = {0};

    printf(">");
    while (scanf("%s", s) != EOF)    //scanf会以空格作为结束标识符
    {
        printf(">");
        printf("i = %d :%s\n", i, s);
        i++;
    }
    return 0;
}

在这里插入图片描述

七、字符串处理函数

C库中,提供了很多字符串处理函数,利用好这些函数,能够让编程事半功倍。

7.1 字符串拷贝函数strcpy

可以在linux终端使用man 3 strcpy查看strcpy的详细用法(所有系统提供的都可以这样查)。
在这里插入图片描述

头文件:string.h
函数原型:char *strcpy(char *dest , const char *src)
功能:字符串拷贝
参数:src:源串起始地址   dest:目标串起始地址
返回值:目标串起始地址

示例:

#include <stdio.h>
#include <string.h>
#define N 50

int main(int argc, char const *argv[])
{
    char dest[N];
    char src[] = "welcome";
    strcpy(dest, src);
    printf("dest: %s src:%s\n", dest,src);
    return 0;
}

在这里插入图片描述
注意:字符串结束字符’\0’也会一起拷贝。

7.2 字符串连接函数strcat

头文件:string.h
函数原型:char *strcat(char *dest , const char *src)
功能:字符串连接,把字符串src连接到dest后面
参数:src:源串起始地址   dest:目标串起始地址
返回值:目标串起始地址

示例:

#include <stdio.h>
#include <string.h>
#define N 50

int main(int argc, char const *argv[])
{
    char dest[N] = "welcome";
    char src[] = "china";
    strcat(dest, src);
    printf("dest: %s src:%s\n", dest,src);
    return 0;
}

在这里插入图片描述
注意:在使用strcat函数时,需要注意目标串应该要有足够的空间。(目标串’\0’被删除然后连接源串)

7.3 字符串比较函数strcmp

头文件:string.h
函数原型:int strcmp(const char *s1 , const char *s2)
功能:对两串从左向右逐个字符比较
参数:s1 s2为两个串的起始地址
返回值:int型整数  若s1 < s2,返回负整数   s1 > s2 ,返回正整数   s1 = s2 , 返回0

示例:

#include <stdio.h>
#include <string.h>
#define N 50

int main(int argc, char const *argv[])
{
    int t = 0;
    char s1[N] = {0};
    char s2[N] = {0};
    printf(">");
    scanf("%s%s",s1,s2);
    t = strcmp(s1,s2);
    if (t == 0) 
        printf("s1 = s2\n");
    else if(t > 0)
        printf("s1 > s2\n");
    else
        printf("s1 < s2\n");
    return 0;
}

在这里插入图片描述

7.4 求字符串长度函数strlen

头文件:string.h
函数原型:size_t strlen(const char *s)
功能:求字符串长度(不含'\0')
参数:s为字符串
返回值:字符串长度(不含'\0'

示例:

#include <stdio.h>
#include <string.h>
#define N 50

int main(int argc, char const *argv[])
{
    char s[N] = "welcome";
    printf("s strlen = %ld\n", strlen(s));           //字符串长度
    printf("s sizeof = %ld\n", sizeof(s)/sizeof(char));  //整个数组大小
    return 0;
}

在这里插入图片描述

7.5 字符串按特定格式分割函数 strtok(常用)

头文件:string.h
函数原型:char *strtok(char *s , const char *delim)
功能:将字符串分割成一个个片断
参数:s为要分解的字符串 delim为分隔符字符串
返回值:分解出的字符串地址

当strtok在参数s的字符串中发现了delim中包含的分隔符时,会将该字符串改为’\0’字符。在第一次调用时,strtok必须给参数s字符串,往后的调用,则将s设置为NULL。每次调用成功则返回指向被分割出片段的指针。
示例程序如下:

#include <string.h>
#include <stdio.h>

int main()
{
    char str[80] = "This is - www.baidu.com - website";
    const char s[2] = "-";
    char *token;

    /* 获取第一个子字符串 */
    token = strtok(str, s);

    /* 继续获取其他的子字符串 */
    while (token != NULL)
    {
        printf("%s\n", token);

        token = strtok(NULL, s);
    }
    printf("\n");
    for (int i = 0; i < 34;i++)
        printf("%c", str[i]);
    printf("\n");
    return 0;
}

在这里插入图片描述

7.6 其他常用字符串函数(可用man手册查看用法,自行测试,学习一个函数最后的方法就是验证它)

char *strncpy(char *dest, const char *src, size_t n);   //复制src串指定长度n到目标dest串
char *strncat(char *dest, const char *src, size_t n);//附加指定长度字符串
int strcasecmp(const char *s1, const char *s2);   //忽略大小写比较字符
int strncmp(const char *s1, const char *s2, size_t n);  //比较两个串的前n个字符
char *strchr(const char *s, int c);    //在字符串s中查找指定字符c
char *strstr(const char *haystack, const char *needle);  //在字符串中查找字符串
int isalpha(int c);				//检查是否为字母字符
int islower(int c);				//检查是否为小写字母字符
int isupper(int c);				//检查是否为大写字母字符
int isdigit(int c);				//检查是否为数字
int toupper(int c);				//转换成大写
int tolower(int c);				//转换成小写

八、总结练习

1、从终端输入10个数字(乱序),分别利用冒泡排序和简单选择排序法对这10个数字排序,结果从小到大排列。(温馨提示:冒泡排序和简单选择排序是两个不同的原理。)

答:
①冒泡排序:
思路:冒泡排序概念:它重复地走访过要排序的数列,一次比较两个元素,如果顺序错误就交换。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成
程序:

#include <stdio.h>

#define N 10

int main(int argc, char const *argv[])
{
    int a[N] = {0};
    int i = 0 , j = 0 , t = 0;
    printf("please input %d numbers:\n",N);
    for (i = 0; i < N; i++)
    {
        scanf("%d",&a[i]);
    }
    for (i = 0; i < N-1; i ++)
        for (j = 0; j < N - 1 - i; j++)
        {
            if (a[j] > a[j+1])
            {
                t = a[j];
                a[j] = a[j+1];
                a[j+1] = t;
            }
        }
    printf("the array after sort:\n");
    for ( i = 0; i < N; i++)
    {
        printf("%5d",a[i]);
    }
    printf("\n");
    
    return 0;
}

演示情况:
在这里插入图片描述
②简单选择排序
思路:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

程序:

/*
 * @Description: 
 * @Author: 余红祥
 * @Date: 2022-07-02 13:58:15
 * @LastEditTime: 2022-07-02 14:02:27
 * @LastEditors:  
 */
#include <stdio.h>
#define N 10
int main(int argc, char const *argv[])
{
    int a[N] = {0};
    int i = 0 , j = 0 , r = 0 , t = 0;
    /*1、输入*/
    printf("please input %d numbers:\n",N);
    for (i = 0; i < N; i++)
    {
        scanf("%d",&a[i]);
    }

    /*2、排序过程*/
    for ( i = 0; i < N - 1; i++)
    {
        r = i;
        for (j = i + 1; j < N; j++)
        {
            if (a[j] < a[r])
                r = j;
        }
        if (r != i)
        {
            t = a[r];
            a[r] = a[i];
            a[i] = t;

        }
    }
    
    /*3、显示*/
    printf("the array after sort:\n");
    for ( i = 0; i < N; i++)
    {
        printf("%5d",a[i]);
    }
    printf("\n");
    return 0;
}

演示情况:
在这里插入图片描述

2、有一个3×4的矩阵,找出其最大值并打印最大值的位置。

答:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int a[3][4] = {{1 , 65 , 34 , 67} , {23 , 33 , 57 , 24} , { 1 , 2 ,3 ,4} };
    int max = 0 , i = 0 , j = 0;
    int row = 0 , column = 0;
    max = a[0][0];   //假定第一个元素为最大值  用后面的和第一个比
    for ( i = 0; i < 3; i++)
    {
        for ( j = 0; j < 4; j++)
        {
            if ( a[i][j] > max )
            {
                max = a[i][j];
                row = i;
                column = j;

            }
        }
    }
    printf("max value is : %d , row = %d , column = %d\n",max,row,column);
    
    return 0;
}

结果演示:
在这里插入图片描述

3、打印杨辉三角的前十行(学会复杂问题分解化:首先打印一个全为1的a[10][10],然后取对角线以下的数据,最后调整值)

代码如下:

#include <stdio.h>
int main(int argc, char *argv[])
{
	static int a[10][10];
	int i = 0,j = 0;
	for(i = 0; i < 10; i++)
	{
		a[i][0] = 1;
		for(j = 1; j <= i; j++)
		{
			a[i][j] = a[i-1][j-1]+a[i-1][j];
		}
	}
	for(i = 0;i<10;i++)
	{
		for(j = 0;j<=i;j++)
			printf("%d  ",a[i][j]);
		printf("\n");
	}
	//printf("%d\n",a[-1][1]);
	return 0;
}

结果演示:
在这里插入图片描述

4、删除字符串重复字符

答:

/*
 * @Description: 
 * @Author: 余红祥
 * @Date: 2022-07-02 16:23:56
 * @LastEditTime: 2022-07-02 16:28:19
 * @LastEditors:  
 */

#include <stdio.h> 
#include <string.h>
#define N 100
int main(int argc, char **argv)
{
	char str[N] = {0};
	printf("please input a string:\n");
	scanf("%s",str) ;
	int length = strlen(str);

	int i = 0, j = 0 , pos = 0;
	for(i = 0;i < length;i++){
		for(j = i+1;j < length;j++)
        {
			if(str[i] == str[j])     //有相同字符
            {
				for(pos = j;pos < length; pos++)    
                {
				str[pos] = str[pos+1];		//下一个字符赋值给当前字符 (相当于删除字符操作) 
				}
				length--;
				j--;	                    //由于当前字符已删除,需要再次判断当前位置的字符是否和第i个字符相等 
			}
		}
	}
	printf("new string:%s\n",str);
	return 0;
}

结果演示:
在这里插入图片描述

5、统计字符串中空格的数量

答:

#include <stdio.h>
#include <string.h>

#define N 100

int main(int argc, char const *argv[])
{
    int i = 0 , num = 0;
    char str[N] = {0};
    printf("please input a string:\n");
    gets(str);                          //本来这里用的是scanf  但是scanf遇到空格认为是结束符了  改用gets
    while (str[i] != '\0')
    {
        if (str[i] == ' ')
        {
            num++;
        }
        i++;
    }
    printf("the space num is: %d\n", num);

    return 0;
}

在这里插入图片描述
ps:欢迎大佬指正,共同学习进步。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值