函数
1、函数的定义
数据类型 函数名 (【数据类型 变量名 】)
int i;
在main函数中,argc表示传递多少个参数,argv 表示传递的列表 ,字符指针数组的首地址
printf("hello!\n");//一个 进程的返回状态是给他的父进程看的,
printf("argc = %d!",argc);
for(i=0;i<=argc;i++)
{
puts(argv[i]);
}
return 0;//结束当前函数
2、函数的传参
值传递:不会改变互换的参数
地址传递
void swap(int *p,int *q)
{
int tmp;
tmp = *p;
*p = *q;
*q = tmp;
}
全局变量传参
3、函数的调用
函数的嵌套调用
递归:一个函数直接或者间接的调用自身
4、函数与数组
函数和一维数组
/*
*int *p = a;
*->a *a a[0] &a[3] p[i] p *p p+1
*
*
*->int* int int int* int int* int int*
*
*/
打印数组的数据
p[]等价一个指针 ,本质还是一个指针,形参。
void print_arr(int p[],int n);
传递只是传递的一维数组的起始地址,因此要用指针偏移打印。
void print_arr(int *p,int n)
{
for(int i=0;i<=n;i++)
{
printf("%d",*(p+i));
printf("\n");
}
}
交换指针数组的顺序
void func(int *p,int n)
{
int i = 0,m;
m = (n-1)/2;
for(;i <= m;i++)
{
int temp,j;
j = n-1-i;
temp = p[i];
p[i] = p[j];
p[j] = temp;
}
}
int a[] = {1,3,5,7,9};
for(int i=0;i<sizeof(a)/sizeof(*a);i++)
{
printf("%d ",a[i]);
}
func(a,sizeof(a)/sizeof(*a));
for(int i=0;i<sizeof(a)/sizeof(*a);i++)
{
printf("%d ",a[i]);
}
函数和二维数组
/*
*int a[M][N] = {};
*int *p = *a;
*int (*q)[N] = a;
*
*-> a[i][j] *(a+i)+j a[i]+j p[i] *p
*-> q[i][j] *q = *(q+0) q p+3 q+2
*
*-> int int * int * int int
*-> int int * int(*)[] int * int(*)[]
*/
打印二维数组
void print_double_arr(int *p,int n)
{
for(int i=0;i<n;i++)
{
printf("%4d ",p[i]);
}
printf("\n");
}
float average_score(int *p,int n)
{
float sum = 0;
for(int i=0;i<n;i++)
{
sum += p[i];
}
return sum/n;
}
#define M 2
#define N 5
//第一种表示方式
void print_double_arr1(int (*p)[N],int m,int n);
//第二种表示方式
void print_double_arr1(int p[][N],int m,int n)
{
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
//第一种打印方式
printf("%4d ",*(*(p+i)+j));
//第二种打印方式
printf("%4d ",p[i][j]);
printf("\n");
}
}
}
float average_score(int *p,int n)
{
float sum = 0;
for(int i=0;i<n;i++)
{
sum += p[i];
}
return sum/n;
}
打印固定的行的元素,注意什么时候传递行指针,什么时候传递列指针
void find_num(int (*p)[N],int num)
{
for(int i=0;i<N;i++)
{
printf("%4d",*(*(p+num)+i));
}
printf("\n");
}
第一种表示方式 ,将二维数组转换成为一维数组 p等价于(p+0)
void print_double_arr(int *p,int n) {
for(int i=0;i<n;i++)
{
printf("%4d ",p[i]);//第一种打印方式
}
printf("\n");
}
#define M 2
#define N 5
//void print_double_arr1(int (*p)[N],int m,int n);//第一种表示方式
void print_double_arr1(int p[][N],int m,int n) //第二种表示方式
{
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
// printf("%4d ",*(*(p+i)+j)); //第二种打印方式,以指针来遍历
printf("%4d ",p[i][j]); //第二种打印方式,以下标来遍历
}
}
}
函数与字符数组
char* mystrcpy(char *dest,const char *src)
{
char *ret = dest;
if((dest != NULL && src != NULL))
{
//使用循环实现把数值实现拷贝
while((*dest++ = *src++) != '\0')
{
}
}
return ret;
}
定义一个字符指针函数,返回值是char*类型
har* mystrncpy(char *dest,const char *src,size_t n)
{
int i;
for(i = 0;i<n&&(dest[i] = src[i]);i++);
dest[i] = '\0';//字符串结尾补上尾0
return dest;
}
char str1[] = "helloworld";
char str2[128];
mystrncpy(str2,str1,5);
puts(str2);
mystrcpy(str2,str1);
puts(str2);
5、函数与指针
指针函数
返回值 *函数名 (形参)
如:int * fun(int);
指针函数:返回的是一个指针
int * find_num1(int (*p)[N],int num)
{
if(num > M-1)
{
return NULL;
}
return *(p + num);
}
指针函数:返回的是一个指针,指针所指向的是一个入口地址
int a[2][5] = {1,3,5,7,9,2,4,6,8,10};
int num = 1;
int *res;
res = find_num1(a,num);
if(res != NULL)
{
for(int i=0;i<N;i++)
{
printf("%4d ",res[i]);
}
}
else
{
printf("can not find\n");
}
函数指针
类型 (*指针名)(形参);
如:int (*p)(int);
函数指针:函数名只是一段代码关联的入口地址
int add(int a,int b)
{
return a + b;
}
int sub(int a,int b)
{
return a - b;
}
int a = 3,b = 5;
int ret;
//指向函数的指针,与指向函数的类型相匹配,函数名仅仅是一个入口地址。
int (*p)(int,int);
int (*q)(int,int);
p = add;
q = sub;
ret = p(a,b);
printf("%4d ",ret);
ret = q(a,b);
printf("%4d ",ret);
函数指针数组
类型 (*数组名 【下标】)(形参)
如:int(*arrN);//数组,数组中有N个元素,这N个元素都是指向函数的指针
int (*funcp[2])(int ,int );
函数指针数组:数组,每一项都是指针,指向函数
int a = 3,b = 5;
int ret;
int (*funcp[2])(int ,int);
funcp[0] = add;
funcp[1] = sub;
for(int i = 0;i<2;i++)
{
ret = funcp[i](a,b);
printf("%d\n",ret);
}
八、构造类型
1、结构体
(1)产生的原因和意义
存放多种不同类型变量。
(2)类型的描述
struct 结构体名字
{
数据类型 成员1;//结构体的类型名字是不占用存储空间的,因此不能直接初始化。
数据类型 成员2;
…
};//分号一定不能丢。
(3)嵌套定义
struct simp_st
{
int i;
char ch;
float f;
};
struct birthday_st
{
int year;
int month;
int day;
};
struct student_st
{
int id;
char name[NAMESIZE];
struct birthday_st birth;
int math;
int chinese;
};
(4)定义变量(变量、数组、指针),初始化以及成员引用
成员引用:变量名.成员名
指针->成员名
(*指针).成员名
结构体的初始化
struct simp_st a = {123,456.789,'a'};
printf("%d %f %c\n",a.i,a.f,a.ch);
struct student_st stu = {10011,"Alal",{2011,11,11},98,97};
结构体的指针传参:
struct student_st *p = &stu;//占用一个指针大小
struct student_st arr[2] ={{10011,"Alal",{2011,11,11},98,97},{10012,"Jlal",{2012,12,12},91,92}};
p = &arr[0];
// struct student_st stu = {.math = 94,.chinese = 95};
printf("%d %s %d-%d-%d %d %d\n",stu.id,stu.name,stu.birth.year,stu.birth.month,stu.birth.day,stu.math,stu.chinese);
for(int i=0;i<2;i++,p++) //p = p+1 跳跃一个指针的大小
{
printf("%d %s %d-%d-%d %d %d \n",p->id,p->name,p->birth.year,p->birth.month,p->birth.day,p->math,p->chinese);
}
(5)占用的字节大小
根据是否会字节对齐,如果字节对齐,那么占用字节数为对齐字节之后的大小,否则,是各个变量的大小的总和。比如:
struct simp_st
{
int i;
char ch;
//char ch1;//占用 12字节
float f;
//char ch2;//占用16字节
};字节对齐占用 12个字节,不对齐占用9个字节
(6)函数传参(值,地址)
值传递:耗费地址空间
地址传递:把变量的指针传过去,消耗的是一个指针的开销。
//传参的对内存而言开销非常大,可以选择使用指针传参。
void func(struct simp_st *b){
printf("%d \n",sizeof(b));
}
struct simp_st a;
struct simp_st *p = &a;
printf("sizeof(p) = %d \n",sizeof(p));
printf("sizeof(struct) = %d \n",sizeof(a)); //结构体对齐 address%sizeof
func(p);
2、共用体
(1)产生的原因和意义
多个成员共同存在,共用同一块内存空间根据所占内存最大的分配,只有一个成员是有效的。
(2)类型描述
union 共用体名
{
数据类型 成员名1;
数据类型 成员名2;
…
};
(3)嵌套定义
struct
{
int i;
char ch;
union
{
int a;
char c;
}un;
float f;
};
union
{
int a;
double d;
struct
{
int arr[10];
float f;
}un;
float f;
};
(4)定义变量(变量、数组、指针),初始化以及成员引用
成员引用:变量名.成员名
指针->成员名
(*指针).成员名
(5)占用内存大小
共用同一块内存空间根据所占内存最大的分配。
结构体和共用体嵌套得到32位的低16位和高16位的和 :巧妙
union //定义形式
{
struct
{
uint16_t i;
uint16_t j;
}x;
uint32_t y;
}a;//定义形式
a.y = 0x11223344;
printf("%x \n",a.x.i+a.x.j);
(6)函数传参(值传递、地址传递)
地址传参节约开销。
(7)位域
union
{
struct
{
char a:1; //a = 0 1
char b:2; //b = 00 01 10 11
char c:3; //c = 0 1
}x;
int y;
}x;
3、枚举类型
enum 标识符
{
成员1:;
成员2:;
...
};
enum //宏定义预处理之后会被替换掉,而枚举类型在预处理之后不会被宏替换掉,就是很多的宏的集合
{
STA_RUNNING = 1,
STA_CANCELED,
STA_OVER
};
enum day a = SAT;
printf("%d\n",a);
struct job_st job1;
/*获取任务状态*/
switch (job1.state)
{
case STA_RUNNING:
//TODO
break;
case STA_CANCELED:
//TODO
break;
case STA_OVER:
//TODO
break;
default:
//TODO
break;
}
九、动态内存管理
auto的分配都是在栈上的,malloc可以在堆上分配。
malloc 和free最好同一个函数,或者同一个模块当中
原则:谁申请,谁释放。
malloc
calloc //连续申请N块size内存的空间
realloc //重新为我分配一块内存空间,原来的空间往下继续扩展
free
int *p = NULL;
p = (int *)malloc(sizeof(int));
if(p == NULL)
{
printf("malloc() error!\n");
return 0;
}
*p = 10;
printf("%d \n",*p);
free(p);
typedef 的使用,取别名。
//typedef int INT;
INT i; --> int i;
#define IP int*
typedef int *IP;
IP p,q; --> int *p,q; //一个整型指针变量和一个整型变量
IP p,q; --> int *p,*q; //定义了两个指针
typedef int ARR[6]; --> int [6] ->ARR;
ARR a; --> int a[6]; //一维数组里面包含一些整型元素
typedef struct node_st NODE; //改名已有的数据类型
NODE a; --> struct node_st a;
typedef struct node_st *NODEP;
NODEP p;-->struct node_st *p;
//给结构体变量改名
typedef struct
{
int i;
float f;
}NODE,*NODEP;//取别名
typedef int FUNC(int); -> int (int) FUNC;
//相当于定义了
FUNC f; --> int f(int);
typedef int *FUNCP(int);
//相当于定义了 如下的指针函数
FUNC p; --> int *p(int);
//指向函数的指针,指向指针函数的指针变量
typedef int *(*FUNCP)(int);
FUNCP p; --> int *(*p)(int);
练习:微型学生管理系统
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "stdint.h"
#include "malloc.h"
#define NAMESIZE 32
#define NAMEMAX 1024
#define INT int
struct
{
int id;
char name[NAMESIZE];
int math;
int chinese;
}STU;
void stu_set(STU *p,const STU *q)
{
//*p = *q;//结构体成员之间可以用等号直接赋值
//p->name = "Alan";//错误的赋值方式,因为等号左边是一个常量。
p->id = q->id;
p->name = malloc(strlen(q->name)+1);
strcpy(p->name,q->name);
p->math = q->math;
p->chinese = q->chinese;
}
void stu_show(STU *p)
{
printf("%d %s %d %d \n",p->id,p->name,p->math,p->chinese);
}
void stu_changename(STU *p,const char *newname)
{
free(p->name);
p->name = malloc(strlen(newname)+1);
strcpy(p->name,newname);
}
void menu(void)
{
printf("1 set\n2 change name\n3 show \n");
printf("please enter the num:\n");
}
int main()
{
STU stu,tmp;
char *newname;
int choice;
int ret;
do{
menu();
ret = scanf("%d",&choice);
if(ret!=1)
break;
switch (choice)
{
case 1:
tmp.name = malloc(NAMEMAX);
printf("please enter for stu[id name math chinese];");
scanf("%d %s %d %d",&tmp.id,tmp.name,&tmp.math,&tmp.chinese);
stu_set(&stu,&tmp);
break;
case 2:
stu_show(&stu);
printf("please enter for NEW stu[id name math chinese];");
scanf("%s",newname);
stu_changename(&stu,newname);
break;
case 3:
stu_show(&stu);
break;
default:
//TODO
break;
}
}while(1);
return 0;
}