完全动态数组——数据结构基础

6 篇文章 0 订阅

简单成绩管理:

#include <stdio.h>
#include <stdlib.h>
int chec(int *p,int d)
{
	for(int i = 0;i < d;i++)
	{
		if (*(p+i) < 60)
		printf("学生%d不及格,成绩为%d\n",i+1,*(p+i));
	}
}
int main()
{
	int nDim1,i;
	int *parray;

	printf("输入一维长度:	");
	scanf("%d",&nDim1);

	parray = (int *)malloc(sizeof(int)*nDim1);
	for(i = 0;i < nDim1;i++)
	{
		printf("%d号学生成绩:	",i+1);
		scanf("%d",parray+i);
	}
	chec(parray,nDim1);
}

输入

输入一维长度:  6
1号学生成绩:   63
2号学生成绩:   80
3号学生成绩:   35
4号学生成绩:   78
5号学生成绩:   56
6号学生成绩:   55

输出

学生3不及格,成绩为35
学生5不及格,成绩为56
学生6不及格,成绩为55

updated 11/13/2019



正文

现在,我们有3种创建数组的方法。
  • 静态数组,常量表达式表维度,数组名访问数组元素
  • 变长数组(VLA),C99,变量表达式表维度,是一种动态数组
  • 完全动态数组(非官方),用malloc进行动态数组分配

  • 之所以C99加入变长数组,是因为FORTRAN语言(FORmulaTRANslator,公式翻译器)的计算库到C语言的转换的需要。 FORTRAN的子例程在没有经过调用之前,它不会被执行,这使得C中没有很好的适应FORTRAN这一特性的工具,它无法在调用时指定维度大小,所以C99引入了变长数组,但 实际上这有损C的简洁(个人意见)。 C一直以无多余的特性为傲,但变长数组类似数组却又不是完全动态的特性使它在C11中成为了C的可选项,因为它只能服务于科学计算。
    但是正如曾提到的,数组和malloc分配内存并不完全等同,数组是一种数据结构,但malloc所形成的也可以称之为数组,具有数组的性质。
    一般来说,由C/C++编译的程序会在运行的时候在内存中占用一些空间,它们分为以下几个部分: 1.二进制代码区 不必过多解释了,就是放二进制代码的地方。 2.常量区 存放文字字符串和常量 3.静态存储区 存放静态和全局变量 4.堆空间 动态内存区,程序员可控制分配和释放的区域。 5.栈空间 由编译器分配内存用于存储函数参数和普通变量。

    malloc能操作的是程序中的堆空间,而普通的数组则是存放在栈空间里面的。堆空间是系统内存中的可用区域,和普遍意义上的“堆(Heap)”不同,基本上可以看作是由空闲内存组成的大链表。

    [ ]运算符是C语言几乎最高优先级的运算符。[ ]运算符需要两个操作数,一个指针类型,一个整数,这样编译器会返回 *(a+i) 的值。
    a[11]等同于

    int *temp;
    temp=a+11;
    return *temp;
    

    因为我们知道temp的大小是int,所以返回的是第11个被当成int 对待的字节。
    考虑以下代码:

    #include <stdio.h>
    int main()
    {
    	int i[3] = {5,8,9};
    	double m[3] = {1,6,9};
    	printf("%p %p %d\n",i,i+1,i+1-i);
    	printf("%p %p %d",m,m+1,m+1-m);
    }
    

    输出:

    0060FEF4 0060FEF8 1
    0060FED8 0060FEE0 1
    

    可见i+1和i的地址差4位,而m+1和m地址差8位,但是它们的下标只差1位,因为下标是数据的个数的表现,而非数据的地址,而在右边的常量表达式已经经计算得到了常量值,便失去了函数映射的特性。

    所以当malloc指定数据类型指针时,它是有下标的;虽然p+1可以解析为p[1]意为下一个int指针类型,如果是仅仅加1的地址的话将找不到合适的同样大小的数据类型,故指针的+1和普通变量的+1是不同的,指针的目的是指向地址,它有地址大小的说明。
    指针的增减是将指针移动到指针的前一个或后一个元素。
    有如下代码:

    #include <stdio.h>
    
    int main()
    {
    	int i = 3;
    	int *p = &i;
    	printf("%p %p\n",p,p+1);
    	printf("%p %p\n",&p,&p+1);
    	printf("%d %d",*p,*(p+1));
    }
    

    输出:

    0060FEFC 0060FF00
    0060FEF8 0060FEFC
    3 7
    

    可知i地址为0060FEF8,p地址为0060FEFC,p+1地址为 0060FF00,为依次申请得到,当取p中的i的地址时,它加1得到一个地址值即p的地址,因为p是在i申请后申请的int大小的指针类型,所以&p+1为0060FEFC即p的地址。
    可知,地址我们是无法根据字节数加以修改的——我们总是按元素数进行解释。sizeof之所以是基本运算符也是因为字节的大小是指针即地址这种变换的关键。
    malloc分配有数据类型的内存,其指针也会按此特性处理,所以构成动态数组。

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	int nDim1,i;
    	int *parray;
    
    	printf("输入一维长度:	");
    	scanf("%d",&nDim1);
    
    	parray = (int *)malloc(sizeof(int)*nDim1);
    	for(i = 0;i < nDim1;i++)
    	{
    		*(parray+i) = i;
    	}
    	for(int k = 0;k < nDim1;k++)
    	{
    		printf("%d",parray[k]);
    	}
    }
    

    [ ]运算符是C语言几乎最高优先级的运算符。[ ]运算符需要两个操作数,一个指针类型,一个整数,这样编译器会返回 *(a+i) 的值。所以,数组下标是数组某个元素的反应,也是地址在内存中的表现形式。
    所以输出为:

    杈撳叆涓€缁撮暱搴︼細  9
    012345678
    

    gcc对中文支持的不好,我是没有什么办法的

    gcc 8.04.5.c -finput-charset=gbk -fexec-charset=gbk
    
    cc1.exe: error: failure to convert gbk to UTF-8
    
    chcp 65001
    

    “chcp 65001”命令行转为UTF-8解码格式解决问题

    输入一维长度:  8
    01234567
    

    updated 11/13/2019 17:02

    #include <stdio.h>
    int main()
    {
    	char  s[20]= "programming", *ps=s;
    	ps+=2;
    	printf("%s",*ps);
    	printf("Success!");
    }
    

    无报错无值。*ps是取值会得到字符’o’,而打印类型是字符串遇到空字符结束,故无法成功结束。

    #include <stdio.h>
    int main()
    {
    	char  s[20]= "programming", *ps=s;
    	ps+=2;
    	printf("%c",*ps);
    	printf("Success!");
    }
    

    输出:

    oSuccess!
    

    高级成绩管理:
    []确实比不断引用指针来的方便,提高了工作效率。

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    	int d1,d2;
    	int **stu,i,j;
    	puts("输入学生个数:");
    	scanf("%d",&d1);
    	puts("输入科目数:");
    	scanf("%d",&d2);
    	d2++;
    	stu = (int**)malloc(d1*sizeof(int *));
    	for(i = 0;i < d1;i++)
    	{
    		stu[i] = (int*)malloc(d2*sizeof(int));
    		printf("请输入%d同学学号:",i+1);
    		scanf("%d",&stu[i][0]);
    		for(j = 1;j < d2;j++)
    		{
    			printf("请输入科目%d成绩:",j);
    			scanf("%d",&stu[i][j]);
    		}
    	}
    	for(i = 0;i < d1;i++)
    	{
    		printf("%d同学学号%d\n",i+1,stu[i][0]);
    		for(j = 1;j < d2;j++)
    		{
    			printf("科目%d成绩:  %4d\n",j,stu[i][j]);
    		}
    		printf("\n");
    	}
    }
    

    在这里我大量运用了[]操作符。

    updated 11/14/2019
    有点类似于杂记……
    链表和数组有很大不同,它可以形容为结构体的数组,但这个结构体往往可以进行动态分配,其应该称之为“结构体的动态数组”(malloc方法)。
    链表的指针指向和int数组的被指针指向是相似的,可以通过方法拥有不同的性质,但链表不能应用p+i或者p[i]等方法,除非构建真实结构体数组,而链表结点之间的关系是用next指针进行联系的,其动态分配于堆内存而不是栈内存,存储结构并不相邻,无法应用p+i等形式,会走到错误的内存区。
    有如下程序:

    #include <stdio.h>
    struct kkk
    {
    	int i;
    	int d;
    }*kk;
    int main()
    {
    	struct kkk k = {1,8};
    	kk = &k;
    	printf("%d",*kk);
    }
    

    其输出为1。
    虽然如此,结构体程序仍看做一块看待,如果将kk改为(kk++)便会出现问题。

    updated in 11/17/2019
    今天有代码如下:

    	p faw;
    	faw->next = a.head;
    

    p是一个结构体:

    struct xx{
    	int xh;
    	char xm[10];
    	struct xx * next;
    };
    typedef struct xx *p,node;
    

    然后这个程序是错的。
    改成这样就对了:

    	p faw = (p)malloc(sizeof(node));
    	faw->next = a.head;
    

    因为指针是没有实际地址的,无论是什么样的指针,其只占四字节的空间。typedef struct xx *p是宣定p可以指向一个struct xx即node结构的地址,但p必须指向这个地址才有实际的成员变量。
    假设代码为以下:

    	p faw;
    	printf("%p\n",faw);
    	faw = (p)malloc(sizeof(node));
    	printf("%p",faw);
    

    输出:

    00000000
    009E0D70
    

    可称,一个是初始化的指针;一个是未初始化的指针。faw没指向任何地址时,默认其“值”为0,地址值&faw谁都有。指针是可以理解为变量的一种引用形式,但它绝不可当作真正的变量,尤其是未初始化时。指针的值仅仅是它们所指向对象的地址。其他的需要我们去具体阐释。
    同时,double类型或结构类型,指针只读一字节地址(PC和Macintosh等很多计算机都有按字节编址的规则),较大对象只会返回该对象第一个字节的地址。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值