从变长数组到两种方法(动态内存与非动态内存)实现数组的动态控制。

目录

前言

一.关于变长数组

二.vs2022不支持变长数组的替代方案

1.非动态内存版

整型数组

字符数组

1.scanf

2.gets

2.动态内存版 

malloc实现一维动态数组p[m]

 malloc实现二维动态数组p[m][n]:

三.补充scanf与gets的讲解

1.scanf函数:

声明

参数 

具体类型

返回值

2.gets函数

声明

参数

返回值

致谢


前言

c语言刚刚刷题时,题目要求对数组进行各种变换,但数组是直接定义好的,不能改(release版本不具有广泛性)。整型数组不知道长度便不知道循环多少次,字符数组对于有空格的输入,scanf忽然“不灵”,现在我关于变长数组,以及关于vs2022不支持c99的替代方案

一.关于变长数组

在C99标准之前,C语⾔在创建数组的时候,数组⼤⼩的指定只能使⽤常量、常量表达式,或者如果我们初始化数据的话,可以省略数组⼤⼩。 这样的语法限制,让我们创建数组 就不够灵活,有时候数组⼤了浪费空间,有时候数组⼜⼩了不够⽤ 的。
C99中给⼀个变⻓数组(variable-length array,简称 VLA)的新特性,允许我们可以使⽤变量指定
数组⼤⼩。
int n = a+b;
int arr[n];

这样就允许我们自行确定数组的长度了,遗憾的是vs2022不支持,但gcc支持

#include <stdio.h>
int main()
{
 int n = 0;
 scanf("%d", &n);//根据输⼊数值确定数组的⼤⼩
 int arr[n];
 int i = 0;
 for (i = 0; i < n; i++)
 {
 scanf("%d", &arr[i]);
 }
 for (i = 0; i < n; i++)
 {
 printf("%d ", arr[i]);
 }
 return 0;
}
第⼀次测试,我给n中输⼊6,然后输⼊6个数字在数组中,并正常输出
第⼀次测试,我给n中输⼊12,然后输⼊12个数字在数组中,并正常输出

二.vs2022不支持变长数组的替代方案

1.非动态内存版

整型数组

不知道循环多少次,就输一个数字,统计一个数字直到回车,这样还不用sizeof函数来求整型数组的长度了。

	int arr[1000] = { 0 };
	int count = 0;
	do
	{
		scanf("%d", &arr[count]);
		count++;
	} while (getchar() != '\n');

定义整型数组,[]中不一定要1000,尽可能大就行了,太小了输入容易造成内存溢出。

整型数组中的每一元素用scanf函数不能一次性输完,必须循环输入进去,输完数字,按了一个回车,getchar获取的字符是‘\n'了,自然就停止输入了。

当然也不一定非要用getchar,如果只是达到变长数组的功能,尽管没有以上那么智能。

int arr[100];
int count;
scanf("%d",&count);
for(int i = 0; i < count;i++)
{
scanf("%d",&arr[i])
}

字符数组

对于字符数组,由于可以一口气输完,因此不需要循环。

针对scanf函数“不能”接收空格(输入I am a student失败),下文给出了两种解决方案。

1.scanf
	char str[1000] = { 0 };
	scanf("%[^\n]", str);

%[]在⽅括号中指定⼀组匹配的字符(⽐如 %[0-9] ),遇到不在集合之中的字符,匹配将会

停⽌。而%[^],则是遇到了^后的字符,匹配就会停止。
2.gets
	char str[1000] = { 0 };
	gets(str);

2.动态内存版 

关于动态内存的malloc等函数的讲解,由于篇幅原因,malloc函数太过强大,还可以实现二维数组的任意控制,我会专门开一期来讲。今天先摆结论

malloc实现一维动态数组p[m]

#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
int main(void)
{
    int m;
    scanf("%d", &m);
    int* const p = (int*)malloc(m*(sizeof(int)));
    memset(p, 0, m);//初始化,每个元素都为零
    int i;
 //赋值
     for (i=0;i<m; i++)
    {
        p[i] = i;
    }
//打印数组
    for (i = 0; i <m; i++)
    {
        printf("%d,", p[i]);
    }
    return 0;
}

 malloc实现二维动态数组p[m][n]

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
int main()
{
    int m, n;
    scanf("%d %d", &m,&n);
    int **p;
    p = (int**)malloc(m*(sizeof(int*)));//二级指针所在地址申请内存
    int i, j;
    for (i = 0; i<m; i++)
        p[i] = (int*)malloc(sizeof(int)*n);//一级指针所在地址申请内存
    for (i = 0; i < m; i++)
        for (j = 0; j < n; j++)
            p[i][j] = i + j;
    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            printf("%d %p   ", p[i][j], &p[i][j]);     //输出每个元素及地址,每行的列与列之间的地址时连续的,行与行之间的地址不连续  
        }
        printf("\n");
    }  
    return 0;
}

三.补充scanf与gets的讲解

1.scanf函数:

声明

scanf函数是从标准输入 stdin 读取格式化输入的函数,大白话:你想要自己给变量赋值,要用键盘输进去,就用scanf函数。它需要包含的头文件及函数原型是:

#define _CRT_SECURE_NO_WARNINGS 1  //vs环境下需要
#include <stdio.h>                 //头文件
int scanf(const char *format, ...);//函数原型

参数 

具体类型

scanf函数的参数是const char *format,是不是指针,是不是一个地址,所以千万要注意&的使用。你是不是想说你前面字符数组就没加啊。所以关于数组名,大家要记住数组名是是数组首元素的地址,因次,我才敢不加的!!!下面介绍几个容易混淆的输入

char arr[100];
scanf("%s",&arr);//ok,没问题,取数组地址就是数组首元素的地址
scanf("%s",arr);//0k,没问题,数组名就是数组首元素的地址
scanf("%s",&arr[6]);//ok,没问题,就是取数组第7个元素的地址嘛,从第七个元素开始输入
scanf("%s“,arr[6]);//戳啦,arr[6]是一个值,为int类型,我们要的是const*char

总之,就是要关注写的是不是一个指针变量,代表的是一个地址。 

 具体格式[=%[*][width][modifiers]type=]具体讲解如下:

参数描述
*这是一个可选的星号,表示数据是从流 stream 中读取的,但是可以被忽视,即它不存储在对应的参数中。
width这指定了在当前读取操作中读取的最大字符数。
modifiers为对应的附加参数所指向的数据指定一个不同于整型(针对 d、i 和 n)、无符号整型(针对 o、u 和 x)或浮点型(针对 e、f 和 g)的大小: h :短整型(针对 d、i 和 n),或无符号短整型(针对 o、u 和 x) l :长整型(针对 d、i 和 n),或无符号长整型(针对 o、u 和 x),或双精度型(针对 e、f 和 g) L :长双精度型(针对 e、f 和 g)
type一个字符,指定了要被读取的数据类型以及数据读取方式。具体参见下一个表格。

我们先讲常用参数的用法

	int year = 0,mouth = 0;
	scanf("%d%*c%d", &year, &mouth);
	printf("%d.%d", year, mouth);
//输入是2024/2
//输出是2024.2

 %*c,其实就相当于,你敲了,但计算机读取完后就丢了,输年月时,别人不空格隔开,就用/,*用*就可以解决他输其它字符的问题。

 %后正负号表示对齐方式,数字代表计算机最少打印多少字符,字符不够就用空格。.数字表示最后打印的是几位小数的意思。是向内,自己里面的形式。

现在开始讲type

合格的输入参数的类型
%a、%A读入一个浮点值(仅 C99 有效)。float *
%c单个字符:读取下一个字符。如果指定了一个不为 1 的宽度 width,函数会读取 width 个字符,并通过参数传递,把它们存储在数组中连续位置。在末尾不会追加空字符。char *
%d十进制整数:数字前面的 + 或 - 号是可选的。int *
%e、%E、%f、%F、%g、%G浮点数:包含了一个小数点、一个可选的前置符号 + 或 -、一个可选的后置字符 e 或 E,以及一个十进制数字。两个有效的实例 -732.103 和 7.12e4float *
%i读入十进制,八进制,十六进制整数 。int *
%o八进制整数。int *
%s字符串。这将读取连续字符,直到遇到一个空格字符(空格字符可以是空白、换行和制表符)。char *
%u无符号的十进制整数。unsigned int *
%x、%X十六进制整数。int *
%p读入一个指针 。
%[]扫描字符集合 。
%%读 % 符号。

 %[]前面已讲过,就不再赘述,这里在单独题一嘴的是%g,很智能,如果你要用%f打印,默认保留6位小数,就算限定格式,小数位数变了就要改,不智能,而%g自动缩去没用的0,特别方便!

返回值

scanf() 的返回值是⼀个整数,表⽰成功读取的变量个数。
如果没有读取任何项,或者匹配失败,则返回 0 。如果在成功读取任何数据之前,发⽣了读取错误或者遇到读取到⽂件结尾,则返回常量 EOF。
#include <stdio.h>
int main()
{
 int a = 0;
 int b = 0;
 float f = 0.0f;
 int r = scanf("%d %d %f", &a, &b, &f);
 printf("a=%d b=%d f=%f\n", a, b, f);
 printf("r = %d\n", r);
 return 0;
}

在VS环境中按3次 ctrl+z ,才结束了输⼊,我们可以看到r是2,表⽰正确读取了2个数值。
如果⼀个数字都不输⼊,直接按3次 ctrl+z ,输出的r是-1,也就是EOF

2.gets函数

声明

#include <stdio.h>
char *gets(char *str);

相较于scanf,gets就比较简洁了,从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。可以说是专门针对字符串的。

参数

str -- 这是指向一个字符数组的指针,该数组存储了 C 字符串。

char a[100];
gets(a);

现在再来看,括号里该写什么是不是更有感觉了~

返回值

如果成功,该函数返回 str。如果发生错误或者到达文件末尾时还未读取任何字符,则返回 NULL。

致谢

感谢大家的观看和支持,如有什么错误或不足,欢迎大家留言指正~~

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值