序言:
c语言每日一练系列,每一期都包含5~8个小题,1~2道编程题,我会尽可能详细地进行讲解,令初学者也能听的清晰。每日一练系列会持续更新,尽情期待!
一、程序分析题
1、下面程序的运行结果是什么?
#include <stdio.h> int main() { int a[5] = { 1, 2, 3, 4, 5 }; int *ptr = (int *)(&a + 1); printf( "%d,%d", *(a + 1), *(ptr - 1)); return 0; }
解析:a表示首元素地址,a+1表示第二个元素地址,*(a+1)表示第二个元素2;
&a是整个数组地址,&a+1表示跳过整个数组,指向数组后面的地址,(int *)(&a + 1)表示将(&a + 1)强制转换为整型指针,ptr为整型指针,里面放着5后面的地址,ptr是整型指针,所以ptr-1表示向后跳过四个字节,*(ptr - 1)表示解引用四个字节,即数字5
答案:2,5
2、 在X86环境下 ,假设结构体的⼤⼩是20个字节 ,程序输出的结构是啥?
struct Test { int Num; char *pcName; short sDate; char cha[2]; short sBa[4]; }*p = (struct Test*)0x100000; int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; }
解析:p是一个结构型指针,p+0x1相当于p+1,表示跳过一个结构体,指向结构体后面的地址----->--------->20转化为16进制为0x14,所以答案为0x100014;
(unsigned long)p将p强制类型转化为无符号长整型,所以(unsigned long)p + 0x1表示0x100000+1==0x100001;
(unsigned int*)p将p强制类型转化为无符号整型指针,(unsigned int*)p + 0x1表示跳过一个无符号整型大小的地址———>0x100000+4==0x100004
答案:
3、下面程序的运行结果是什么?
#include <stdio.h> int main() { int a[4] = { 1, 2, 3, 4 }; int *ptr1 = (int *)(&a + 1); int *ptr2 = (int *)((int)a + 1); printf("%x,%x", ptr1[-1], *ptr2); return 0; }
解析:ptr1整型指针中存储整个数组a后面的地址,ptr1[-1]------>*(ptr1-1)表示元素4;
(int)a + 1将首元素的地址强制转化为整型然后加1, (int *)((int)a + 1)再强制类型转换为整型指针,ptr2为数组第二个字节的地址,*ptr2表示向后读取四个字节数字。
答案:
4、下面程序的运行结果是什么?
#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]={1,3,5},a[0]为第一行数组名,表示首元素地址,p里面为1的地址,p[0]==*(p+0)。
答案:1
5、假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h> 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; }
解析:a是二维数组数组名,表示第一行数组地址,而p是存放四个元素的一维数组指针,&p[4]表示p+4,p每次加一跳过一个int [4],p+4如图所示;&p[4][2]表示&p[4]向后在找2个元素的地址,如图所示;
&p[4][2] - &a[4][2]表示指针-指针为俩地址之间元素个数,用整型打印出来为-4;
用地址打印出来,则是-4在内存中的存储,-4在内存以补码形式存储,转化十六进制为ff ff ff fc。
答案:
6、下面程序的运行结果是什么?
#include <stdio.h> 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; }
解析:&aa+1表示跳过整个二维数组的地址;ptr1-1表示向前跳过一个整型的地址, *(ptr1 - 1)即元素10;
*(aa + 1)==aa[1],第二行数组名相当于第二行第一个元素地址,即6的地址;*(ptr2 - 1)表示向前跳过一个整型的地址然后解引用,即5.
答案:
7、下面程序的运行结果是什么?
#include <stdio.h> int main() { char *a[] = {"work","at","alibaba"}; char**pa = a; pa++; printf("%s\n", *pa); return 0; }
解析:常量字符串作为表达式时,它的值是字符首元素的地址,a为指针数组数组名,表示'w'的地址的地址,pa++表示跳过一个char*的地址,即'a'的地址的地址,*pa表示'a'的地址,打印结果at。
答案:at
8、下面程序的运行结果是什么?
#include <stdio.h> 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; }
解析:c数组里面的元素依次是'E','N','P','F'的地址(char*),cp数组里面的元素依次是'F','P','N','E'的地址的地址(char**),cpp里面的元素是首元素的地址(charr***);
++cpp是cp中第二个元素地址,*++cpp是第二个元素,**++cpp是'P'的地址,打印出来即POINT;
++cpp是cp中第三个元素地址,*++cpp表示cp第三元素(c+1),--*++cpp表示c中第一个元素地址(c),*--*++cpp表示'E'的地址,*--*++cpp+3表示跳过三个字符的地址,打印出来即ER;
cpp[-2]==*(cpp-2)表示cpp往前跳过俩个char**后的地址再解引用,即cp首元素(c+3),
*cpp[-2]表示c中第四个元素即'F'的地址,*cpp[-2]+3表示跳过三个字符的地址,打印出来即ST;
cpp[-1][-1]==*(*(cpp-1)-1),*(cpp-1)表示cp中的第二个元素(c+2),*(*(cpp-1)-1)表示c中第二个元素,即'N'的地址,cpp[-1][-1]+1表示跳过1个字符的地址,打印出来即EW.
二、编程题
1、实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
分析:
思路一:若字符串长度为5的情况下,旋转6、11、16...次相当于1次,7、12、17...次相当于2次,以此类推。所以要对旋转次数处理,真实次数为旋转次数%字符串长度;一次左旋为从第二个字符开始一次向前挪移一位,在将第一个字符放在最后,左旋几次就循环几次
//思路一: void leftRound(char* str, int k) { int i, j, tmp; int len = strlen(str); k =k% len; //长度为5的情况下,旋转6、11、16...次相当于1次,7、12、17...次相当于2次,以此类推。 for (i = 0; i < k; i++) //执行k次的单次左旋 { tmp = str[0]; for (j = 0; j < len - 1; j++) //单次左旋 { str[j] = str[j + 1]; } str[j] = tmp; } } int main() { char arr[] = { "ABCDE" }; int k = 0; scanf("%d", &k); leftRound(arr, k); printf("%s", arr); return 0; }
思路二:创造一个新字符数组,将原数组分为俩部分,旋转部分和剩余部分,将剩余部分copy到新数组,将旋转部分拼接到原数组,最后copy回来。
//思路二 void leftRound(char* str, int k) { int len = strlen(str); int pos = k % len; //断开位置的下标 char tmp[100] = { 0 }; strcpy(tmp, str + pos); //先将后面的全部拷过来 strncat(tmp, str, pos); //然后将前面几个接上 strcpy(str, tmp); //最后拷回去 } int main() { char arr[] = { "ABCDE" }; int k = 0; scanf("%d", &k); leftRound(arr, k); printf("%s", arr); return 0; }
思路三:
ABCDEFG,左旋3次后变成DEFGABC,有一个特殊的操作方式:先将要左旋的前三个字符串逆序(CBADEFG),然后将后半段也逆序(CBAGFED),最后整体逆序(DEFGABC)即可。这样只需要做数值交换即可,可以写一个函数帮我们完成局部逆序,代码如下:
//思路三 void leftRound(char* str, int k) { int len = strlen(str); k = k % len; int left = 0; int right = k - 1; while (left < right) { char tmp = str[left]; str[left] = str[right]; str[right] = tmp; left++; right--; } left = k; right = len - 1; while (left < right) { char tmp = str[left]; str[left] = str[right]; str[right] = tmp; left++; right--; } left = 0; right = len - 1; while (left < right) { char tmp = str[left]; str[left] = str[right]; str[right] = tmp; left++; right--; } } int main() { char arr[] = { "ABCDE" }; int k = 0; scanf("%d", &k); leftRound(arr, k); printf("%s", arr); return 0; }
2、
两个整数二进制位不同个数_牛客题霸_牛客网
输入两个整数,求两个整数二进制格式有多少个位不同。题目来自【牛客题霸】
https://www.nowcoder.com/practice/16e48900851646c0b2c6cdef9d7ea051?tpId=182&tqId=34802&ru=/exam/oj
思路一:一个整型二进制共32位,将俩个整型二进制对应位分别&1
如果:
结果是0,则最后一个比特位是0
结果是非0,则最后一个比特位是1
俩结果比较,不同则计数1;然后让俩个二进制同时右移一位
以上内容循环32次
//思路一 #include <stdio.h> int main() { int a, b; int count=0; while (scanf("%d %d", &a, &b) != EOF) { // 注意 while 处理多个 case int i=0; for(i=0;i<32;i++) { if((a&1)!=(b&1)) { count++; } a=a>>1; b=b>>1; } printf("%d",count); } return 0; }
思路二:
1. 先将m和n进行按位异或,此时m和n相同的二进制比特位清零,不同的二进制比特位为1。
2. 统计异或完成后结果的二进制比特位中有多少个1即可
//思路二 #include <stdio.h> int calc_diff_bit(int m, int n) { int tmp = m^n; int count = 0; while(tmp) { tmp = tmp&(tmp-1);//每进行一次则tmp少一个1,若tmp=100101,tmp = tmp&(tmp-1),则tmp=1001 count++; } return count; } int main() { int m,n; while(scanf("%d %d", &m, &n) == 2) { printf("%d\n", calc_diff_bit(m, n)); } return 0; }
今天的内容到此结束了,祝大家学有所成,天天开心!