0
点赞
收藏
分享

微信扫一扫

C语言【进阶】付费知识(上)

水沐由之 2022-05-25 阅读 1

告知:使用C语言的7个步骤

请添加图片描述

  1. 第1步:定义程序的目标

    在动手写程序之前,要在脑中有清晰的思路。想要程序去做什么首先自己要明确自己想做什么,思考你的程序需要哪些信息,要进行哪些计算和控制,以及程序应该要报告什么信息。在这一步骤中,不涉及具体的计算机语言,应该用一般术语来描述问题。

  2. 第2步:设计程序

    对程序应该完成什么任务有概念性的认识后,就应该考虑如何用程序来完成它。例如,用户界面应该是怎样的?如何组织程序?目标用户是谁?准备花多长时间来完成这个程序?

    除此之外,还要决定在程序(还可能是辅助文件)中如何表示数据,以及用什么方法处理数据。学习C语言之初,遇到的问题都很简单,没什么可选的。但是,随着要处理的情况越来越复杂,需要决策和考虑的方面也越来越多。通常,选择一个合适的方式表示信息可以更容易地设计程序和处理数据。

    再次强调,应该用一般术语来描述问题,而不是用具体的代码。但是,你的某些决策可能取决于语言的特性。例如,在数据表示方面,C的程序员就比Pascal的程序员有更多选择。

  3. 第3步:编写代码

    设计好程序后,就可以编写代码来实现它。也就是说,把你设计的程序翻译成 C语言。这里是真正需要使用C语言的地方。可以把思路写在纸上,但是最终还是要把代码输入计算机。这个过程的机制取决于编程环境,我们稍后会详细介绍一些常见的环境。一般而言,使用文本编辑器创建源代码文件。该文件中内容就是你翻译的C语言代码。程序清单1.1是一个C源代码的示例。

#include <stdio.h>
int main(void)
{
int dogs;
printf("How many dogs do you have?\n");
scanf("%d", &dogs);
printf("So you have %d dog(s)!\n", dogs);
return 0;
}

在这一步骤中,应该给自己编写的程序添加文字注释。最简单的方式是使用 C的注释工具在源代码中加入对代码的解释。

第4步:编译
接下来的这一步是编译源代码。再次注意,编译的细节取决于编程的环境,前面介绍过,编译器是把源代码转换成可执行代码的程序。可执行代码是用计算机的机器语言表示的代码。这种语言由数字码表示的指令组成。如前所述,不同的计算机使用不同的机器语言方案。C 编译器负责把C代码翻译成特定的机器语言。此外,C编译器还将源代码与C库(库中包含大量的标准函数供用户使用,如printf()和scanf())的代码合并成最终的程序(更精确地说,应该是由一个被称为链接器的程序来链接库函数,但是在大多数系统中,编译器运行链接器)。其结果是,生成一个用户可以运行的可执行文件,其中包含着计算机能理解的代码。
编译器还会检查C语言程序是否有效。如果C编译器发现错误,就不生成可执行文件并报错。理解特定编译器报告的错误或警告信息是程序员要掌握的另一项技能。
第5步:运行程序
传统上,可执行文件是可运行的程序。在(常端模式和Linux终端模式)中运行程序要输入可执行文件的文件名,而其他环境可能要运行命令(如,在VAX中的VMS[2])或一些其他机制。例如,在Windows和Macintosh提供的集成开发环境(IDE)中,用户可以在IDE中通过选择菜单中的选项或按下特殊键来编辑和执行C程序。最终生成的程序可通过单击或双击文件名或图标直接在操作系统中运行。
第6步:测试和调试程序
程序能运行是个好迹象,但有时也可能会出现运行错误。接下来,应该检查程序是否按照你所设计的思路运行。你会发现你的程序中有一些错误,计算机行话叫作bug。查找并修复程序错误的过程叫调试。学习的过程中不可避免会犯错,学习编程也是如此。因此,当你把所学的知识应用于编程时,最好为自己会犯错做好心理准备。
第7步:维护和修改代码
创建完程序后,你发现程序有错,或者想扩展程序的用途,这时就要修改程序。例如,用户输入以Zz开头的姓名时程序出现错误、你想到了一个更好的解决方案、想添加一个更好的新特性,或者要修改程序使其能在不同的计算机系统中运行,等等。如果在编写程序时清楚地做了注释并采用了合理的设计方案,这些事情都很简单。

最简单的C代码

int main(void){
    return 0;
}

判断浮点数变量x的值是否为零

if(|x-0.000001|<=0.000001)
    printf("零")else
    printf("不是零")

逗号表达式

请添加图片描述

求位

百分位:n/00;
十位:n/10%100(n-a*100)/10
个位:n%10
控制行号:if(++n%5==0)//五个为一行
printf("\n");

循环语句

看下面代码,你认为会打印多少句”hehe“?

  • for变种1
#include<stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	for(;i<10;i++)
	{
	
		for(;j<10;j++)
			printf("hehe\n");
	}

	return 0;
}
  • for变种2

分支语句

int day = 0;
int n = 1;
scanf("%d",&day);
switch(day)
{
    case 1+0://ok
        printf("星期一");
    case 1.0://error ,只能是整形常量
    case n://error
    
    break;//在最后的case语句加一个break,为以后添加case分支而好维护
}

if代码规范

int num = 4;
if(5 == num)//常量和变量比较时,常量因放在变量左边
//if(5 = num);//编译失败,直观错误
printf("hehe");

函数

实际参数(实参)

形式参数(形参)

时间函数

#include <stdio.h>
#include <time.h>

int main ()
{
    time_t rawtime;
    struct tm * timeinfo;
    
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
    printf ( "当前本地时间为: %s", asctime (timeinfo) );
    
    return 0;
}

函数的调用

#include<stdio.h>

void Swap2(int* pa,int* pb)
{

	int tmp = 0;
	tmp = *pa;
	*pa = *pb;
	*pb = tmp;

}
//void Swap1(int x,int y)
//{
//
//	int tmp  = 0;
//	tmp = x;
//	x = y;
//	y = x;
//}

int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a = %d b = %d\n",a,b);
	//Swap1(a, b);//传值调用
	Swap2(&a, &b);//传址调用
	printf("交换后:a = %d,b = %d\n",a,b);
	return 0;
}

传值调用:

函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参。

传址调用✅✅

  • 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
  • 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。

函数嵌套

#include<stdio.h>
int main()
{

	printf("%d",printf("%d",printf("%d",printf("%d",4))));//打印4111
	//因为最后一个printf打印4,倒数第二个printf打印调用printf输出的个数为1(包括空格),依次...详细看printf函数文档
	printf("%d",printf("%d",printf("%d",43))//4321
	return 0;
}

函数递归(递去归来)

递归的两个必要条件

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续
  • 每次递归调用之后越来越接近这个限制

错误使用 递归

#include<stdio.h>
int main()
{
    main();//err
    return 0;
}

正确使用递归

#include<stdio.h>

void print(int n)//依次打印1 2 3 4
{

	if(n>9)
		print(n/10);
	printf("%d ",n%10);
}
int main()
{

	int num = 0;
	scanf("%d",&num);//1234
	print(num);
}

回调函数

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//void qsort(void *base,	//base中存放的是待排序数据中的第一个对象的地址
//			size_t num,	//排序数据元素的个数
//			size_t size,//排序数据中一个元素的大小,单位是字节
//			int (*cmp)(const void*,const void*)//是用来比较待排序数据中的2个元素的(一个)函数	
//			);

int cmp_int(const void* e1,const void* e2)
{

	return *(int *)e1-*(int *)e2;//因为参数类型是void,我们用(*int)强制类型转换

/*返回值		意义
	<0			所指向的元素在所指向的元素之前p1p2
	0			指向的元素等效于 指向的元素p1p2
	>0			指向的元素位于 指向的元素之后p1p2
*/
}
void print(int arr[],int sz)
{

	int i= 0;
	for(i=0;i<sz;i++)
	{
	
		printf("%d ",arr[i]);
	}
	printf("\n");
}

void test1()
{
	//整形数据的排序
	int arr[]={9,8,7,6,5,4,3,2,1,0};
	int sz=sizeof(arr)/sizeof(arr[0]);
	//排序
	qsort(arr,sz,sizeof(arr[0]),cmp_int);
	//打印
	print(arr,sz);
}
struct Stu
{

	char name[20];
	int age;
};
int sort_by_age(const void* e1,const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;//转换结构体类型指针,e1指向age成员

}
void test2()
{
	//排序结构体数据
	struct Stu s[3]={ {"zhangsang",30},{"lisi",34},{"wangwu",20}};
	int sz=sizeof(s)/sizeof(s[0]);
	//按照年龄来排序
	
	qsort(s,sz,sizeof(s[0]),sort_by_age);
}

int sort_by_name(const void* e1,const void* e2)
{

	return strcmp(((struct Stu*)e1)->name ,((struct Stu*)e2)->name);
}
void test3()
{
	//排序结构体数据
	struct Stu s[3]={ {"zhangsang",30},{"lisi",34},{"wangwu",20}};
	int sz=sizeof(s)/sizeof(s[0]);
	//按照名字来排序
	
	qsort(s,sz,sizeof(s[0]),sort_by_name);
}


int main()
{
	//test1();
	//test2();
	test3();
	return 0;
}

数组

  • 数组的长度单位是常量表达式表示
  • 数组在内存中连续存放的,

请添加图片描述

int n =10;
int arr[n]={1,2,3,4,4,};//error

下标

指针

指针,间接访问和变量

请添加图片描述

#include<stdio.h>
int main()
{
	int a=7;
	int *d = &a;
	*d = 10-*d;//*d==a   --  a=10-a(7)
	//d= 10- *d;//err,因为d是个指针变量
	//整形变量不能存储一个指针变量

	*&a=*d;//说明变量a==*d,不建议这种表达式

	printf("a==%p \n",a);
	printf("*a==%p \n",*d);
	printf("d==%p\n",d);
	printf("&a==%p\n",&a);
	return 0;
}

函数指针

void test(int **p2)
{

	**p2=20;
	
}
int test_p(int x,int y)
{


	return x+y;
}
#include<stdio.h>
int main()
{

	int a =10;
	int * pa=&a;//pa是一级指针
	int **ppa=&pa;//ppa是二级指针
	//二级指针传参
	test(ppa);
	test(&pa);//传一级指针的地址

	int* arr[10]={0};
	test(arr);
	printf("%d ",a);

	//pf函数指针变量
	int (*pf)(int ,int )=&test_p;
	return 0;
}
int Add(int x,int y)
{

	return x+y;
}
#include<stdio.h>
int main()
{
	//int (*pf)(int,int )=&Add;
	int (*pf)(int,int )=Add;//Add  == pf

	//int ret=(Add)(3,5);//3
	//int ret=(*pf)(3,5)//1	;*是摆设,可以int ret=(*****pf)(3,5)结果依然正确
	//int ret=*pf(3,5)//err;优先级  pf会先和(3,5)结合,任何*对函数的返回值8解引用肯定会错

	int ret = pf(3,5);//2
	printf("%d ",ret);//
	return 0;
}

函数指针数组

int add(int x,int y)
{
	return x+y;
}
int sub(int x,int y)
{

	return x-y;
}

#include<stdio.h>
int main()
{
	int (*pf1)(int ,int)=add;
	int (*pf2)(int ,int)=sub;

	int (*pfarr[2])(int ,int )={add,sub};//pfarr就是函数指针数组,{}里可以是pf1,pf2,*注意是花括号
	//add函数和sub函数的形参因为一样,内存存放也是一样,而数组前提就是内存类型一样;
	//所以可以同数组存放到函数指针,故叫做函数指针数组。
	return 0;
}

函数指针数组的使用

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}

int Div(int x, int y)
{
	return x / y;
}

void menu()
{
	printf("**************************\n");
	printf("**** 1. add    2. sub ****\n");
	printf("**** 3. mul    4. div ****\n");
	printf("****     0. exit      ****\n");
	printf("**************************\n");
}

int main()
{
	int input = 0;
	//计算器-计算整型变量的加、减、乘、除
	//a&b a^b a|b a>>b a<<b a>b

	do {
		menu();
		
		int x = 0;
		int y = 0;
		int ret = 0;
		printf("请选择:>");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			printf("请输入2个操作数>:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("请输入2个操作数>:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("请输入2个操作数>:");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("请输入2个操作数>:");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
		
	} while (input);
	return 0;
}

优化后的代码:

#include<stdio.h>


int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}

int Div(int x, int y)
{
	return x / y;
}

void menu()
{
	printf("**************************\n");
	printf("**** 1. add    2. sub ****\n");
	printf("**** 3. mul    4. div ****\n");
	printf("****     0. exit      ****\n");
	printf("**************************\n");
}

int main()
{
	int input = 0;
	//计算器-计算整型变量的加、减、乘、除
	//a&b a^b a|b a>>b a<<b a>b

	do {
		menu();

		//pfArr就是函数指针数组
		//转移表 - 《C和指针》

		int (*pfArr[5])(int, int) = { NULL, Add, Sub, Mul, Div };
		int x = 0;
		int y = 0;
		int ret = 0;
		printf("请选择:>");
		scanf("%d", &input);//2

		if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数>:");
			scanf("%d %d", &x, &y);
			ret = (pfArr[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if(input == 0)
		{
			printf("退出程序\n");
			break;
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

回调函数_qsort函数的使用

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//void qsort(void *base,	//base中存放的是待排序数据中的第一个对象的地址
//			size_t num,	//排序数据元素的个数
//			size_t size,//排序数据中一个元素的大小,单位是字节
//			int (*cmp)(const void*,const void*)//是用来比较待排序数据中的2个元素的(一个)函数	
//			);

int cmp_int(const void* e1,const void* e2)
{

	return *(int *)e1-*(int *)e2;//因为参数类型是void,我们用(*int)强制类型转换

/*返回值		意义
	<0			所指向的元素在所指向的元素之前p1p2
	0			指向的元素等效于 指向的元素p1p2
	>0			指向的元素位于 指向的元素之后p1p2
*/
}
void print(int arr[],int sz)
{

	int i= 0;
	for(i=0;i<sz;i++)
	{
	
		printf("%d ",arr[i]);
	}
	printf("\n");
}

void test1()
{
	//整形数据的排序
	int arr[]={9,8,7,6,5,4,3,2,1,0};
	int sz=sizeof(arr)/sizeof(arr[0]);
	//排序
	qsort(arr,sz,sizeof(arr[0]),cmp_int);
	//打印
	print(arr,sz);
}
struct Stu
{

	char name[20];
	int age;
};
int sort_by_age(const void* e1,const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;//转换结构体类型指针,e1指向age成员

}
void test2()
{
	//排序结构体数据
	struct Stu s[3]={ {"zhangsang",30},{"lisi",34},{"wangwu",20}};
	int sz=sizeof(s)/sizeof(s[0]);
	//按照年龄来排序
	
	qsort(s,sz,sizeof(s[0]),sort_by_age);
}

int sort_by_name(const void* e1,const void* e2)
{

	return strcmp(((struct Stu*)e1)->name ,((struct Stu*)e2)->name);
}
void test3()
{
	//排序结构体数据
	struct Stu s[3]={ {"zhangsang",30},{"lisi",34},{"wangwu",20}};
	int sz=sizeof(s)/sizeof(s[0]);
	//按照名字来排序
	
	qsort(s,sz,sizeof(s[0]),sort_by_name);
}


int main()
{
	//test1();
	//test2();
	test3();
	return 0;
}

模仿qsort函数的通用算法

void Swap(char*buf1,char*buf2,int width)
{

	int i = 0;
	for(i=0;i<width;i++)
	{
	
		char tmp = *buf1;
		*buf1=*buf2;
		*buf2=tmp;
		buf1++;
		buf2++;
	}
}
//模仿qsort实现一个冒泡排序的通用算法
void bubbble_sort(void *base,
				int sz,	
				int width,
				int (*cmp)(const void*e1,const void*e2)
			)
{
	int i =0;
	for(i=0;i<sz-1;i++){
		int j = 0;
		for(j=0;j<sz-1-i;j++)
		{
			if(cmp((char*)base+j*width,(char *)base+(j+1)*width)>0)//(兼并)适合int类型和字符类型,如果是用int的话,那我用字符排序那字节+几就是跳过4个字节,单一
			{
			Swap((char*)base+j*width,(char*)base+(j+1)*width,width);

			}
		}
	}

}
举报

相关推荐

0 条评论