c语言练习及知识点补充

本文详细介绍了C语言中的return语句用法,包括返回值、void函数、类型转换,以及assert宏的作用和使用。此外,还涉及字符串旋转算法、数组操作(如奇数偶数排列和冒泡排序)、杨氏矩阵查找和逻辑谜题(猜凶手)。
摘要由CSDN通过智能技术生成

一,return语句

1,return后边可以是一个数值,也可以是一个表达式,如果是表达式则先执行表达式,再返回表达式的结果,如:

int add(int a, int b)
{
	return a + b;
}


case 1:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret = %d\n", ret);
			break;

2,return后边也可以什么都没有,直接写return;这种写法适合函数返回类型是void的情况

3,return返回的值和函数返回类型不一致,系统将会自动将返回的值隐式转换为函数的返回类型

4,return语句执行中,函数就彻底返回,后边代码就不再执行

5,如果函数中存在if等分支的语句,则要保证每种情况下都有return返回,否则会出现编译错误

如:

if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == ' ')
		{
			return;
		}
		else if (GetMineCount(mine,x,y)!= 0)
		{
			show[x][y] = GetMineCount(mine, x, y)+'0';
			return;
		}

二,assert宏的使用

在C语言中,`assert`是一个宏,用于在程序运行期间进行断言检查。它的基本语法如下:

#include <assert.h>

assert(expression);

在这里,`expression`是你想要检查的表达式。如果这个表达式的结果是`false`(也就是0),那么`assert`就会触发一个错误,并向标准错误输出(`stderr`)打印一条错误信息,然后调用`abort()`函数来终止程序的运行。

例如,假设我们有一个函数,该函数接受一个整数作为参数,并假设这个整数大于0。我们可以使用`assert`来确保这个条件得到满足:

#include <stdio.h>
#include <assert.h>

void do_something(int value) {
    assert(value > 0);  // 断言:value大于0
    // ... 其他代码 ...
}

int main() {
    do_something(2);  // 这个调用将成功
    do_something(0);  // 这个调用将导致断言错误
    return 0;
}

在这个例子中,如果`do_something`函数的参数是0,那么`assert`就会触发一个错误,程序将停止运行,并显示一条错误消息。

需要注意的是,`assert`主要用于调试目的。在程序发布时,为了提高性能,常常会通过定义`NDEBUG`宏来禁用`assert`。因此,依赖`assert`的代码在发布版本中可能不会工作。

 让我们来举个栗子:

assert(P! = NULL);

 上面代码在程序运行到这一行语句时,验证变量p是否等于NULL。如果确实不等于NULL,程序继续运行,否则将会终止运行,并且给出报错信息提示

使用assert()的好处:

①它不仅自动标识文件和出问题的行号,还有一种无需更改代码就能开启或关闭assert()的机制。

②如果已经确认程序没有问题,就不需要再做断言就在#include<assert.h>语句的前面定义一个宏NDEBUG。

#define NDEBUG
#include<assert.h>

然后,重新编译程序,编译器就会禁用文件中所有的assert()语句。如果程序又出现问题,可以移除这条#define NDEBUG指令(或者把它注释掉),再次编译,这样就重新启用了assert()语句

assert()的缺点是,因为引入了额外的检查,增加了程序的运行时间

一般我们可以再Debug中使用,再Release版本中选择禁用assert就行,再VS这样的集成开发环境中,再Release版本中,直接就是优化掉了。这样在debug版本写有利于程序员排查问题

三,字符串旋转结果

写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。

例如:给定s1 =AABCD和s2 = BCDAA,返回1

给定s1=abcd和s2=ACBD,返回0.

AABCD左旋一个字符得到ABCDA

AABCD左旋两个字符得到BCDAA

AABCD右旋一个字符得到DAABC

此题有三个解法:

解法一:

#define DEBUG
#include<stdio.h>
#include<string.h>
#include<assert.h>
void leftRound(char* str, int k)
{
	int len = strlen(str);
	int time = k % len;
	
	for (int i = 0; i < time; i++)
	{
		char tmp = str[0];
		int j = 0;
		for (; j < len - 1; j++)
		{
			str[j] = str[j + 1];
		}
		str[j] = tmp;
	}
}

int main()
{
	char str[20] = "abcdef";
	leftRound(str, 5);
	printf("%s\n", str);
	return 0;
}

解法二:

//第二种解法
void leftRound(char* str, int k)
{
	int len = strlen(str);
	int time = k % len;//2
	char tmp[256] = { 0 };

	strcpy(tmp, str + time);//tmp==cd
	strncat(tmp, str, time);//cdab  tmp存储的就是cdab
	strcpy(str, tmp);//将tmp里面的所有内容都拷贝到str中
}

int main()
{
	char str[20] = "abcdef";
	leftRound(str, 7);
	printf("%s\n", str);

	//strcpy();字符串拷贝
	//strncpy();字符串拼接->n个
	/*char str1[10] = {0};
	char* str2 = "abcd";
	strcpy(str1, str2);//意思是把str2拷贝到str1中
	printf("%s\n", str1);//abcd
	printf("%s\n", str2);//abcd

	char str3[100] = "abc";
	char* str4 = "hello";

	strcat(str3, str4);//把str4里面的所有内容拷贝到str3当中
	strncat(str3, str4, 2);//拼接部分,拼接str4中的前面两个字母
	printf("%s", str3);*/
	return 0;
}

解法三:

 

void Reverse(char* str, int start, int end)
{
	int left = start;
	int right = end;
	while (left < right)
	{
		char tmp = str[left];
		str[left] = str[right];
		str[right] = tmp;
		left++;
		right--;
	}
}
void leftRound(char* str, int k)
{
	int len = strlen(str);
	int time = k % len;//2
	Reverse(str, 0, time - 1);
	Reverse(str, time, len - 1);
	Reverse(str, 0, len - 1);
}


int main()
{
	char str[20] = "abcdef";
	leftRound(str, 7);
	printf("%s\n", str);
}

四,调整数组使奇数全部都位于偶数前面。

思路:
1. 给定两个下标left和right,left放在数组的起始位置,right放在数组中最后一个元素的位置
2. 循环进行一下操作
 a. 如果left和right表示的区间[left, right]有效,进行b,否则结束循环
 b. left从前往后找,找到一个偶数后停止
 c. right从后往前找,找到一个奇数后停止
 d. 如果left和right都找到了对应的数据,则交换,继续a,
void funcction(int arr[], int len)
{
	int left = 0;
	int right = len - 1;
	int tmp = 0;
	while (left < right)
	{
		// 从前往后,找到一个偶数,找到后停止
		while ((left < right) && arr[left] % 2 == 1)
		{
			left++;
		}
		// 从后往前找,找一个奇数,找到后停止
		while ((left < right) && (arr[right] % 2 == 0))
		{
			right--;
		}
		// 如果偶数和奇数都找到,交换这两个数据的位置
	 // 然后继续找,直到两个指针相遇
		if (left < right)
		{
			tmp = arr[left];
			arr[left] = arr[right];
			arr[right] = tmp;
		}
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int len = sizeof(arr) / sizeof(arr[0]);
	funcction(arr, len);

	for (int i = 0; i < len; i++)
	{
		printf("%d ", arr[i]);
		//printf("%d ", *(arr+i));
	}
}

五,使用指针打印数组内容

#include <stdio.h>
int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    //在这里完成代码
    // 分析:因为数组中存储的元素类型是int类型的,因此只要给一个int的指针,依次取索引数组中的每个元素即可
    int* p = arr;  // 数组名代表数组首元素的地址
    for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i)
    {
        printf("%d ", *p);   // *p: 取到p所指向位置的元素
        ++p;                 // 获取p的下一个位置
    }
    
   	return 0;
}

六,实现一个对整形数组的冒泡排序

/*
思路:
遍历数组,对数组中相邻的两个元素进行比较,如果需要升序,前一个数据大于后一个数据时,交换两个位置上的数据,直到所有的数据比较完,此时,最大的数据已经放在数组的末尾。
除最大数据已经排好序外,其余数据还是无需,对剩余数据采用与上述类似的方式进行处理即可
*/


void BubbleSort(int array[], int size)
{
    // 外层循环控制冒泡排序的趟数
    // size-1表示:最后一趟区间中只剩余1个元素,该趟冒泡可以省略
    for(int i = 0; i < size-1; ++i)
    {
        // 具体冒泡的方式:用相邻的两个元素进行比较,前一个大于后一个元素时,交换着两个数据,依次直到数组的末尾
        for(int j = 1; j < size-i; ++j)
        {
            if(array[j-1] > array[j])
            {
                int temp = array[j-1];
                array[j-1] = array[j];
                array[j] = temp;
            }
        }
    }
}

int main()
{
	int arr[10] = { 1,3,5,7,9,2,4,6,8,10 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BubbleSort(arr, size);
	for (int i = 0; i < size; i++)
	{
		printf("%d ", *(arr + i));
	}
}

//优化:如果某次冒泡结束后,序列已经有序了,后面剩余元素的冒泡可以省略

void BubbleSort(int array[], int size)
{
	// 外层循环控制冒泡排序的趟数
	// size-1表示:最后一趟区间中只剩余1个元素,该趟冒泡可以省略
	for (int i = 0; i < size - 1; ++i)
	{
		int isChange = 0;
		// 具体冒泡的方式:用相邻的两个元素进行比较,前一个大于后一个元素时,交换着两个数据,依次直到数组的末尾
		for (int j = 1; j < size - i; ++j)
		{
			if (array[j - 1] > array[j])
			{
				int temp = array[j - 1];
				array[j - 1] = array[j];
				array[j] = temp;

				isChange = 1;   // 如果本次冒泡进行数据交换了,说明本次还是无序的,就将isChange设置为1
			}
		}

		// 如果本次冒泡中,元素没有交换,则本次开始冒泡时,数据已经有序了,后面的冒泡可以不用进行了
		if (!isChange)
			return;
	}
}int main()
{
	int array[10] = { 1,3,5,7,9,2,4,6,8,10 };
	int size = sizeof(array) / sizeof(array[0]);
	BubbleSort(array, size);
	for (int i = 0; i < size; i++)
	{
		printf("%d ", *(array + i));
	}
}

七,杨氏矩阵

要求:

有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。

时间复杂度小于O(N);

思路:

我们仔细分析,不难发现,
对于杨氏矩阵老说,右上角和左下角的元素是有特点的。
右上角的元素是一行中最大的,一列中最小的。左下角的元素是一行中最小的,是一列中最大的。
所以我们可以从右上角或者左下角开始查找。
比如:从右上角开始查找的时候,右上角的元素比我们要查找元素小,我们就可以去掉右上角元素所在的这一行
右上角的元素比我们要查找的元素大,我们就可以去掉右上角元素所在的这一列。
然后依然找右上角的元素继续和要查找的元素与比较。这样每一次比较去掉一行或者去掉一列。
这个查找效率是高于遍历数组元素的
,所以时间复杂度是小于O(N),也满足题目要求。

 

int findnum(int a[][3], int x, int y, int f) //第一个参数的类型需要调整
{
	int i = 0, j = y - 1; //从右上角开始遍历
	while (j >= 0 && i < x)
	{
		if (a[i][j] < f) //比我大就向下
		{
			i++;
		}
		else if (a[i][j] > f) //比我小就向左
		{
			j--;
		}
		else
		{
			return 1;
		}
	}
	return 0;
}

int main()
{
	int a[][3] = { {1, 3, 5},
				  {3, 5, 7},
				  {5, 7, 9} }; //一个示例

	if (findnum(a, 3, 3, 2))
	{
		printf("It has been found!\n");
	}
	else
	{
		printf("It hasn't been found!\n");
	}

	return 0;
}

八,猜凶手

内容:日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。

以下为4个嫌疑犯的供词:

A说:不是我。

B说:是C。

C说:是D。

D说:C在胡说

已知3个人说了真话,1个人说的是假话。

现在请根据这些信息,写一个程序来确定到底谁是凶手。

//这个题就是按照正常方式,假设凶手是a,b,c,d其中的一个,看是不是满足题目条件,如果满足就找出了凶手。

#include<stdio.h>
int main()
{
   int killer = 0;
   //分别假设凶手是a,b,c,d,看谁是凶手时满足3个人说了真话,一个人说了假话
   for (killer = 'a'; killer <= 'd'; killer++)
   {
    if ((killer != 'a') + (killer == 'c') + (killer == 'd') + (killer != 'd') == 3)
     printf("凶手是:%c", killer);
   }
   return 0;
}

九,杨辉三角

由于此题要打印整个杨辉三角的数据而非取出某一项,所以不可避免的一定是要填出每一项,没有偷懒的余地,那就老老实实的根据规律填空即可。按照题设的场景,能发现数字规律为:d[i][j] = d[i - 1][j] + d[i - 1][j - 1]。所以我们只要按照这个方法填表即可。

void yangHuiTriangle(int n)
{
	int data[30][30] = { 1 }; //第一行直接填好,播下种子

	int i, j;

	for (i = 1; i < n; i++) //从第二行开始填
	{
		data[i][0] = 1; //每行的第一列都没有区别,直接给1,保证不会越界。
		for (j = 1; j <= i; j++) //从第二列开始填
		{
			data[i][j] = data[i - 1][j] + data[i - 1][j - 1]; //递推方程
		}
	}

	for (i = 0; i < n; i++) //填完打印
	{
		for (j = 0; j <= i; j++)
		{
			printf("%d ", data[i][j]);
		}
		putchar('\n');
	}
}

改进:

由于我在填第n行的杨辉三角时,只跟第n-1行的杨辉三角产生联系,不会跟之前的有联系,所以没必要保存每一行的杨辉三角,填一行打一行就行了,这样能让空间复杂度从n^2降低到n。但是在填数据的时候不能对之前的数据覆盖,所以需要从后向前填。而填杨辉三角顺序对结果是没有影响的,所以可以实现。

void yangHuiTriangle(int n)
{
	int data[30] = { 1 };

	int i, j;
	printf("1\n"); //第一行就直接打印了
	for (i = 1; i < n; i++) //从第二行开始
	{
		for (j = i; j > 0; j--) //从后向前填,避免上一行的数据在使用前就被覆盖
		{
			data[j] += data[j - 1]; //公式同上,由于变成了一维,公式也变简单了。
		}
     
		for (j = 0; j <= i; j++) //这一行填完就直接打印了。
		{
			printf("%d ", data[j]);
		}
		putchar('\n');
	}
}

※这种方法虽然降低了空间复杂度,但只能保存最后一行的数据,不利于反复查询,两个填法各有各的适用场景。就本题而言,改进后的胜出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值