【20180727】【C/C++基础知识】数组与结构:定义、初始化、使用和两者的区别,几个简单小程序

数据类型 数组名[数组元素数量];

注意:数组元素数量一定是常数、常量、整型,不能是变量!

 

数组:一组具有相同类型的变量的集合。

数组名:标识这组相同的数据的名字。

数组元素:构成数组的每个数据项。

 

数组:定义、初始化、使用

 

一维数组

  • 定义:存储类型 数据类型 数组名[正整数]
  • 初始化:

全部初始化:int a[5]={12,23,45,35,9},int a[]={11,22,3,4,3},没有数组长度的是全部初始化,它的长度根据后面的个数来定。

部分初始化:int a[5]={0},第一个元素初始化为0,其他没有初始化的也是零。int a[5]={11},第一个元素是11,其他没有初始化的为0。

  • 使用:

一维数组的使用

数组下标:数组元素的索引

注意:数组下标的索引都是从0开始的,例如使用score[0],…,score[9],下标既可以是常量,也可以是整型表达式,允许快速随机访问。

对于单个数组元素,我们可以把它看成普通变量使用。

float score[10];
score[5]=80;   // 第6个人的成绩是80。
  • 存储:

系统分配一块连续的存储空间,大小为:数据类型的大小*数组元素数量。

注意:数组名表示数组的首地址(即第一个数组元素的地址)!

例如:

int a[10];

&a[5] = ?

假设数组首地址是a = 1000,整数类型占用4个字节空间,因此,第6个元素的地址是:&a[5] = 1000+4*5=1020。(第6个元素前面存储了5个元素)

例:怎么使两个数组的值相等?

int a[4]={2,1,4,6},b[4];

b=a;

注意:数组名是常量(表示首地址),它的值不能被修改!因此数组间不能进行赋值!

方法:

方法1:逐个元素赋值

b[0]=a[0];

b[1]=a[1];

……

 

方法2:循环语句赋值(实质还是逐个赋值)

int i;

for(i=0;i<4;i++)

{

       b[i]=a[i];

}

 

程序1:显示用户输入的月份拥有的天数(不包括闰年的月份)

输入:月份,如果不在1~12之间,则要求重新输入

输出:月份对应的天数

算法思想:每个月多少天是固定的,我们把12个月分别有几天存储在长度为12的一维数组中,并用month-1作为数组的下标进行索引。

/* 显示用户输入的月份拥有的天数(不包括闰年的月份) */
#include "stdio.h"
#include<stdlib.h>
#define MONTH 12    // 注意:define宏定义放在main函数之前,并且不能有分号!!!
int main()
{
    int i, month;
    int days[MONTH] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
    do
    {
        printf("Please input a month...\n");
        scanf_s("%d", &month);
    } while ((month < 1) || (month > 12));  // 注意这里是逻辑或!用于处理不正常的月份输入!如若输入正常,那么会退出循环输出天数!
    printf("The number of days is %d!\n", days[month - 1]);   // 注意:下表索引是month-1 !!!
    system("pause");
    return 0;
}

 

程序2:把10个人的成绩存入score数组中,找出最高分及所在位置。

输入:10个人的成绩

输出:最高分和所在位置

输入转化成输出的算法思想:用一个整数存储找到的最高分,一个整数存储它的位置,初始时第一个人,初始是第一个人,然后从第二个人依次判断是否高于上一个,如果是,则更新;

/* 把10个人的成绩存入score数组中,找出最高分及所在位置 */
#include "stdio.h"
#include<stdlib.h>
int main()
{
    int i, student = 0;
    float max, score[10];
    for (i = 0, max = score[0];i < 10;i++)
    {
        scanf_s("%f", &score[i]);
        if (score[i] > max)
        {
            max = score[i];
            student = i;
        }
    }
    printf("The highest score is %.2f!\nThe student is %d!\n", max, i);
    system("pause");
    return 0;
}

补充:

1. const常量使用方法:const int n=10; 放在main函数里面,后面有分号!

2. define宏定义:#define N 10; 放在main函数前,不能有分号!

3. score[i]为数组元素;score+i和&score[i]表示元素地址。

4. 所有数据在使用前必须初始化!

 

如果一维数组的元素是一个n-1维数组,就构成了n维数组。

  • 多维数组的定义:

类型定义符 数组名 [元素个数1][元素个数2]…[元素个数n]

使用多维数组时注意越界问题!

  • 二维数组的存储:一个两行三列的数组的逻辑存储结构

列优先:把每一列存完再存下一列

行优先:先存每一行

不同的语言采用的存储方式不一样,C语言采用行优先方式。

二维数组的存储空间大小(多少字节数):

            数据类型的大小*一维数组元素数量*二维数组元素数量

  • 二维数组的初始化

全部初始化:

按元素初始化:int a[3][3]={1,2,3,4,5,6,7,8,9}

按行初始化: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}}

部分初始化:

部分元素初始化:int a[3][3]={1,2}    // a[0][0]=1; a[0][1]=2

按行部分初始化:int a[3][3]={{1,2}{4}{7,8,9}}

省略行数的部分初始化:int a[][3]={{1,2}{4}{7,8,9}}

 

字符数组同理

一维字符数组char James[]="James"; // 字符串初始化常量,默认在字符串最后加一个结束符\0,因此要加1!一个一维字符数组可以存放一个字符串!

二维字符数组:char name[][n]={};一个二维数组可存放多个字符串!

求字符串长度:len=strlen("James");

字符串处理函数,需要加头文件<string.h>。

字符数组的定义:char str[10];

初始化:

逐个元素赋值str[1]='c'; str[2]='h';

定义的同时初始化:按字符char str[10]={'c','h'};

按字符串常量初始化(可省略大括号):char s1[20]={"How do you do"}; char s2[20]="How do you do"; 这两个是等价的!

注意:用字符串常量进行初始化时,末尾自动加\0。

其他初始化的方法:

1. 输入输出操作

单个字符输入输出(格式字符为%c):

char str[10];
for (i=0;i<5;i++)
    scanf_s("%c",&str[i]);   // 数组元素输入
for(i=4;i>=0;i--)
    printf("%c",str[i]);    // 数组元素输出

字符串整体输入输出(格式字符为%s):

char str[10];
scanf_s("%s",str);    // 数组名是数组的起始地址
printf("%s",str);

注意:scanf函数这里不加&,对于数组不加取地址符,因为数组名就是地址!

 

字符串输入输出函数:

gets函数:gets(字符数组名),作用:从终端输入一个字符串到字符数组。

例如:

gets(word);  // 从终端输入一个字符串存到word数组中,系统会自动加上'\0'结束符

puts函数:puts(字符数组名),作用:把一个字符串输出到终端,并在输出时将字符串结束标记'\0'转换为'\n',即输出完字符串后换行。

如:

char word[]='abc';
word1[]='def';     // 用puts输出时,abc和def显示在两行。

 

常见的字符串处理函数:

字符串连接函数strcat(字符数组1,字符数组2);

功能:将字符数组2连接到字符数组1的后面。因此要求新数组长度可以容纳两个字符数组合并之后的长度。

 

字符串拷贝函数strcpy(字符数组1,字符数组2);

功能:把数组2的内容拷贝到数组1中。第二个参数可以是字符串常量!str1=str2这种形式是错的!

 

字符比较函数strcmp(字符串1,字符串2);

功能:将两个字符串的字符从左到右逐个进行比较,返回值为比较结果。

字符串1=字符串2,则返回0;

字符串1>字符串2,则返回正数;(看ASCII码谁大)

字符串1<字符串2,则返回负数;

 

字符串长度函数strlen(字符数组)

功能:测试字符数组中字符串的实际长度。(注意这里不包含结束符,即不要加1!)

 

程序3:存储100个学生的姓名、学号、语文成绩、数学成绩、英语成绩,并按语文成绩进行排序,按名次输出所有学生信息。

思路:姓名可以用字符数组存储,100个学生就要100个一维字符数组,姓名是有长度的,假设所有姓名长度不超出20,因此我们用100*20的一个二维数组存储姓名。学号用整数存储,用一个长度为100的整数数组,三门成绩用3个长度为100的实数数组。

选择排序算法:把所有人的最高分找到并放到第一个位置,余下的最高分放到第二个位置,…,最低分放到最后一个位置。

错误:语文成绩降序排列了,但其他信息没有跟着一起更新!说明其他信息没有配对!

解决方法:需要将每个人的信息组织在一起(结构),对语文成绩降序排列时其他成绩也跟着排序。

 

结构:(定义方式和普通变量定义方式相同,对于这个问题而言,每个学生有五个变量:姓名、学号和三门成绩)

struct student
{
    char name[20];
    int id;
    float chinese;
    float english;
    float math;
}

结构是一个或多个变量的集合,结构中的变量可以是不同的类型(这点和数组不同,数组要求变量类型相同!),为了处理方便,我们把这些变量组织在一个名字下。结构将一组相关的变量看做一个存储单元,而不是各自独立的实体,因此结构有助于组织复杂的数据。

结构类型需要先定义,定义结构类型struct,一般格式为:

struct 结构类型名
{
    类型名1 成员名1;
    类型名2 成员名2;
    ……
    类型名n 成员名n;
}

定义了结构类型之后,并不能直接使用!struct和int/char这些都一样,只是说明了变量集合是什么样子,要定义变量之后才能分配空间、存储数据、操作数据!例如:

struct student s1,s2={"张三", 2, 95, 88, 73};  // 初始化(存储)了s2的姓名、学号和三门成绩
s1.id = 1;   // 初始化(存储)s1的id(学号)

 

问题:如果想要s1结构体也具有和s2完全相同的数据,可不可以s1=s2?

答:可以!数组不可以进行整体赋值,但相同类型的结构体可以进行整体赋值!因为数组名是常量,是不能被修改的!

 

注意:

1. 结构类型名不得与其他变量的名字相同。

2. 结构成员名可以与其他变量名字相同(作用域不同)。

3. 结构类型定义之后一定要加一个分号。

4. 不同类型的结构变量不允许相互赋值(相同类型可以相互赋值)。

5.结构类型名必须包含关键字struct。

6. 结构变量的初始化和数组变量的初始化相同。

7. 结构类型不是定义变量,只是定义了数据类型是结构体类型。

/* 按语文成绩降序排列 */
#include<stdio.h>
#include<stdlib.h>
const int n = 4, m=20;  // C文件中const常量的使用并没有把n和m看做常量,而是固定的变量,因此会报错,只需要将文件类型改为.cpp即可。
typedef struct student  // 定义了struct student的类型,然后用typedef将struct student重新定义为student,因此在后面我们就可以直接使用student
{
    char name[m];
    int id;
    float chinese;
    float english;
    float math;
}student;

int main()   // 通过for语句将所有人的信息读入到数组s[n]中
{
    student s[n],tmp;
    int i,max,j;
    for(i=0;i<n;++i)
    scanf_s("%s%d%f%f%f",s[i].name,m,&s[i].chinese,&s[i].english,&s[i].math);
}


for(i=0;i<n;++i)   // 选择排序算法
{
    max=i;
    for(j=i+1;j<n;++j)
    if(s[j].chinese>s[max].chinese)
    max=j;
    tmp=s[max];
    s[max]=tmp;
}


for(i=0;i<n;++i)   // 输出
  printf("%s%d%.2f%.2f%.2f\n",s[i].name,s[i].id,s[i].chinese,s[i].english,s[i].math);
    system("pause");
    return 0;

}

 

程序4:求1~100间所有素数。

/* 求1~100之间的所有素数 */
#include<stdio.h>
#include<stdlib.h>
#include<math.h>   // 错误:sqrt()未定义!原因是:忘记加这个头文件<math.h>
int main()
{
    int i,n,bprim;
    for(n=1;n<=100;++n)
    {
        int bprim=1;
        for(i=2;i<=sqrt(n);++i)
        {
            if (n%i==0)
            bprim=0;
            break;
        }
        if(bprim==1)
        printf("%d is prim!\n",n);
        else
            printf("%d is not prim!\n",n);
    }
    system("pause");
    return 0;
}

思路:依次判断2,3,…,100,方法可行,但效率较低。

优化思路:2是素数,2的所有倍数是合数,3是素数,3的所有倍数标记为合数,4是2的倍数(已经标记过),5是素数,5的所有倍数我们标记为合数。

关键:需要标记2~100间的数是否处理过!我们假定一个长度为100的数组,初始化为0,表示全部是素数,如果判断为合数那么置为1。

/* 求1~100之间的所有素数 */
#include<stdio.h>
#include<stdlib.h>
#include<math.h>   // 错误:sqrt()未定义!原因是:忘记加这个头文件<math.h>
int main()
{
    const int n=100;   // C文件中const常量的使用并没有把n和m看做常量,而是固定的变量,因此会报错,只需要将文件类型改为.cpp即可。
    int isPrim[n+1]={0};   // 注意:我们要判断2~100,因此需要101个数作为下标!
    int i,j;
    for(i=2;i<sqrt(double(n));++i)  // 错误:sqrt对重载函数的调用不明确。
                                   //原因:sqrt函数的参数和返回值都是double类型,因此要做一个强制类型转换!)                
                                  // 一个数一定是:大数乘小数,sqrt(n)内没有出现它的小因子,那么之后就一定没有大因子,
                                  // 说明sqrt(n)之后的数是不是素数,取决于前面半部分的倍数。
    {
        if (isPrim[i]==0)  // 很简单:因为2是素数,因此从2开始,把它所有倍数标记为1,
                     // 那么再依次搜索下一个为0的标志位,只要遇到0,一定说明它是素数(因为是1点1点加上来的)     
            for(j=2*i;j<=n;j+=i)   // i是素数,那么输出它的所有倍数j,并把它们的标志位标记为1
                isPrim[j]=1;
    }
    for(i=2;i<=n;++i)
        if(isPrim[i]==0)
        {
            printf("%d ",i);  // %d后面有个空格!
        }
    system("pause");
    return 0;
}

 

程序5:给定由6个整数组成的序列{16,8,4,32,5,9},将其按从小到大的顺序排列。

思路:首先存储数据(一维数组存储)

方法:冒泡排序法(从起始端开始,依次比较相邻的两个元素,若他们不合顺序,则交换位置)。第一趟:最大元素放到了最后的位置,第二趟:次大元素放到了右边第二个位置……

/* 给定由6个整数组成的序列{16,8,4,32,5,9},将其按从小到大的顺序排列 */
#include<stdio.h>
#include<stdlib.h>
#include<math.h>   // 错误:sqrt()未定义!原因是:忘记加这个头文件<math.h>
int main()
{
    const int n=6;
    int i,j,tmp;
    int s[n]={16,8,4,32,5,9};  // 测试时最好初始化便于检查是否正确,若正确,再加上输入数据的功能。
    for(j=1;j<6;++j)    // j表示第几趟,若有n个数,那么进行n-1趟,每一趟比较进行n-j次比较!
    {
        for(i=0;i<n-j;++i)   // i表示第j趟的第几位参与比较(注意:是进行n-j次比较!)
        {
            if(s[i]>s[i+1])
            {
                tmp=s[i];
                s[i]=s[i+1];
                s[i+1]=tmp;
                }
            }
    }
    for(i=0;i<=5;i++)
        printf("%d\n",s[i]);    // 不要忘记输出啊!
    system("pause");
    return 0;    // 养成习惯,函数返回整数,则在还没有进行程序编写前,加一个return+整数。
}

C语言中不允许内部定义!C++允许。意思是说:for(int i=1; ;);在C++中允许,C语言中不允许。

 

程序6:查找在N个元素中是否存在某一元素。

分析:若数据无规律,则需进行逐一比较。若有规律(数据有序),采用折半查找法可以提高效率。

思路:若数据有序,那么定义low,high和middle。每次只差找一半,效率很高!

/* 在10个有序的数据中查找是否存在某一元素,折半查找法! */
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int *data,len,x,i;
	int low=1,high,mid;
	printf("输入数组元素个数:");
	scanf_s("%d",&len);
	data=(int*)malloc((len+1)*sizeof(int));  // malloc用来动态申请内存空间
	                            // 下标是从0开始,为了能够存储len那个位置的元素,这里是len+1!
	if (data==NULL)
	{
		printf("分配内存失败");
		return -1;
	}
	printf("输入有序数据:");
	for (i=0;i<len;++i)
		scanf_s("%d",&data[i+1]);
	printf("输入待查找数据:");
	scanf_s("%d",&x);
	high=len;
	mid=(low+high)/2;
	while(low<=high)    // 折半查找法
	{
		if(data[mid]==x)
		{
			printf("%d\n",mid);
			break;
		}
		else if(data[mid]>x)
		{
			high=mid-1;
		}
		else
		{
			low=mid+1;
		}
		mid=(low+high)/2;
	}
	if(data[mid]==x)
	{
		printf("%d在数组的第%d个位置\n",x,mid);
	}
	else
		printf("%d不在待查找元素序列中。\n",x);
	if(data)    // 如果data内存空间分配成功,则最后一定要释放内存空间!
		free(data);  // free用来释放malloc申请的空间,malloc和free配对使用,有malloc一定有free!
	system("pause");
	return 0;
}

(参考:https://www.jb51.net/article/114244.htm

 

程序7:求Fibonacci数。

规律:前面两个月的和是后面一个月的兔子对数(前两个月是1对,从第三个月开始后面一个月的兔子对数是前面两个月之和)。

分析:有多少月就需要多少需要保存(用数组)。

/* 求第10个月有多少对兔子 */
#include<stdio.h>
#include<stdlib.h>
int main()
{
	const int n=11;
	int f[n],i;
	f[1]=1; f[2]=1;
	printf("% 4d % 4d",f[1],f[2]);
	for(i=3;i<n;++i)
	{
		f[i]=f[i-1]+f[i-2];
		printf("% 4d",f[i]);
	}
	system("pause");
	return 0;
}

 

程序8:在屏幕上模拟显示一个数字时钟。

分析:时钟分为时分秒,因此定义一个结构存储时分秒。

/* 在屏幕上模拟显示一个时钟 */
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>  // Sleep函数的头文件!
struct clock   // 定义一个时钟结构体类型(结构体定义在主函数外面)
	{
		int hour;
		int minute;
		int second; 
	};   // 或者在后面用typedef struct clock CLOCK;
	typedef struct clock CLOCK;

int main()
{
	CLOCK t={0,0,0};  // 定义一个CLOCK类型的t变量
	int n=100,i=0;    // 这个n表示让它显示的总秒数
	while(++i<n)
	{
		t.second++;
		if(t.second>=60)  // 秒数》=60,分加一,秒归零
		{
			t.second=0;
			t.minute++;
		}
		if(t.minute>=60)
		{
			t.minute=0;
			t.hour++;
		}
		if(t.hour>=24)
			t.hour=0;
		printf("%2d:%2d:%2d\r",t.hour,t.minute,t.second);  // 用冒号:分隔开
// \r表示在同一行输出,如果用的是\n,可以用清屏函数system("CLS")达到同样的目的。
		Sleep(1000); // 每1000ms进行一次循环,注意:Sleep函数S是大写!头文件是<windows.h>!
	}
	system("pause");
	return 0;
}

 

区分:Sleep(ms)和sleep(s),简单说,VC用Sleep,其他一律用sleep(例如Linux)。

 

已知获取本机当前时间的函数,修改时间显示程序,可以实时显示本机当前时间。

time_t now;  // 定义time_t变量
struct tm *local;  // 定义struct tm变量
time(&now);   // 利用time函数获取时间
local=localtime(&now);   // 通过localtime把获取的时间转换为当地时间,输出类型是struct tm类型
printf("%2d:%2d:%2d\r",local->tm_hour,local->tm_min,local->tm_sec);  // 输出
                             // \r表示在同一行输出,如果用的是\n,可以用清屏函数system("CLS")达到同样的目的。

注意:\r是回车符,表示还是在当前行,并且光标移到当前行的第一格。\n是换行符。\r\n是回车加换行。

 

程序9:数据分析。从键盘输入学生的学号,分析其年级、学院、专业、班级、编号。

分析:读入字符串,共14位(加一个结束符)。每个学生的所有数据是一个结构,结构体中有年级、学院、专业、班级、编号五个成员,每个成员是一个字符数组,用字符数组存储字符串,存储时内存要多分配一个存储结束符。

/* 数据分析 */
#include<stdio.h>
#include<stdlib.h>
typedef struct student
	{
		char grade[5];  // 每个成员最后都要加一个结束符,因此成员长度比实际大一
		char department[3];
		char major[3];
		char cclass[3];
		char number[4];
	}student;
int main()
{
	char number[14];
	student s;
	int i;
	scanf_s("%s",number,14);   // scanf_s用于字符型数据时,要加上字符个数!
	for(i=0;i<4;++i)
		s.grade[i]=number[i];
	s.grade[i]='\0';      // 加字符结束符
	s.department[0]=number[i++];
	s.department[1]=number[i++];
	s.department[2]='\0';
	s.major[0]=number[i++];
	s.major[1]=number[i++];
	s.major[2]='\0';
	s.cclass[0]=number[i++];
	s.cclass[1]=number[i++];
	s.cclass[2]='\0';
	s.number[0]=number[i++];
	s.number[1]=number[i++];
	s.number[2]=number[i++];
	s.number[3]='\0';
	printf("学院:%s;专业:%s;年级:%s;班级:%s;学号:%s\n",s.department,s.major,s.grade,s.cclass,s.number);
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40583722/article/details/81291408