首页 > 其他分享 >C语言学习Part2(1000-2000行代码)

C语言学习Part2(1000-2000行代码)

时间:2024-01-28 11:00:44浏览次数:25  
标签:arr 函数 int Part2 C语言 2000 数组 printf strlen

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//头文件,仅标注一次
e.g.猜数字游戏
//e.g.猜数字游戏
//1.电脑生成一个随机数
//2.猜数字
//3.循环玩

#include<stdlib.h>
#include<time.h>
void menu()
{
	printf("************************\n");
	printf("***1.play    0.exit  ***\n");
	printf("************************\n");
}

void game()
{
	//printf("猜数字\n");
	//1.生成一个随机数
	int ret = 0;
	int guess = 0;

	//rand()-->生成随机数0~32,767
	// rand_max=32,767(右键)转到定义-->    #define rand_max 0x7fff(十六进制)

	/*时间戳
	计算机当前的时间 - 计算机的起始时间(1970.01.01 08:00:00) = (xxxx)秒
	
	拿时间戳来设置随机数的生成起点:time()函数
	函数原型:time_t time(time_t* timer);
	time_t(类型)
		typedef __time64_t time_t;
		typedef __int64                       __time64_t;
	
	*/	
	ret = rand()% 100 + 1;//但是这样写随机数顺序都一样
	//printf("%d\n", ret);

	//2.猜数字
	while (1)
	{
		printf("猜数字吧:>");
		scanf("%d", &guess);
		if (guess > ret)
			printf("猜大了\n");
		else if (guess < ret)
			printf("猜小了\n");
		else
		{
			printf("恭喜你,猜对了!\n");
			break;
		}
	}

}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	//随机数生成起点整个程序设置一次就可以,不需要频繁调用

	do {//游戏至少进行一次
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}

	} while (input);//不为0,就循环(nb)

	return 0;
}
goto语句

可以随意滥用的goto语句和标记跳转的标号

主要用途(常见用法):

终止程序在某些深度嵌套的结构的处理过程,例如一次跳出两层或多层循环

for(...)
	{
		for(...)
		{
			for(...)
			{
				if(disaster)
					goto error;
			}
		}
	}
	...
	error:
		if(disaster)
			...//处理错误情况
e.g.一个关机程序
#include<string.h>
#include<stdlib.h>

int main()
{
	char input[10] = { 0 };
	system("shutdown -s -t 60");//关机程序

	while (1)
	{
	again:
		printf("电脑将在1分钟内关机,如果输入:我是猪,"
			"就取消关机!\n请输入:>");
		scanf("%s", input);
		if (0 == strcmp(input, "我是猪"))
		{
			system("shutdown -a");//取消关机
			break;
		}
		else
		{
			goto again;//循环
		}
	}

	return 0;
}
函数

1.库函数

2.自定义函数

C语言常用库函数:

1)I/O函数

2)字符串操作函数

3)字符操作函数

4)内存操作函数

5)时间/日期函数

6)数字函数

7)其他...


char* strcpy(char* destination, char* source);

包含结束的'\0'也要拷贝

memory--内存;set--设置:

void* memset(void* ptr, int value, size_t num);

把ptr所指向的空间的前num个字节的内容设置成指定的value值


//例.memset()
#include<string.h>
int main()
{
	char arr[] = "hello world";
	memset(arr, '*', 5);
	printf("%s\12", arr);
	
	return 0;
}

C语言学习Part2(1000-2000行代码)_递归


学会如何使用库函数?

库函数查询工具:

  1. MSDN(Microsoft Developer Network)
  2. www.cplusplus.com
  3. http://en.cppreference.com
自定义函数

交换函数:

函数的参数:

1.实际参数(实参)

真实传递给函数的参数。

实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时

他们都必须有确定的值,以便传递给形参。

2.形式参数(形参)

函数名后括号中的变量。

因为形式参数中有在函数被调用的过程中才实例化(分配内存单元),所以叫形参。

形参当参数调用完成之后自动销毁,因此形参只在函数中有效。


当实参传递给形参时,形参实际是实参的一份临时拷贝(形参与实参毫无联系)

对形参的修改不会影响实参。


函数调用:

1.传值调用

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

2.传址调用

把函数外部创建的变量的内存地址传递给函数参数

这种传参方式可以让函数和函数外的变量建立起真正的联系,即函数内部可以直接操作函数外部的变量

3.区别

如果只想获取值(get_max()),选传值调用

如果想利用函数修改值,选传址调用


※数组传参传的是元素首地址

※本质上arr是个指针,因此在函数内部,sizeof(arr) = 1

//交换函数
void swap1(int a, int b)//传值调用
{//形参
	int temp = 0;
	temp = b;
	b = a;
	a = temp;
	printf("x = %d  y = %d\n", a, b);//5  10
}

void swap2(int* pa, int* pb)//传址调用
{
	int temp = 0;
	temp = *pa;
	*pa = *pb;
	*pb = temp;
}

int main()
{
	int x = 10;
	int y = 5;
	//swap1(x, y);//10  5
 
	swap2(&x, &y);//5  10
	//实参
  printf("x = %d  y = %d\n", x, y);
	return 0;
}
函数的嵌套调用与链式访问

函数与函数之间可以有机的结合

链式访问:把一个函数的返回值作为另一个函数的参数

int main()
{
	printf("%d", printf("%d", printf("%d", 43)));
	//printf()的返回值是打印字符的个数
	//2 43
	//1 2 43 -->4321
	return 0;
}
函数的声明与定义

int Add(int, int);//声明,适用于模块化编程


在Add.h头文件里声明函数Add();

在Add.c源文件里定义函数

在test.c源文件里调用#include"Add.h"


//Add.h
#pragma once
#ifndef __ADD_H__//函数名可换if not define add.h
#define __ADD_H__

int Add(int, int);
#endif // !

/*
如果没有定义过,就包含这个头文件了
如果定义过了,就不要下面的代码了
			     ---防止头文件重复包含
*/

函数声明:

1.告诉编译器有一个函数叫什么,参数类型是什么,返回类型是什么,但具体是否存在无关紧要;

2.函数的声明一般在头文件中,先声明后用

 

函数定义:

指函数的具体实现,交代函数的功能实现

函数递归(recursion)


函数递归(recursion)

程序调用自身的编程技巧。


只需要少量程序就可以描述除解题过程所需要的多次重复运算,大大减少了代码量


递归---把大事化小


必要条件 * 2

1存在限制条件,当满足这个限制条件时,递归便不再继续

2每次递归调用之后越来越接近这个限制条件


递归常见错误:

栈溢出(stack overflow)

任何一次函数调用都会在栈区占用一块空间,最后栈区无空间,就溢出


int main()
{
	printf("hehe\12");
	main();//死循环

	return 0;
}
/※e.g.输入无符号数字1234,然后得到输出1 2 3 4
//※e.g.输入无符号数字1234,然后得到输出1 2 3 4(nb)
void print(int num)
{
	if (num > 9) {	//1234		12	
		print(num / 10);//123	1
	}
	printf("%d ", num  % 10);//1 
}

int main()
{
	unsigned int num = 0;
	printf("请输入一个大于9的数字:>");
	scanf("%d", &num);

	print(num);

	return 0;
}
递归与迭代

求n的阶乘

//求n的阶乘
int Fac1(int n)
{
	int i = 0;
	int ret = 1;
	for (i = 1; i <= n; i++)
		ret *= i;
	return ret;
}

int Fac2(int n)
{
	if (n <= 1)
		return n;
	else
		return Fac2(n - 1) * n;//nb
}

int main()
{
	int n = 0;
	printf("输入n,求n的阶乘:>");
	scanf("%d", &n);

	printf("%d! = %d\12", n, Fac1(n));
	printf("%d! = %d\12", n, Fac2(n));
	
	return 0;
}
e.g.2 编写函数,不允许创建临时变量,求字符串长度
#include<string.h>

int my_strlen(char* str)
{
	int count = 0;//用了临时变量
	while (*str != '\0')
	{
		count++;
		str++;
	}

	return count;
}

//函数1会影响函数2,用2时屏蔽掉!
int my2_strlen(char* str)
{
	if (*str != '\0')
		return 1 + my2_strlen(str + 1);
	else
		return 0;
}

int main()
{
	char arr[] = "I lost myself";

	//int len = strlen(arr);
	//int len = my_strlen(arr);

	int len = my2_strlen(arr);

	printf("%d", len);
	return 0;
}
e.g.求第n个斐波那契数(不考虑溢出)

斐波那契数列-->前两个数之和等于第三个数

Fib(n) = 1, n <= 2;

Fib(n) = Fib(n - 1) + Fib(n - 2), n > 2;

1 1 2 3 5 6 13 21 34 55 89...

int Fib1(int n)//but 效率低,缺陷大
//算第40个,要跑39,000,000+次???
{
	if (n <= 2)
		return 1;
	else
		return Fib1(n - 1) + Fib1(n - 2);
}
//时间复杂度O(2^n)

int Fib2(int n)//迭代循环
{
	int a = 1;
	int b = 1;
	int c = 1;
	if (n <= 2 )
		return 1;
	while (n > 2)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}

int Fib3(int n)//数组(易越界)
{
	int fibs[100] = { 0,1,1 };
	int i = 2;
	for (i = 2; i <= n; i++)
	{
		fibs[i] = fibs[i - 1] + fibs[i - 2];
	}
	return fibs[n];
}
//时间复杂度:O(n)


int main()
{
	int n = 0;
	printf("输入n,求第n个斐波那契数;>");
	scanf("%d", &n);
	//TDD-->测试驱动开发
	//先写怎么用,再去具体实现
	printf("%d", Fib3(n));
	return 0;
}

选递归还是选循环?

first of all,保证正确 ,其次可任选

递归满足了两个限制条件,就不会溢出嘛?

Not sure.

//栈溢出
void test(int n)
{
	if (n < 10000)//stack overflow
		test(n + 1);
}

int main()
{
	test(1);

	return 0;
}
函数递归经典题目案例

1.汉诺塔

2.青蛙跳台阶(广联达原题)

   ---《剑指offer》67道笔试题

1.汉诺塔

思路:n个盘子首先以大小顺序依次叠在A柱上,借助B柱实现从A柱平移到C柱上。难点在于每次移动都必须保证每根柱子上的盘子排序是下面的盘子比上面的大。

例:n=3时,A柱从上到下盘子编号为1,2,3,首先1盘-->C,2盘-->B,然后C上的1盘可以-->B,此时A只有3盘,B依次有1、2盘,C没有盘子。所以可以将A的3盘-->C,此时,A柱0盘。再将B柱的1盘-->A,那么B柱的2-->C,A的1-->C,移动结束。

C语言学习Part2(1000-2000行代码)_数组_02

借助递归,省略了具体实现细节,从广义上解释移动过程——

将A上前n-1个盘子借助A、C移动到B上

把A上第n个移动到C上

将B上前n-1个盘子借助B、A移动到C上

//汉诺塔
void print(char a, char b)
{
	printf("%c-->%c\n", a, b);//输出移动过程
}
//递归,省略具体实现细节,nb
void Hanoi(int n, char a, char b, char c)
{
	if (n == 1)
		print(a, c);//A-->C
	else
	{
		Hanoi(n - 1, a, c, b);
		//将A上前n-1个盘子借助A、C移动到B上
		print(a, c);
		//把A上第n个移动到C上
		Hanoi(n - 1, b, a, c);
		//将B上前n-1个盘子借助B、A移动到C上
	}
}

int main()
{
	int n = 0;
	printf("请输入盘子的个数:>");
	scanf("%d", &n);

	printf("移动次序为:>\12");
	Hanoi(n, 'A', 'B', 'C');

	return 0;
}

2.青蛙跳台阶

一只青蛙一次可以跳上 1 级台阶,也可以跳上2 级。

求该青蛙跳上一个n 级的台阶总共有多少种跳法?

n = 1--->1

n = 2--->2

n = 3--->3

n = 4--->5

n = 5--->8

...

1,2,3,5,8...Fibs?

We can see, 第n次的跳法时第n-1次和第n-2次的和


int Fib1(int n)
{
	//递归(效率低,运算大量重复)
	if (n <= 2)
		return n;
	else
		return Fib1(n - 1) + Fib1(n - 2);
}

int Fib2(int n)
{
	//循环
	int a = 1;
	int b = 2;
	int c = 0;

	if (n == 1)
		return a;
	else if (n == 2)
		return b;
	else
	{
		int i = 0;
		for (i = 3; i <= n; i++)
		{
			c = a + b;
			a = b;
			b = c;
		}
		return c;
	}
}

int main()
{
	int n = 0;
	printf("请输入台阶数n:>");
	scanf("%d", &n);

	printf("共有%d中跳法\n", Fib2(n));
	return 0;
}


数组
  • 1.一维数组的创建和初始化
  • 2.一维数组的使用
  • 3.一维数组在内存中的存储
  • 4.二维数组的创建和初始化
  • 5.二维数组的使用
  • 6.二维数组在内存中的存储
  • 7.数组作为函数参数
  • 8.数组的应用实例1:三子棋
  • 9.数组的应用实例2:扫雷游戏


1.数组的创建

一组类型相同的数据元素的集合

int arr[10];

char arr1[5];


int n = 5;

char arr[n];//err

初始化


#include<string.h>
int main()
{
	int arr[10] = { 1,2,3,4,5 };//不完全初始化

	char arr2[5] = "ab";
	//{a, b, '\0', 0, 0}
	//arr2和arr3存储方式不同
	char arr3[5] = { 'a','b'};//strlen是随机值
	//{a, b, 0, 0, 0}
  
	char arr4[5] = { 'a',98 };
	//{a, b, 0, 0, 0}


	char arr5[] = "abcdef";
	char arr6[] = { 'a','b','c','d','e','f' };

	printf("%d\12%d\xa", strlen(arr2), strlen(arr3));
	printf("%d\n", strlen(arr4));

	
	//printf("%d\12%d\xa", strlen(arr5), strlen(arr6));

	printf("%d\n", sizeof(arr5));//所占空间大小
	printf("%d\12", strlen(arr5));//字符串长度
	//arr6长度时随机值
	return 0;
}


C语言学习Part2(1000-2000行代码)_递归_03

strlen()和sizeof()的关系

1.strlen和sizeof没什么关联

2.strlen只能求字符串长度

3.sizeof计算变量、数组、类型的大小---单位是字节

4.strlen是库函数---头文件string.h; sizeof是操作符

#include<string.h>
int main()
{
	char arr1[] = "abc";
	char arr2[] = { 'a','b','c' };

	printf("sizeof(arr1) = %d\n", sizeof(arr1));
  //4-->a,b,c,\0
	printf("sizeof(arr2) = %d\n", sizeof(arr2));
  //3-->a,b,c

	printf("\12strlen(arr1) = %d\n", strlen(arr1));//3
	printf("strlen(arr2) = %d\n", strlen(arr2));//3,随机值。找'\0'
	
  return 0;
}
2.[]--->下标引用操作符

字符数组遍历形式:


for (i = 0; i < strlen(arr); i++)

strlen(arr)默认返回无符号整型值

(int)strlen(arr)-->强制类型转换会警告

修改如下:

int len = strlen(arr);

for (i = 0; i < len; i++)


整型数组遍历形式:


int arr[] = { 1,2,3,4,5,6 };

int sz = sizeof(arr) / sizeof(arr[0]);

int i = 0;

for (i = 0; i < sz; i++)


3.内存分布

数组在内存中连续存放


int main()

{

	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
  printf("&arr[%d]=%p\n", i, &arr[i]);

	return 0;
}


C语言学习Part2(1000-2000行代码)_递归_04

4.二维数组创建

二维数组是由一维数组组成的一维数组

5.二维数组通过下标访问
6.内存分布
int main()
{
	//4.
	//数组创建
	int arr11[3][4];//三行四列
	char arr21[3][5];
	double arr31[3][6];

	//初始化
	int arr1[3][4] = { 1,2,3,4 };
	int arr2[3][5] = { {2,3},{3,4} };
	int arr3[][3] = { {2,3},{3,4} };//行号可省,列号不可

	//5.
	//二维数组通过下标访问
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
			printf("%d ", arr2[i][j]);
		printf("\12");
	}
	printf("\12");

	//6.
	//内存分布
	int arr[3][4] = { {1,2,3},{2,3,4},{3,4,5} };
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
			printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]);
	}

	return 0;
}

C语言学习Part2(1000-2000行代码)_数组_05

冒泡排序
//e.g.设计一个函数实现冒泡排序,将一个整型数组排序

void Bubble_Sort(int* arr, int sz)
//arr是数组,对数组传参,传的是首元素地址&arr[0]
{
	//确定bubble排序的趟数
	int i = 0;
	int count = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = 0;
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
				flag = 0;
				//本趟排序的数据不完全有序,进行交换
				count++;
			}			
		}
		if (flag == 1)//有序,跳出循环,冒泡结束
			break;
	}
	printf("共排序%d次\n", count);
	
}

int main()
{
	//int arr[] = { 9,8,7,6,4,5,3,2,1 };//36
	int arr[] = { 1,2,3,4,5,6,7,8,9 };//36
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	//对数组进行升序排序
	Bubble_Sort(arr, sz);
	
	for (i = 0; i < sz; i++)
		printf("%d ", arr[i]);

	printf("\12");
	return 0;
}
数组名不是首元素地址?

数组名是什么?--->首元素地址

两个例外:

1.sizeof(arr)

数组名表示整个数组,sizeof(arr)计算的是整个数组的大小,单位是字节

2.&arr

数组名表示整个数组,&arr取出的是整个数组的大小

int main()
{
	int arr[] = { 1,2,3,4,5 };
	printf("%p\n", arr);//数组首地址
	printf("%p\n", &arr[0]);//数组首地址

	printf("%p\n", &arr);//整个数组的地址,也从arr[0]开始
	printf("%p\n", *arr);//数组名解引用

	int arr1[10] = { 0 };
	printf("%d\n", sizeof(arr1));//10 * 4
	
	return 0;
}

C语言学习Part2(1000-2000行代码)_递归_06

//e.g.地址比较
int main()
{
	int arr[] = { 1,2,3,4,5,6,7 };
	printf("arr = %p\n", arr);
	printf("arr+1 = %p\n\n", arr + 1);//+4

	printf("&arr[0] = %p\n", &arr[0]);
	printf("&arr[0] + 1 = %p\n", &arr[0] + 1);//+4

	printf("&arr = %p\n", &arr);
	printf("&arr + 1 = %p\n", &arr + 1);//+28

	return 0;
}









标签:arr,函数,int,Part2,C语言,2000,数组,printf,strlen
From: https://blog.51cto.com/u_16287559/9452344

相关文章

  • (2024.1.22-2024.1.28)C语言学习小结
    本周主要围绕《HeadfirstC》这本书展开C语言学习,按照计划,我学习了前四章的内容。基本内容以下时学习做的思维导图(笔记)第1章虽然做的是思维导图,但实际上因为大多数内容已经掌握,所以实际上就是补充记了几个零散的点。第2、2.5章主要是指针、数组、字符串的内容,大多也已经......
  • C语言笔记9
       函数的参数传递形式参数:函数定义时的参数,简称形参。实际参数:函数调用时的参数,简称实参。实参与形参数目、类型和顺序应一致,占据不同存储单位。 理解单向值传递每个函数都有自己的变量空间,参数也位于这个空间;形参调用前不占内存单位,调用时对形参分配单位并传......
  • SciTech-EE-Virtual Electronics Lab: How to Create an Oscilloscope Using Python a
    https://wiki.analog.com/university/tools/m2kVirtualElectronicsLab:HowtoCreateanOscilloscopeUsingPythonandADALM2000byArnieMaeBaesandChristianGarciaDownloadPDFAbstractAvirtualelectronicslaboratoryisacollectionofsoftware-based......
  • C语言近段时间的总结
    一、电脑我们一开始买回来的电脑分成   硬件和操作系统   在二者中间有一层叫做  驱动层   。关于驱动层,目前我是这么理解的,它相当于是一座桥梁,是用来连接起虚拟的操作和现实的机器,因此可以通过现实当中的动作来使计算机完成一定的操作,也就是作为一个翻译来帮助......
  • [经验] c语言怎么对齐-如何使用C语言进行数据对齐
    C语言怎么对齐在C语言中,对齐(alignment)是一项非常重要的操作。它是指在内存中分配一个变量或数据结构的位置时,如何选择该位置。因为计算机硬件的限制,内存中的访问一般是按照字节顺序依次排列的。因此,如果一个变量被放在了一个不对齐的位置上,它的访问就会变得非常困难和低效。因此,对......
  • 物联网工程师技术之C语言IO输入输出技术
    本章重点​语句和语句块​printf函数​scanf函数在C语言编程中,经常需要通过输入设备(如键盘)向程序录入信息,或者将信息显示在输出设备(如屏幕),这时,可以使用输入输出语句来完成。输入输出语句是用户与程序交互的唯一途径,掌握好输入输出语句对后面的学习至关重要。本章将针对输入......
  • C语言---Day7
    16、指针---windows电脑在数据存储是采用小端对齐---指针就是内存地址,指针变量是用来存放内存地址的变量;每一个变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址---在32位操作系统下,所有指针类型都是4个字节大小;  在64位......
  • 【C语言进阶篇】模拟实现通讯录 (内附源码)
    (文章目录)......
  • windows程序设计---使用c语言开发windows桌面应用程序
     消息机制--队列消息(常规消息鼠标,键盘等等,经过消息循环)GetMessage()得到消息--从消息队列中检索,DispatchMessage()分发消息消息机制----非对列消息-----调用特定windows程序函数触发的消息如:CreateWindow()函数被调用则发送WM_PAINT消息   windows窗口,非客户区是无......
  • 初识C语言:掌握未来的编程利器
    ​✨✨欢迎大家来到贝蒂大讲堂✨✨​......