2023年3月

一、引子

我们学习了C语言了,我们可能会想知道如果我们有一列数字,或者其他的同种类型的一长串的数据,如果我们还是按照以往的设置变量,给每个变量赋值。这样显然时可行的,但是如果我们有10个甚至更多的数据,这样去定义似乎就有点愚蠢了,然后数组就正好能够胜任这个工作了。至于我们有上万的数据,这个另当别论,可以导入文件这种进行操作。我们单纯地介绍数组的这种情况。

二、数组的相关概念

数组是由数据类型相同的一系列元素组成,需要使用数组的时候,通过声明数组告诉编译器数组中内含多少元素和这些元素的类型。

如何创建一个数组:

int arr[5] = { 1,5,6,7,8 };

如何创建一个只读数组:(注意这里有一个const,这个我后期会对他进行解释。)

const int arrt[5] = { 4,8,9,12,6 };

这里因为我们给数组赋予了相应数量的值,问题来了,如果我们在数组中提供的值小于数组大小的话,会产生什么效果呢?如果是更大呢?第一个问题答案是它会自动给未赋值的数组值赋值未0,第二个就直接报错了。然后这里需要提一下,就是如果我们有一组数据是这样的:0,0,0,0,122,0我们可以这样赋值来节省时间。

int arr[6] = { };
    arr[4] = 122;

上面介绍的都是一维数组的例子,现在我们来介绍一下二维数组的例子。

二维数组和一维数组类似,实际上使用的方式也是类似的:

#include<stdio.h>
int main(void)
{
int arr[6][6] ={ };
arr[
4][2] = 122;
arr[
3][3] = 15;inti,j;for (i = 0; i < 6; i++)
{
for (j = 0; j < 6; j++)
{
printf(
"%5d", arr[i][j]);
}
printf(
"\n");
}
return 0;
}

然后数组的相关知识是讲完了。它的实际应用需要和指针,函数结合在一起进行阐述。在指针中实际上只需要记住一个点,就是数组名就是该数组首元素的地址,这给了我们利用指针调用数组中的元素提供了方便。然后我们数组爱函数中实际上还可以作为形参。这也就意味着这两种形式都是可行的:(需要注意的是,只有在函数原型或者函数定义里面,才能用int ar[]代替int *arr[]

int sum(int *arr, intn);int sum(int arr[], int n);

然后,我们可能回想,我们能不能创建一个变长数组?答案是可以的。

但是这个变长的意思是说我们在创建数组的时候,可以通过使用变量来指定数组的维度,不是说可以随意的改变数组的长度这样子。

补充:然后我们实际上还可以使用malloc+free进行。

malloc的定义方法为:

    double *ptd;
ptd
= (double*)malloc(30 * sizeof(double));
free(ptd);

malloc就是我们可以在使用的时候才对它进行输入所需要的内存大小,然后如果我们这个变量用完了,就得free掉这段内存,不然这段内存就会一直存放在电脑里面。

三、字符数组

用来存放字符数据的数组是字符数组,字符数组的一个元素存放一个字符。类似的定义以及引用如下:

char arr[10] = { 'I','L','O','V','E','Y','O','U'};inti;for (i = 0; i < 10; i++)
{
printf(
"%c\n", arr[i]);
}

说到字符数组,就将字符串一起解释:

字符串的结束系统是会自动加上一个‘\0’.常用的字符串处理函数包括:(注意,下面这些函数,均需要加头文件string.h才可以使用)

(1)puts(str)函数——输出字符串的函数

    char arr[20] = "string";
puts(arr);

(2)gets(str)函数——输入字符串的函数(注意这个函数可能有的情况下无法使用,如果不能使用,可以考虑使用gets_s(str)函数

    char arr[20];
gets_s(arr);
puts(arr);

(3)strcat(str1,str2)函数——字符串连接函数,会把str2加到str1上去,这个函数无法计算str1中是否能容纳下str2。

    char str1[20] = { "string"};char str2[20] = { "usuuss"};
printf(
"%s", strcat(str1,str2));

为了解决上面的那个问题,我们可以使用strncat来进行相关操作:strncat(str1,str2,n)中的n是说在str1中的最大添加字符数。例如:

    char str1[10] = { "string"};char str2[10] = { "usuuss"};
printf(
"%s", strncat(str1,str2,2));

注意,这里要是把n设置为了6,因为6+6>10,所以会报错。

(4)strcpy(str1,str2)和strncpy(str1,str2,n)函数

第一个是说将str2复制到str1中去。第二个是说将str2中的n个元素复制到str1中去。

(5)strcmp(str1,str2)函数——字符串比较函数

    char str1[10] = { "string"};char str2[10] = { "usuuss"};
printf(
"%d\n", strcmp(str1, str2));

如果str1>str2,返回1;str1=str2,return 0;str1<str2 return -1;

(6)strlen(str)函数——测字符串长度的函数

(7)strlwr函数——转换为小写的函数(如果strlwr或者strupr无法使用,请换成_strlwr和strupr)

    char str1[10] = { "string"};char str2[10] = { "uAuUss"};
printf(
"%s\n", _strlwr(str1));
printf(
"%s\n", _strupr(str2));

(8)strupr函数——转换为大写的函数

然后数组的相关知识也讲完了,现在就对上面提到的const进行解释。const的意思是说这个变量或者参数不能够修改,这个和使用#define指令有点类似,但是const会更加灵活。我们可以使用const来创建const 数组,const指针和指向const的指针。需要强调的是吧const指针或非const数据的地址初始化为指向const的指针或为其赋值是合法的,然而,只能把非const数据的地址赋给普通指针。这里需要进行一个辨析:就是const和*的位置的问题,如果const在*左边,限定了指针指向的数据不能改变,如果在右边,那么就是指针本身不能够改变

一、引子

在上一篇的博客中,我们讲述了数组和字符串,然后知道了数组可以用于存储相同类型的数据。现在问题来了,我们能不能创建一种数据类型,使得可以用来存储不同种的数据?答案是可以,就是利用结构体。

二、如何构建结构体?

    structbook
{
floatprice;char_name;intnumber;
}store;

注意,这里面的book,store,{}内的内容最少要有两个。

三、结构体变量的初始化:

store.number = 12;
store.price
= 15.2;
store._name
= 'A';
printf(
"numer=%d,_name=%c,price=%f\n", store.number, store._name, store.price);

四、指向结构体的指针:

    struct book*b_point;
b_point
= &store;
printf(
"%d\n",b_point->number);

这里需要注意一个地方,就是我这里用的是->,而不是上面的.,这是因为b_point是一个指针,而不是结构名。

一、引子

上面我们已经讲过了结构体,结构体是说我们定义了一个结构体,这个结构体可以由不同的数据组合,然后每一个变量都有一定的内存地址。然后现在有另外一种数据结构,它允许在相同的内存位置存储不同的数据类型。然后可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。

二、如何定义共用体

union book
{
floatprice;intnumber;char _name[20];
}store;

三、共用体的实例

union book
{
floatprice;intnumber;char _name[20];
}store;
store.number
= 12;
strcpy(store._name,
"happy");
printf(
"please output the size of store:%d\n", sizeof(store));
printf(
"please output the value of strore._name:%s\n", store._name);
printf(
"please output the value of strore.number:%d\n", store.number);

输出:

please output the size of store:20please output the value of strore._name:happy
please output the value of strore.number:
1886413160

同一时间只用一个成员,这是造成第三个输出结果的原因。

下面提一下typedef

typedef是一种我们可以用来对类型取一个新的名字的关键字,举个例子:

    typedef intAll_Number;
All_Number i
= 12;
printf(
"i=%d\n", i);return 0;

输出

i=12

它和define 的区别:

1、typedef仅限于为类型定义符号名称,#define不仅可以为类型定义别名,也能为数值定义别名,比如将2定义为true

2、typedef是由编译器执行解释的,#define语句是由预编译器进行处理的。

在进行阐述之前,我们必须得先明白一个问题,就是C语言是什么。它是一门高级语言,这使得我们不必再用数字码表示指令,使得我们的指令可以更好的体现我们的想法,然后也不需要去考虑机器它怎么执行我们的代码的,我们要做的,仅仅是让计算机得出我们想要的结果。然后,它是一门面向过程的语言,也就是说它是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用。

我们使用C语言,那么它有那些特点呢?它具有:

1、语言简洁、紧凑、使用方便、灵活

2、运算符丰富

3、数据类型丰富

4、具有结构化控制语句

5、语法限制不太严格,程序设计自由度大

6、C语言允许直接访问物理地址,能进行位操作,能实现汇编语言的大部分功能,可以直接对硬件操作

7、可移植性好

8、生成目标代码质量高,程序执行效率高等.

上面说明了C语言的八个特性。我们对其进行相应的阐述,

较之于之前的语言,其的可读性有了较大的提升,对于运算符方面,可以直接参考这个网站中说明的,也就是说包含了算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、杂项运算符。这里不再赘述:

C 运算符 | 菜鸟教程 (runoob.com)

其中需要进行说明的是位运算符中的取反运算、左移、右移。取反运算是说我们输入一个数字,将它的二进制进行取反,也就是1变成0.

数据类型丰富

它目前拥有的数据类型可以概括为:基本类型、枚举类型、空类型、派生类型。其中的基本类型就是我们常用的整数类型和浮点类型,派生类型有指针类型、数组类型、结构体类型、共用体类型以及函数类型。

整数类型:包含了int,long,short,char.。浮点数类型:float,double

枚举类型:枚举类型比较特别,它是声明符号名称来表示整形常量。例如我们常用的一周,将周一设为0,周二设为1这样。例子如下:

#include<stdio.h>
int main(void)
{
	enum week{monday,tuesday,wednesday,thirsday,friday,saturday,sunday
	};
	int day;
	for(day=monday;day<=sunday;day++)
	{
		printf("%d\n", day);
	}
	return 0;
}	

在除此以外,我们也可以像定义数组一样,给里面的变量分别赋值。然后这个就可以想到同样效果的另外一个东西,没错,就是宏。我们也常使用宏来给一个变量赋值,那么它们之间有什么区别吗?

1、宏常量是在预编译阶段进行简单替换,枚举变量则是在编译的时候确定其值。2、一般在编译器里面,可以调试美剧常量,但是不能调试宏常量。3、枚举可以一次定义大量相关的常量,但是宏定义只能定义一个。

4、枚举类型可以自增一(但是这是在你定义的值之间为连续,或者仅仅只是定义了一个值的时候)5、枚举是一个集合,代表一类值,就像上面代码中一周中的天数归为一类,宏定义却不行。

空类型:这个用的比较少

指针类型:这个可以参照我的另外一篇博客:

指针

数组类型:这个可以参照我另外一篇博客:

数组

结构体类型:这个可以参照我另外一篇博客:

结构体

共用体类型:这个可以参照我另外一篇博客:

共用体

函数类型:这个可以参照下面的链接:

函数

具有结构化控制语句
:具体可以参考我的一篇博客

结构化控制语句

说到结构化控制语句,我们首先得知道C语言有五种常用的语句,包括了:

1、表达式语句,就像12;c=12;这样的语句

2、流程控制语句

(包括结构化语句和非结构化语句)

结构化语句包括条件语句和循环语句

非结构化语句包括限定向语句和非限定转向语句

3、函数调用语句

4、空语句

5、复合语句

对文件的处理
:具体参照这个链接里面的讲述:

文件的处理

C语言中使用的两种注释方法:

1、//

2、/* */

C语言程序的结构特点

1、一个程序包含一个或多个源程序文件组成。

一个源程序文件可以包含3个部分:预处理指令(比方说#include<stdlib.h>、全局声明、函数定义

2、函数是C程序的主要组成成分。

如果我们敲代码多的话,我们会发现一个有意思的现象,就是一个程序中一般都包含着一个main函数,如果这个函数存在的话,那么这个函数就是程序的主要部分(是说程序从这开始调用其他函数,进行程序运行)。注意,我说的是一般,有的时候,如果这个项目很大,会存在源文件中没有main函数地方情况,这个需要注意。

3、一个函数包含两个部分

函数是完成特定任务的独立程序代码单元。它包含两个部分,函数头和函数体,函数体由声明部分和执行语句组成

4、程序总是从main函数开始执行,从main函数结束

5、程序中对计算机的操作都是由函数中的C语句完成的

6、在每个数据声明和语句最后必须有一个分号

7、程序中应该包含注释

我们现在知道C语言程序的特点了,那么我们需要知道一个问题,就是程序是什么?程序=数据结构+算法+程序设计方法+语言工具和环境。

看到这句话我们可能就懵掉了,数据结构是什么?算法又是什么?数据结构是说在程序中需要指定数据的类型和数据的组织形式。算法是说操作步骤,计算机算法可以分为数值运算算法(求解数值)和非数值运算算法(事务管理领域)。

算法的特性:

1、有穷性:一个算法应包含有限的操作步骤而不是无限的。

2、确定性:算法中每一个操作步骤应当是确定的,而不能是模糊的。

3、由零个或多个输入

4、有一个或斗个输出

5、有效性:算法中每一个步骤应该能够有效地执行,并得到确定的结果

如何表示一个算法?主要表示方法为:伪代码和流程图。其中伪代码是指我们可以通过使用一种介于自然语言和计算机语言之间的文字符号来描述算法,伪代码在编译原理中常常使用来进行表示系统中代码之间的逻辑。流程图实际上就是由几个图像进行算法的描述。包括的图像有起止框、输入输出框、判断框、处理框、流程线、连接点这些。可以参考这篇文章里面的讲述

流程图

C语言的三种基本结构为顺序结构、选择结构、循环结构。这三种基本结构的特点为只有一个入口,只有一个出口,结构内的每一部分都可以被执行,结构内不存在“死循环”。当然了,还有一种绘制流程图的方法,就是用N-S流程图表示算法,可以参考这篇文章:

N-S流程图

结构化程序设计方法:自顶向下,逐步细化,模块话设计,结构化编码。

一、引子

我们前面讲数组的时候,提到了创建数组的三种方法,一种是创建静态数组,一个是创建动态数组,以及使用malloc创建动态数组。然后我们可能会有一个场景,就是我们并不知道我们实际需要多大的内存,我们需要可以不确定的添加数据,这个时候,我们可能回想,这个无所谓的,直接malloc就可以了。然后这个时候就有两个方式了,一个是说我们一来就定义好300个内存空间,还有一种是我们用的时候在调用malloc。然后第一种方法就造成了两种情况的发生,如果我们所需内存少于300,就浪费了,要是所需内存多余300,就不够了。第二种方法会出现一个问题,就是我们一个数据,可能所需内存太大了,使得一个malloc存不下。然后我们想到了一个新的思路,就是我们能不能这样,就是我们每次使用的时候,就进行malloc一次,然后这个malloc正好满足我们所需的内存大小,然后我们在该内存的的一部分空间内存放一个指针,指向上一个内存的地址,这样就像火车那样连接成了一串。

例子:

#include<stdio.h>#include<stdlib.h>#include<string.h>
#define TSIZE 45
structfilm {chartitle[TSIZE];intrating;struct film*next;
};
char* s_gets(char* st, intn);int main(void)
{
struct film* head =NULL;struct film* prev, *current;charinput[TSIZE];
puts(
"Enter first movie title:");while (s_gets(input, TSIZE) != NULL && input[0] != '\0')
{
current
= (struct film*)malloc(sizeof(structfilm));if (head ==NULL)
head
=current;elseprev->next =current;
current
->next =NULL;
strcpy(current
->title, input);
puts(
"Enter your rating<0-10>:");
scanf(
"%d", &current->rating);while (getchar() != '\n')continue;
puts(
"Enter next movie title (empty line to stop)");
prev
=current;
}
if (head ==NULL)
printf(
"No data entered");elseprintf("HERE is the movie lists:\n");
current
=head;while (current !=NULL)
{
printf(
"Movie :%d rating:%d", current->title, current->rating);
current
= current->next;
}
current
=head;while (current !=NULL)
{
free(current);
head
= current->next;
}
printf(
"BYE\n");return 0;
}
char* s_gets(char* st, intn)
{
char*ret_val;char*find;
ret_val
=fgets(st, n, stdin);if(ret_val)
{
find
= strchr(st, '\n');if(find)*find = '\0';else while (getchar() != '\n')continue;
}
returnret_val;
}