一、二维数组
什么是二维数组:
普通数组也叫一维数组,可以看作是若干个变量排成一排。 二维数组就是若干个row*col个变量排成的方阵。
定义二维数组:
类型 数组名[行数][列数]; int arr[3][5]; // 相当于定义了3行5列15个变量 [] [] [] [] [] [] [] [] [] [] [] [] [] [] []
访问二维数组元素:
数组名 [行下标][列下标]; 行下标范围:0 ~ 行数-1 列下标范围:0 ~ 列数-1
int arr[3][5]; // 15个变量的行下标和列下标 [0][0] [0][1] [0][2] [0][3] [0][4] [1][0] [1][1] [1][2] [1][3] [1][4] [2][0] [2][1] [2][2] [2][3] [2][4]
二维数组的遍历:
需要与双层for循环配合,外层循环变量当作数组的行下标,内层循环变量当作数组的列下标。
for(int r=0; r<3; r++) { for(int c=0; c<5; c++) { printf("%d ",arr[r][c]); } printf("\n"); }
二维数组初始化:
二维数组在初始化时,其它特点与一维数组相同,不同的是列数必须确定。
类型 数组名[行下标][列下标] = { {v00,v01,v02,...}, // 第一行 {v10,v11,v12,...}, // 第二行 ... };
三、变长数组
定长数组:
使用整数常量作为定义数组时的长度参数,或者使用初始化数据省略长度参数,由编译器统计初始化数据的数量设置数组的长度。
定长数组在编译时数组的长度就确定了,这种数组编译器可以帮我们进行初始化操作,为了避免数据不够使用所以在定义时一般要多定义点,可能会浪费一部分内存。
变长数组:
使用变量作为定义数组的长度参数,而程序不运行变量的值就无法确定,因此在编译时数组的长度就无法确定,编译器也就无法帮我进行初始化操作。 当程序运行起来后,在执行数组的定义语句前,长度变量都可以根据实际情况变化,当执行完数组的定义语句后,数组的长度就固定下来,不会再变了。
int len; printf("请输入数组的长度:"); scanf("%d",&len); int arr[len];
变长数组优缺点:
根据本次运行的实际情况,数组需要多长,就定义多长,每次运行都可以不同,避免内存浪费。
不能对变长数组进行初始化。
四、函数
什么是函数:
函数就是一段具有某项功能的代码,它是C语言中管理代码的最小单位,把具有某项功能的若干行代码封装在函数中方便管理代码且方便重复调用。
函数的分类:
标准库函数:
C语言标准委员会为C语言以函数形式提供了一些基础功能,这些函数被封装在libc.so库文件中,它们的详细介绍在man手册的第3章节。
系统函数:
操作系统为程序员提供了一些系统API,可以以函数形式调用,但它们不是真正的函数,讲UNIX系统环境编程时会详细讲解,它们的详细介绍在man手册的第2章节。
第三方库函数:
一些公司或开源组织实现的一些常用工具供程序员使用。
自定义函数:
为了更方便的管理、调用代码,降低开发难度,程序员自己封装的一些函数。
常用标准库函数介绍:
标准库中除上封装了函数,还提供一些头文件,里面是对函数的说明。
stdio.h 输入输出相关功能的函数:
int printf(const char *format, ...); 功能:输出数据到终端 format:提示信息+占位符+转义字符组成 ...:若干个变量名或数据 返回值:成功输出的字符个数 int scanf(const char *format, ...); 功能:从终端读取数据 format:一般情况下,只需要占位符即可,除了占位符以外的信息,在输入数据时要原样补出。 ...:若干个变量的地址 返回值:成功读取的变量个数
stdlib.h 实用的库函数:
int system(const char *command); 功能:调用系统命令,命令执行完成后,该函数才返回 返回值:成功返回0,失败返回-1。 void srand(unsigned int seed); 功能:所谓的随机数就是把所有整数打乱顺序,从某个位置获取,默认从1位置获取,程序运行时如果"位置"不改变,获取随机数与上次一样,为了保证每个运行时,提供的位置都发生变化,一般把time函数的返回值提供给srand作为随机数的种子。 int rand(void); 功能:从系统中获取随机数 返回值:都是正整数,如果需要负数或浮点数,需要程序员自已处理。 int abs(int j); 功能:计算并返回j的绝对值
ctype.h 字符类型的判断函数:
int isdigit( int ch ); 功能:判断是否是数字字符 int islower( int ch ); 功能:判断是否是小写字母 int isupper( int ch ); 功能:判断是否是大写字母
time.h 时间日期相关的函数:
time_t time(time_t *tloc); 功能:获取当前系统赶时间,返回自 1970年1月1日 00:00:00 到现在一共过了多少秒,格林时间+8小时就是北京时间。 struct tm *localtime(const time_t *timep); 功能:把秒数据时间转换成年月日、时分秒
math.h 数学相关的函数 :
double pow(double x, double y); 功能:计算出x的y次数 double sqrt(double x); 功能:计算x的平方根 double ceil(double x); 功能:向上取整,返回大于x的最小整数 double floor(double x); 功能:向下取整,返回小于x的最大整数 注意:使用这些函数在编译时必须有-lm参数
自定义函数:
有两情况适合把代码封装成自定义函数: 1、代码量过多,一般代码量超过50行就要考虑封装成函数,方便管理代码,提高代码的安全性(程序员平均每50行会出现一个BUG)。 2、如果一个代码需要在不同位置多次执行,为了防止出现代码冗余,就要把它封装成函数,方便重复使用,也能降低可执行文件的大小。
函数声明:
返回值类型 函数名(参数列表);
1、根据函数的功能为函数取名字,在Linux系统下函数名全部小写,多个单词用下划线分隔。 2、参数列表,指的是函数执行时,调用者需要传递它的数据,此时重点关注的是参数的类型,在函数声明时可以忽略参数的名,如果函数执行时不需要调用者传递数据则写void。 3、返回值类型,指的是函数的执行结果是什么类型的数据,如果函数没有返回值,则写void。 4、函数声明就是告诉编译器该函数的格式,方便编译器检查调用者的使用是否正确。
函数定义:
返回值类型 函数名(类型 参数名) { // 函数体 } // 如果函数的定义出现在调用之前,函数声明可以省略
函数调用:
函数名(实参);
1、调用者会把实参赋值给形参变量。 2、函数的返回值会放置在调用位置,可立即使用,也可用变量保存。
自定义函数要注意的问题:
1、函数的命名空间互相独立,函数之间传参是单向值传递(实参给形参赋值),所以两个函数之间不能通过传参共享变量。
void swap(int num1,int num2)
{
// 只是形参变量发生的交换,不会影响实参变量
printf("num1=%d num2=%d\n",num1,num2);
int tmp = num1;
num1 = num2;
num2 = tmp;
printf("num1=%d num2=%d\n",num1,num2);
}
int main(int argc,const char* argv[])
{
// 输入三个整数,按从小到大的顺序输出
int num1,num2,num3;
printf("请输入三个整数:");
scanf("%d%d%d",&num1,&num2,&num3);
if(num1 > num2)
swap(num1,num2);
if(num1 > num3)
swap(num1,num3);
if(num2 > num3)
swap(num2,num3);
printf("%d %d %d\n",num1,num2,num3);
return 0;
}
2、C语言中如果函数的参数列表是空的,则意味着该函数提供任意类型、多个参数都可以调用,容易给调用者造成误会,影响代码的可读性,如果函数执行时不需要调用者传递数据则参数列表要写void,不要空着。
// void func()
void func(void)
{
printf("我是函数func,我被调用了...!\n");
}
int main(int argc,const char* argv[])
{
func(2,3.14);
func(2,3.14,'A');
}
3、如果函数有返回值但没有写return语句,调用该函数时依然有返回值。 当调用一个有返回值的函数时,系统会为调用者和被调用者约定一个空间用于存储返回值,而return语句的作用就是把一个数据存储到这个空间,如果没有写return语句,调用者依然会从共用空间读取返回值,只是读取到的数据是随机的。
int func(void)
{
}
int main(int argc,const char* argv[])
{
printf("%d\n",func());
int num = 0;
num = func();
printf("%d\n",num);
}
gcc -Wall -Werror xxx.c 可以防止漏写return语句。
xxx.c:x:x: error: control reaches end of non-void function [-Werror=return-type]
4、当使用数组作为函数的参数传递时,它的长度信息就丢失了,(数组会蜕变成指针),无法使用sizeof计算数组的长度,需要调用者额外提供一个参数作为数组的长度。
void show_arr(int arr[],size_t len)
{
for(int i=0; i<len; i++)
{
printf("%d ",arr[i]);
}
}
int main(int argc,const char* argv[])
{
int arr[] = {1,2,3,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5};
show_arr(arr,sizeof(arr)/sizeof(arr[0]));
}
5、当使用二维数组作为函数的参数时,C语言规则定义二维数组时必须有列数,所以要行、列数在前,数组在后,并且把列数设置给数组。
void show_arr(int row,int col,int arr[][col])
{
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
int main(int argc,const char* argv[])
{
int arr[2][5] = {
{1,2,3,4,5},
{2,3,4,5,6}
};
show_arr(2,5,arr);
return 0;
}