您现在的位置是:首页 >其他 >C进阶习题网站首页其他
C进阶习题
目录
用精神内耗的态度去搞学习搞事业搞钱,用躺平和摆烂的态度对待人际关系,烦恼能消失一大半。
一、指针进阶
1.1 知识点
(1)指针的数据类型,声明的是指针实际指向内容的数据类型;free释放后,并不会自动置为NULL;野指针指向的是未分配或者已经释放的内存地址
(2)int类型的指针数组,就是一个数组,里面存放的是int*
(3)函数return返回值,不能返回两个(可以通过地址传址),可以一个,可以没有
(4)定义一个函数指针,指向的函数有两个int形参并且返回一个函数指针,返回的指针指向一个有一个int形参且返回int的函数 int (*(*F)(int, int))(int)
(5)声明一个指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,参数是int*,(int (*(*p)[10])(int *) 【4 5要求会写】
(6)一个地址是否能放到指针里面,要看指针指向内容的类型是否是该地址指向内容的类型
(7)函数的声明:int Add(int a, int b) 或者是 int Add(int, int);
(8) 做选择题,要注意是单选题还是多选题
(9)int arr[3][5]; int (*arr)[5] 指的是,一个指针指向的是二维数组第一行
1.2 实现一个函数,左旋字符串中的K个字符(旋转字符串)可以类比右旋)
方法一:
#include <stdio.h>
#include <string.h>
//思路:字符串的第一个元素,拿出来放在一个空间里面,再把字符串向前移一位,
//再把新的空间里面的元素放在字符串所在空间的最后一个位置
void left_move(char* str, int k)
{
int i = 0;
for (i = 0; i < k; i++)
{
//每次一个字符
char tmp = *str;
int len = 0;
len = strlen(str);
int j = 0;
for (j = 0; j < len - 1; j++)
{
*(str + j) = *(str + j + 1);
}
*(str + len - 1) = tmp;
}
}
int main()
{
char arr[] = "abcdef";//一般用数组存放字符串
int k = 0;
scanf("%d", &k);
left_move(arr, k);
printf("%s
", arr);
return 0;
}
方法二:
#include <stdio.h>
#include <string.h>
//思路:三步反转法,首先需要左移的字符进行逆序进行逆序,
//然后字符串不需要进行左移的字符进行逆转,最后整个字符串的元素进行逆序
//逆序函数
void reverse(char* left, char* right)
//学习完指针之后,用指针,不需要用数组下标
{
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
//左移函数
void left_move(char* str, int k)
{
int len = strlen(str);
k = k % len;//这个点要注意,容易忽略
reverse(str, str + k - 1);
reverse(str + k, str + len - 1);
reverse(str, str + len - 1);
}
int main()
{
char arr[] = "abcdef";//一般用数组存放字符串
int k = 0;
scanf("%d", &k);
left_move(arr, k);
printf("%s
", arr);
return 0;
}
1.3 有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在,时间复杂度小于O(N)[说明,不可以遍历整个数组]。(杨氏矩阵)
代码一:(这种写法不是特别的好)
#include <stdio.h>
//思路:因为每一行每一列都是递增的,所以需要查找的数字,
//只需要和每一行的最后一个元素进行比较(不能和第一个元素进行比较)【可以从右上角以及左下角开始】,如果大于
//就可以对下一行进行比较。小于后,找到相应的行,
//然后对该行的元素进行比较,从后向前比较,如果该行遍历完之后还是没有,
//就说明没有这个数字
void find_int_arr(int arr[3][3], int r, int c, int k)
{
int x = 0;
int y = c - 1;
while (x <= r - 1 && y >= 0)
{
if (arr[x][y] < k)
{
x++;
}
else if (arr[x][y] > k)
{
y--;
}
else
{
printf("找到了,下标是:x = %d y = %d
", x, y);
return;
}
}
printf("找不到
");
}
int main()
{
int arr[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int k = 0;
scanf("%d", &k);
find_int_arr(arr, 3, 3, k);
return 0;
}
代码2:(优化后的代码)
#include <stdio.h>
//区别:函数返回下标的值,以及找不到的内容,
//通过地址传址
void find_int_arr(int arr[3][3], int* px, int* py, int k)
{
int x = 0;
int y = *py -1;
while (x <= *px - 1 && y >= 0)
{
if (arr[x][y] < k)
{
x++;
}
else if (arr[x][y] > k)
{
y--;
}
else
{
*px = x;
*py = y;
return;
}
}
*px = -1;
*py = -1;
}
int main()
{
int arr[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int x = 3;
int y = 3;
int k = 0;
scanf("%d", &k);
//参数是返回型参数
find_int_arr(arr, &x, &y, k);
if (x == -1 && y == -1)
{
printf("找不到
");
}
else
{
printf("找到了,下标是:%d %d", x, y);
}
return 0;
}
1.4 写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。是的话,返回1,否返回0
方法一:
//判断是一个字符串的旋转字符
//思路:旋转判断旋转判断
#include <stdio.h>
#include <string.h>
void reverse(char* left, char* right)
{
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
//左移函数
void left_move(char* str, int k)
{
int len = strlen(str);
k = k % len;//这个点要注意,容易忽略
reverse(str, str + k - 1);
reverse(str + k, str + len - 1);
reverse(str, str + len - 1);
}
int is_left_move(char* arr1, char* arr2)
{
int len = strlen(arr1);
int i = 0;
for (i = 0; i < len; i++)
{
left_move(arr1, 1);
if (strcmp(arr1, arr2) == 0)
return 1;
}
return 0;
}
int main()
{
char arr1[] = "AABCD";
char arr2[] = "BCDAA";
int ret = is_left_move(arr1, arr2);
printf("%d
", ret);
return 0;
}
方法二:
// 判断是一个字符串的旋转字符
//思路:字符串追加本身,再判断是否包含里面,包含就是旋转字符
#include <stdio.h>
#include <string.h>
int is_left_move(char* arr1, char* arr2)
{
int len1 = strlen(arr1);
int len2 = strlen(arr2);
if (len1 != len2)
return 0;
strncat(arr1, arr1, len1);
if (strstr(arr1, arr2))
return 1;
else
return 0;
}
int main()
{
char arr1[20] = "AABCD";
char arr2[] = "BCDAA";
int ret = is_left_move(arr1, arr2);
printf("%d
", ret);
return 0;
}
二 动态内存分配
2.1 知识点
(1)枚举,打印出来,依次加一,相较于上一个(除了,定义时候就赋值的)
(2)define定义的标识符常量,是替换内容。需要带进去,而不是计算完再代入(易错点)
2.2代码结果
int main()
{
unsigned char puc[4];
struct tagPIM
{
unsigned char ucPim1;
unsigned char ucData0 : 1;
unsigned char ucData1 : 2;
unsigned char ucData2 : 3;
}*pstPimData;
pstPimData = (struct tagPIM*)puc;
memset(puc,0,4);
pstPimData->ucPim1 = 2;
pstPimData->ucData0 = 3;
pstPimData->ucData1 = 4;
pstPimData->ucData2 = 5;
printf("%02x %02x %02x %02x
",puc[0], puc[1], puc[2], puc[3]);
return 0;
}
结果为:02 29 00 00
知识点:%x指的是打印16进制;%02x指的是,打印两位
2.3
#include<stdio.h>
int main()
{
union
{
short k;
char i[2];
}*s, a;
s = &a;
s->i[0] = 0x39;
s->i[1] = 0x38;
printf("%x
", a.k);
return 0;
}
打印结果:3839
2.4 BC100-有序序列合并
输入两个升序排列的序列,将两个序列合并为一个有序序列并输出。输入描述:输入包含三行,第一行包含两个正整数n, m,用空格分隔。n表示第二行第一个升序序列中数字的个数,m表示第三行第二个升序序列中数字的个数。第二行包含n个整数,用空格分隔。第三行包含m个整数,用空格分隔。输出描述:输出为一行,输出长度为n+m的升序序列,即长度为n的升序序列和长度为m的升序序列中的元素重新进行升序序列排列合并。
#include <stdio.h>
int main()
{
int n = 0;
int m = 0;
//输入
scanf("%d %d", &n, &m);
int arr1[n];//在C99标准就可以,牛客网支持
int arr2[m];
//输入两个升序的序列
int i = 0;
for (i = 0; i < n; i++)
{
scanf("%d ", &arr1[i]);
}
for (i = 0; i < m; i++)
{
scanf("%d ", &arr2[i]);
}
//输出序列
int j = 0;
i = 0;
while ((i < n) && (j < m))
{
if (arr1[i] < arr2[j])
{
printf("%d ", arr1[i]);
i++;
}
else
{
printf("%d ", arr2[j]);
j++;
}
}
if (i == n)
{
for (; j < m; j++)
{
printf("%d ", arr2[j]);
}
}
else
{
for (; i < n; i++)
{
printf("%d ", arr1[i]);
}
}
return 0;
}
2.5 BC38 变种水仙花
描述:变种水仙花数 - Lily Number:把任意的数字,从中间拆分成两个数字,比如1461 可以拆分成(1和461),(14和61),(146和1),如果所有拆分后的乘积之和等于自身,则是一个Lily Number。例如:655 = 6 * 55 + 65 * 5;1461 = 1*461 + 14*61 + 146*1求出 5位数中的所有 Lily Number。输入描述:无 输出描述:一行,5位数中的所有 Lily Number,每两个数之间间隔一个空格。
#include <stdio.h>
#include <math.h>
int main()
{
// 12345 12345/10 * 12345%10
//12345/100 * 12345%100
int i = 0;
for (i = 10000; i < 99999; i++)
{
int j = 0;
int sum = 0;
for (j = 1; j <= 4; j++)
{
int m = i / pow(10, j);
int n = i % (int)pow(10, j);
sum = sum + m * n;
}
if (i == sum)
{
printf("%d ", i);
}
}
return 0;
}
知识点:(1)%的两边的数字,必须是整形(2)pow返回的值为double类型的
2.6一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。编写一个函数找出这两个只出现一次的数字。例如:有数组的元素是:1,2,3,4,5,1,2,3,4,6;只有5和6只出现1次,要找出5和6.
#include <stdio.h>
//进行分组:只出现一次的两个数字分到两个分组中,一组一个
// 如何进行分组:没有进行分组的时候,组内数字进行异或出现的结果,结果是1的位置说明,两个数字是不同的,按照这个位置的数字是1还是0,就可以将这两个数字分到不同的组中
//每个组都满足,只有一个数字出现一次,其他数字都是成对出现的,这样每一个组的数字进行异或,那么结果就是只出现一次的那个数字
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 6 };
//0异或任何数还是等于任何数
//1.先数组内进行异或
int ret = 0;
int sz = sizeof(arr) / sizeof(int);
int i = 0;
for (i = 0; i < sz; i++)
{
ret = ret ^ arr[i];
}
//计算ret的二进制中第几位是1
int pos = 0;
for (i = 0; i < 32; i++)
{
if (((ret >> 1) & 1) == 1)
{
pos = i;
break;
}
}
//3.按照pos位的0或者1进行分组
int m = 0;
int n = 0;
for (i = 0; i < sz; i++)
{
if (((arr[i] >> pos) & 1) == 1)
{
m = m ^ arr[i];
}
else
{
n = n ^ arr[i];
}
}
printf("%d %d
", m, n);
return 0;
}
2.7 模拟实现atoi
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
//atoi a to i 头文件是<stdlib.h>
//把字符串转换为整形
//'1' -'0' = 1 字符1减去字符0等于1;'2' - '0' = 2
//比如,字符串"12" 0*10+1=1; 1*10+2=12;
//比如“123” 0*10+1=1; 1*10+2=12 12*10+3=123
//空指针 空字符串 正负 非数字字符 超大数字(超过int整形范围的数字)空白字符
enum State //枚举
{
INVALID,//valid 合法的
VALID
};
enum State status = INVALID;
//正常的情况只有一种,非法的情况有许多,所以这里直接写INVALID
int my_atoi(const char* str)
{
assert(str);
//空字符串的问题
if (*str == '