第一题.strlen函数的模拟实现
strlen函数用于计算字符串的长度,以下是一个模拟实现:
int strlen(char *str) { int len = 0; while (*str != '\0') { len++; str++; } return len; }
该函数通过一个while循环来遍历字符串,每遍历一个字符,计数器len就加1。当遍历到字符串的结尾字符'\0'时,循环结束,函数返回计数器的值,即字符串的长度。
第二题 模拟实现strcpy
strcpy是C语言标准库中的函数,用于将字符串复制到另一个字符串中。
以下是模拟实现strcpy的代码:
char* strcpy(char* dest, const char* src) { char* p_dest = dest; while (*src != '\0') { *p_dest++ = *src++; } *p_dest = '\0'; // 添加字符串结尾符 return dest; }
strcpy的工作原理是遍历源字符串,逐个字符地复制到目标字符串中,遇到字符串结尾符\0时停止复制并在目标字符串末尾添加一个\0。在模拟实现中,使用指针p_dest指向目标字符串,每复制一个字符,就将指针后移一位;使用指针src指向源字符串,每读取一个字符,就将指针后移一位。最后返回指向目标字符串的指针dest。
第三题.模仿qsort的功能实现一个通用的冒泡排序
void swap(char* buf1, char* buf2, size_t size) { int i = 0; for (i = 0; i < size; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } } void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void*e2)) { //冒泡排序的趟数 int i = 0; for (i = 0; i < num - 1; i++) { //一趟冒泡排序 int j = 0; for (j = 0; j < num - 1 - i; j++) { if(cmp((char*)base + j * size, (char*)base + (j + 1) * size)<0) { //交换 swap((char*)base + j * size, (char*)base + (j + 1) * size, size); } } } }
第四题.实现一个函数,可以左旋字符串中的k个字符。
例如:ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
设计循环使其可以旋1次,然后让他执行n次是一个最简单的思路:
void leftRound(char * src, int time) { int i, j, tmp; int len = strlen(src); time %= len; //长度为5的情况下,旋转6、11、16...次相当于1次,7、12、17...次相当于2次,以此类推。 for (i = 0; i < time; i++) //执行k次的单次平移 { tmp = src[0]; for(j = 0; j < len - 1; j++) //单次平移 { src[j] = src[j + 1]; } src[j] = tmp; } }
改进一:
这个思路当然可以,但是一次一次转毕竟太麻烦,就不能一次到位么?
当然可以,我们可以选择拼接法,一次到位:
void leftRound(char * src, int time) { int len = strlen(src); int pos = time % len; //断开位置的下标 char tmp[256] = { 0 }; //更准确的话可以选择malloc len + 1个字节的空间来做这个tmp strcpy(tmp, src + pos); //先将后面的全部拷过来 strncat(tmp, src, pos); //然后将前面几个接上 strcpy(src, tmp); //最后拷回去 }
改进二:
这个方法要用到一个数组形成的辅助空间,让人觉得有点不爽,还可以有更好的选择,例如ABCDEFG,左旋3次后变成DEFGABC,有一个特殊的操作方式:
先将要左旋的前三个家伙逆序(CBADEFG),然后将后半段也逆序(CBAGFED),最后整体逆序(DEFGABC)即可。这样只需要做数值交换即可,可以写一个函数帮我们完成局部逆序,代码如下:
void reverse_part(char *str, int start, int end) //将字符串从start到end这一段逆序 { int i, j; char tmp; for (i = start, j = end; i < j; i++, j--) { tmp = str[i]; str[i] = str[j]; str[j] = tmp; } } void leftRound(char * src, int time) { int len = strlen(src); int pos = time % len; reverse_part(src, 0, pos - 1); //逆序前段 reverse_part(src, pos, len - 1); //逆序后段 reverse_part(src, 0, len - 1); //整体逆序 }
第五题.写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:给定s1 =AABCD和s2 = BCDAA,返回1
给定s1=abcd和s2=ACBD,返回0.
AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
解析:
本题当然可以将所有旋转后的结果放到一个数组里,然后进行查找,但是这种做法既不好操作,也太费事,但是这题有一个很简单的做法:
其实ABCDE无论怎么旋,旋转后的所有结果,都包含在了ABCDEABCD这个字符串里了。
所以做法很简单,只需要将原字符串再来一遍接在后面,然后找一找待查找的字符串是不是两倍原字符串的子集即可。
int findRound(const char * src, char * find) { char tmp[256] = { 0 }; //用一个辅助空间将原字符串做成两倍原字符串 strcpy(tmp, src); //先拷贝一遍 strcat(tmp, src); //再连接一遍 return strstr(tmp, find) != NULL; //看看找不找得到 }
方法二
int is_left_move(char* str1, char* str2) { int len1 = strlen(str1); int len2 = strlen(str2); if (len1 != len2) return 0; strncat(str1, str1, len1); if (strstr(str1, str2) == NULL) return 0; else return 1; } int main() { char arr1[20] = "abcdef";//abcdefabcdef //bcdefa //cdefab //defabc //efabcd //fabcde //abcdef char arr2[] = "cdef"; int ret = is_left_move(arr1, arr2);//判断arr2是否是arr1旋转所得 if (ret == 1) printf("Yes\n"); else printf("No\n"); return 0; }
第六题.杨氏矩阵
有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。
要求:时间复杂度小于O(N);
#include <stdio.h> 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; }
方法二,可返回所求值的坐标
int Find(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 1; } } *px = -1; *py = -1; return 0; } int main() { int arr[3][3] = { 1,2,3, 4,5,6, 7,8,9 }; int k = 0; int x = 3; int y = 3; scanf("%d", &k);//7 int ret = Find(arr, &x, &y, k);//存在返回1,如果不存在返回0 if (ret == 1) { printf("Yes\n"); printf("%d %d\n", x, y); } else { printf("No\n"); printf("%d %d\n", x, y); } return 0; }
第七题.下面程序的结果是:( )
int main()
{
int aa[2][5] = {10,9,8,7,6,5,4,3,2,1};
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
A.1, 6
B.10, 5
C.10, 1
D.1, 5
&aa的类型是int (*)[2][5],加一操作会导致跳转一个int [2][5]的长度,直接跑到刚好越界的位置。减一以后回到最后一个位置1处。*(aa + 1)相当于aa[1],也就是第二行的首地址,自然是5的位置。减一以后由于多维数组空间的连续性,会回到上一行末尾的6处。故选A。
第八题.模拟实现了二维数组
方法一.静态开辟
int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 2,3,4,5,6 }; int arr3[] = { 3,4,5,6,7 }; int* arr[3] = {arr1, arr2, arr3}; int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { //printf("%d ", *(arr[i] + j)); printf("%d ", arr[i][j]); } printf("\n"); } return 0; }
方法二.动态开辟
#include <stdlib.h> // int main() { int **arr = (int**)malloc(3*sizeof(int*)); int i = 0; for (i = 0; i < 3; i++) { *(arr + i) = (int*)malloc(5*sizeof(int)); } //初始化 for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { arr[i][j] = 1; } } //打印 for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { printf("%d ", arr[i][j]); } printf("\n"); } //释放 for (i = 0; i < 3; i++) { free(arr[i]); } free(arr); arr = NULL; return 0; }
第六题.
/int main() //{ // int a[5] = { 1, 2, 3, 4, 5 }; // int* ptr = (int*)(&a + 1); // printf("%d, %d", *(a + 1), *(ptr - 1)); // // return 0; //}
第七题
//由于还没学习结构体,这里告知结构体的大小是20个字节 //X86 环境下演示 struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; } * p; //假设p 的值为0x100000。 如下表表达式的值分别为多少? //已知,结构体Test类型的变量大小是20个字节 //0x开头的数字是16进制的数字 int main() { p = (struct Test*)0x100000; printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; }
第八题
#include <stdio.h> int main() { int a[3][2] = { (0, 1), (2, 3), (4, 5) }; int* p; p = a[0]; printf("%d", p[0]);//? return 0; }
int a[3][2] = { (0, 1), (2, 3), (4, 5) };//实际初始化为1 3 5,为逗号表达式取两者中的最大值
第九题
int main() { int a[5][5]; int(*p)[4];//数组指针 p = a; printf("%p, %d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); // // return 0; }
第十题
int main() { int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int* ptr1 = (int*)(&aa + 1); int* ptr2 = (int*)(*(aa + 1)); printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); return 0; }
第十一题
#include <stdio.h> //阿里的笔试题 int main() { char* a[] = { "work","at","alibaba" }; char** pa = a; pa++; printf("%s\n", *pa); return 0; }
第十二题
int main() { char* c[] = { "ENTER","NEW","POINT","FIRST" }; char** cp[] = { c + 3,c + 2,c + 1,c }; char*** cpp = cp; printf("%s\n", **++cpp); printf("%s\n", *-- * ++cpp + 3); printf("%s\n", *cpp[-2] + 3); printf("%s\n", cpp[-1][-1] + 1); return 0; }