LinuxC应用开发学习笔记(三)--C语言

函数

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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值