稍微简单的复杂数据类型:数组
计算机程序处理的对象是数据信息(计算机中的信息包括数据信息和控制信息),计算机内存可以存储大量信息,姑且可以认为只有存储在内存中的数据信息才是可
以使用的。内存由操作系统管理,程序要使用内存存储空间需要向操作系统申请并被成功分配后才可以使用,C语言提供的变量声明(定义)语句可以实现向操作系
统申请存储空间。
C语言提供的变量声明(定义)语句实现向操作系统申请存储区(存储空间),变量名有"两个代表",一是代表所申请到的存储区,一是代表存储区里面存放的数
据信息。数组声明(定义)语句可以实现一次性向操作系统申请成倍的存储空间,数组名有"两个代表",一是代表所申请到的(整个)存储区,二是代表着获得的
成倍存储空间的首存储单元首地址。要注意的是:数组名根本不能代表所申请的全部存储区里面存放的数据信息。数组的"数组名"和函数的"函数名"非常类似,
数组名代表数组首元素的首地址,函数名代表函数中首条代码的地址(我们知道,程序语句可以分组,顶级分组就是函数)。
附:
/*
* 函数指针:指向函数的指针
*
* 函数名称代表函数本身整个存储区,函数名称还代表着函数中的第一条语句的地址。函数名取址是函数地址,是整个函数的地址。这和数组名称一样,数组名称代表数组本身的整个存储区,数组名称还代表数组第一个元素的地址。数组名取址是整个数组的地址。
*
* 函数地址。 从地址信息上看函数地址就是函数中第一条语句所在的地址。
*
* */
#include <stdio.h>
int main()
{
int add(int , int);
printf("add是%p\n", add);
printf("&add是%p\n", &add);
int (*p_f)(int, int);
p_f = add;
printf("p_f(1, 1)是%d\n", p_f(1, 1));
return 0;
}
int add( int value, int value1)
{
return value + value1;
}
关于数组的分析:
int integer_arr[100];
//当一次性成功申请了100倍的可以放整型数据的存储空间后,integer_arr一是代表所申请的整个存储区(看看
sizeof(integer_arr)就知道了),integer_arr再就是代表着获得的成倍存储空间的首存储单元的首地址(具体就
是&integer_arr[0])。
//因为integer_arr代表着整个存储区,所以&integer_arr是整个数组的地址(可以想象这个地址值是和integer_arr一样的,值都是&integer_arr[0])
分析:
integer_arr + 1和&integer_arr + 1的区别
函数
程序语句可以分组,分组可以一层又一层的进行下去,顶级分组就是函数。函数实现了对程序语句的分组。不同的函数通过函数名区分,函数名有"两个代表",一是代表整个函数所在代码区,二是代表函数所在代码区里的首条语句首地址。
函数分为函数头和函数体,函数头单独出现叫做函数声明。
注意:传统上C语言(C89)是不支持变长数组功能的,也就是说数组长度是在编译期间就确定下来的,不能在运行期改变。不过在C99标准中已经允许定义变长数组。然而,C99所允许的变长数组是使用是有限制的。
由于变长数组的长度是在编译时无从得知的,因此变长数组的存储空间位于栈中。
1 /* 2 * 数组初始化练习 3 * */ 4 5 #include <stdio.h> 6 int main() 7 { 8 //int arr[3] = {1, 2, 3}; 9 //int arr[3] = {1, 2, 3, 4, 5};//这种情况下,编译器编译会给出警告,同时编译器丢掉多余元素 10 //int arr[] = {1, 2, 3, 4, 5};//这种情况下,编译器自动计算数组元素个数然后成倍申请内存空间,然后给出初始值。 11 int arr[3] = {1};//编译器在这种情况下,会自动将其他未指明初始值的元素初始化为零 12 int loop = 0; 13 for (loop = 0; loop < 3; loop++) 14 printf("%d\t", arr[loop]); 15 putchar(‘\n‘); 16 17 return 0; 18 }
1 /* 2 * 二维数组练习 3 * 4 * 注意:多维数组从右到左依次是低维-高维 5 * */ 6 7 #include <stdio.h> 8 int main() 9 { 10 //int arr[3][4] ={{11, 12, 13, 14}, {21, 22, 23, 24}, {31, 32, 33, 34}}; 11 //int arr[3][4] ={{11, 12 }, {21, 22, 23, 24}, {31, 32, 33, 34}}; 12 //int arr[3][4] ={{11, 12 }};//部分初始化,那么,没有被明确给出初始化值的,编译器用零来自动补全其他未初始化元素 13 //int arr[][4] ={{11, 12 }, {21, 22, 23, 24}, {31, 32, 33, 34}};//不一定非要指明最高维度的数组长度,编译器可以根据初始化表来算出 14 int arr[3][4] ={11, 12, 31, 32, 33, 34};//这种情况的初始化表并没有指明某一维度数组初始化哪个数值,所以编译器读取初始化表,然后逐一赋值给arr[0][0],arr[0][1], arr[0][2],...,arr[1][0], arr[1][1],... 15 //int arr[3, 4]//错误,gcc编译器报错 16 //int arr[12/4][4]//正确,[]中可以出现表达式 17 int row = 0, col = 0; 18 19 for (row = 0; row < 3; row++) { 20 for (col = 0;col < 4; col++) 21 printf("%d ", arr[row][col]); 22 putchar(‘\n‘); 23 } 24 putchar(‘\n‘); 25 26 }
1 /* 2 * 变长数组 3 * 注意:传统上C语言(C89)是不支持变长数组功能的,也就是说数组长度是在编译期间就确定下来的,不能在运行期改变。变长数组的长度是在编译时无从得知的。 4 * */ 5 6 #include <stdio.h> 7 8 int main() 9 { 10 int students; 11 unsigned i = 0; 12 printf("请输入学生人数:"); 13 scanf("%d", &students); 14 float scores[students]; 15 printf("students是%d\nsizeof(scores)是%d\n", students, sizeof(scores)); 16 17 printf("请输入学生人数:"); 18 scanf("%d", &students); 19 printf("students是%d\nsizeof(scores)是%d\n", students, sizeof(scores)); 20 21 22 for (i = 0;i < students;i++) 23 scanf("%f", &scores[i]); 24 for (i = 0; i < students; i++) 25 printf("%f\t", scores[i]); 26 printf("students是%d\nsizeof(scores)是%d\n", students, sizeof(scores)); 27 putchar(‘\n‘); 28 return 0; 29 30 }
函数
程序语句可以分组,分组可以一层又一层的进行下去,顶级分组就是函数。函数实现了对程序语句的分组。不同的函数通过函数名区分,函数名有"两个代表",一是代表整个函数所在代码区,二是代表函数所在代码区里的首条语句首地址。
函数分为函数头和函数体,函数头单独出现叫做函数声明。
函数中的return;语句主要功能在于立即结束函数的执行,她的附带作用是想主调函数传送一个返回值。也就是说,函数在执行过程中将会在遇到
return语句或者遇到函数右大括号时候立即结束函数执行。需要注意的是,当由return语句传送一个返回值时候必须在函数头中指明返回值类型。
注意: return;//立即停止执行当前函数执行,返回主调函数。
exit(0);//功能执行比return更强硬,exit();语句立即终止程序执行,将参数返回给操作系统,相当于位于main函数的return语句。exit()函数在头文件stdllib.h里面。
主调函数的函数调用语句的函数参数称为"实际参数",简称"实参";被调函数函数定义时定义的参数变量称为"形式参数",简称"形参"。C语言实参向形参只进行"值传递"。
C语言源程序中函数的声明应避免依赖编译器的隐式声明。我们应该在源程序的开头把文件中所有函数的声明都列出来。否则有可能在编译时候导致编译器进行隐式声明,这种声明方式可能引起问题,我们应该在源程序中避免编译器的隐式声明。
1 /* 2 * 函数练习 - 隐式函数声明 3 * 4 * 以下源程序是可以编译通过的,因为gcc聪明了,当她编译到语句value = add(2, 3)时,自行假定了add函数声明是int add();这叫做"隐式声明"。 5 * 6 * 正因存在隐式声明int add();所以编译就通过了。但是,若实际的add函数是void add();那么gcc的假定就是错误的,所以编译会报错 7 8 C语言源程序中函数的声明应避免依赖编译器的隐式声明。我们应该在源程序的开头把文件中所有函数的声明都列出来。否则有可能在编译时候导致编译器进行隐式声明,这种声明方式可能引起问题,我们应该在源程序中避免编译器的隐式声明。 9 * */ 10 11 #include <stdio.h> 12 13 14 void show(int value) 15 {//output 16 printf("计算结果是"); 17 printf("%d\n", value); 18 19 return ; 20 } 21 22 int main() 23 { 24 int value = 0; 25 value = add(2, 3); 26 show(value); 27 28 return 0; 29 } 30 31 32 int add(int value1, int value2) 33 {//add 34 return value1 + value2; 35 }
1 /* 2 * 函数练习 3 * 编辑read函数和show函数,show从键盘读入一个整数,由show函数输出 4 * */ 5 6 #include <stdio.h> 7 8 int read(); 9 void show(int);//该语句书写为void show();编译也可以通过,函数声明语句可以供编译器编译本源程序时候使用 10 11 int main() 12 { 13 int value = 0; 14 value = read(); 15 show(value); 16 17 return 0; 18 } 19 20 int read() 21 { 22 int value; 23 printf("请输入一个整数:"); 24 while (!scanf("%d", &value)){ 25 scanf("%*[^\n]"); 26 scanf("%*c"); 27 printf("程序未能从输入流中提取到整数,请再次输入一个整数:"); 28 } 29 scanf("%*[^\n]"); 30 scanf("%*c"); 31 return value; 32 } 33 34 void show(int value) 35 { 36 37 //fflush(stdout)可以把输出缓冲区中的所有内容立刻显示在屏幕上 38 printf("%d\n", value); 39 40 return ; 41 }
1 /* 2 * 汉诺塔问题 3 * */ 4 5 #include <stdio.h> 6 7 unsigned han_nuo_ta(int value, char a, char b, char c); 8 main() 9 { 10 int value = 0; 11 char a = ‘A‘, b = ‘B‘, c = ‘C‘; 12 scanf("%d", &value); 13 han_nuo_ta(value, a, b, c); 14 } 15 16 unsigned han_nuo_ta(int value, char a, char b, char c) 17 { 18 static unsigned counter = 0; 19 if (value != 1) { 20 han_nuo_ta(value - 1, a, c, b); 21 printf("第%d步:%d盘子%c -> %c\n", ++counter, value, a, c); 22 han_nuo_ta(value - 1, b, a, c); 23 return counter; 24 } 25 else 26 { 27 printf("第%d步:%d盘子%c -> %c\n", ++counter, value, a, c); 28 return counter; 29 } 30 }
1 /* 2 * 函数练习 3 * */ 4 5 #include <stdio.h> 6 7 void func() 8 { 9 printf("abc\n"); 10 //return;//立即停止执行当前函数执行,返回主调函数。 11 //exit(0);//功能执行比return更强硬,exit();语句立即终止程序执行,将参数返回给操作系统,相当于位于main函数的return语句。exit()函数在头文件stdllib.h里面。 12 printf("def\n"); 13 14 return ; 15 } 16 17 int main() 18 { 19 func(); 20 printf("xyz\n"); 21 22 return 0; 23 }