上一篇:从0开始学c语言-28-qsort函数、 数组和指针参数、函数指针数组(转移表)、回调函数_阿秋的阿秋不是阿秋的博客-优快云博客
目录
练习1:整型提升和截断练习
int main()
{
unsigned char a = 200;
unsigned char b = 100;
unsigned char c = 0;
c = a + b;
printf(“%d %d”, a+b,c);
return 0;
}
先不说结果,一步步分析
unsigned char a = 200;
00000000 00000000 00000000 11001000 - 200的补码
11001000 - a
unsigned char b = 100;
00000000 00000000 00000000 01100100 - 100的补码
01100100 - b
11001000 - a
01100100 - b
c = a + b
//因为a和b不够int大小,所以在相加的时候需要进行整型提升
//而a和b都是无符号整型,所以高位补0
00000000 00000000 00000000 11001000 - a
00000000 00000000 00000000 01100100 - b
00000000 00000000 00000001 00101100 - a+b
//又因为c是无符号整型的char整型
//需要进行截断保存
00101100 - c
所以,
printf(“%d %d”, a+b,c);
a+b=300
c=44
练习2:大小端
unsigned int a = 0x1234;
unsigned char b = *(unsigned char *)&a;
在32位大端模式处理器上变量b等于?
我们先补齐一下a变量。
unsigned int a = 0x1234;
0x1234 ——> 0x00001234
unsigned char b = *(unsigned char *)&a;
0x00001234 - a
因为题目说是大端模式处理器,所以高字节位数字放到内存中的低地址。
那么内存中从低地址到高地址a的储存样子应该是(低地址)00001234(高地址)。
此时 &a 取到的是00 00 12 34当中00字节的地址,能够访问四个字节,又因为对&a进行了强制类型转换,那么便只能取到一个字节的内容,先读取低地址的,也就是00字节的地址。
然后对这个地址进行解引用存储到 char b 变量中。
所以 b=00 。
练习3:杨辉三角
这就是杨辉三角,但是这样看不好转换成代码来写,
我们换成这样。
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
可以看到第一列和每行最后一个数字都是1,然后每行1和1中间的数字(共三种情况),比如:6=3+3
6(1和1之间的数字)
3(6自己列上面一行的数字)
3(6自己列上面一行数字的前一列数字)
然后又观察到第一行一个数字,第二行两个数字,第n行就是n个数字了。
然后我就写出来了
#define ROW 10
#define RAN 10
int main()
{
//int ROW, RAN;
//scanf("%d %d", &ROW, &RAN);
//int arr[ROW][RAN]; //这样输入值的数组不行,因为创建数组的数值必须是常量
int arr[ROW][RAN]; //这样定义的就行
int i = 0;
int j = 0;
for (i = 0; i < ROW; i++)
{
for (j = 0; j <= i; j++)
{
if ((0 == j) || (i == j))
{
arr[i][j] = 1;
}
else
{
arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];
}
}
}
for (i = 0; i < ROW; i++)
{
for (j = 0; j <= i; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
如果想打印成三角的样子,就试着学学接下来这道题来借鉴一下
练习4:打印菱形
int main()
{
int line = 0;
int i = 0;
scanf("%d", &line);//7
//打印上半部分
for (i = 0; i < line; i++)
{
//打印一行
//打印空格
int j = 0;
for (j = 0; j < line - 1 - i; j++)
{
printf(" ");
}
//打印*
for (j = 0; j < 2 * i + 1; j++)//奇数
{
printf("*");
}
printf("\n");
}
//打印下半部分
for (i = 0; i < line - 1; i++)
{
//打印一行
int j = 0;
for (j = 0; j <= i; j++)
{
printf(" ");
}
for (j = 0; j < 2 * (line - 1 - i) - 1; j++)
{
printf("*");
}
printf("\n");
}
return 0;
}
练习5:猜凶手
日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。
以下为4个嫌疑犯的供词:
A说:不是我。
B说:是C。
C说:是D。
D说:C在胡说
已知3个人说了真话,1个人说的是假话。
现在请根据这些信息,写一个程序来确定到底谁是凶手。
int main()
{
int killer = 0;
for (killer = 'A'; killer <= 'D'; killer++)
{
if ((killer != 'A') + (killer == 'C') + (killer == 'D') + (killer != 'D') == 3)
{
printf("%c\n", killer);
}
}
return 0;
}
练习6:猜名次
5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果:
A选手说:B第二,我第三;
B选手说:我第二,E第四;
C选手说:我第一,D第二;
D选手说:C最后,我第三;
E选手说:我第四,A第一;
比赛结束后,每位选手都说对了一半,请编程确定比赛的名次。
代码中检查不能重复的函数代码是具有普适性的,这道题也可以投机取巧,去掉检查重复的函数,改为if条件语句(五个数相乘=120 也就等价于不重复)进行输出。
int checkData(int *p)
{
int tmp[7] = { 0 }; //标记表,实际是哈希表的思路。一开始每个元素都是0。
int i;
for (i = 0; i < 5; i++)
{
if (tmp[p[i]]) //如果这个位置的标记已经是1,则代表重复,直接返回0。
{
return 0;
}
tmp[p[i]] = 1; //如果不是,则给这个位置标记为1。
}
return 1; //全部标记完毕也没有出现重复的情况,代表OK。
}
int main()
{
int p[5]; //0 1 2 3 4分别代表a b c d e
for (p[0] = 1; p[0] <= 5; p[0]++)
{
for (p[1] = 1; p[1] <= 5; p[1]++)
{
for (p[2] = 1; p[2] <= 5; p[2]++)
{
for (p[3] = 1; p[3] <= 5; p[3]++)
{
for (p[4] = 1; p[4] <= 5; p[4]++) //五层循环遍历
{
//这里是五个人的描述,由于比较表达式只有0和1两个结果,如果要两个条件
//有且只有一个为真,则可以用比较表达式的值总和为1的方式直接判定。别忘了还要判定不能并列。
if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三
(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四
(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二
(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三
(p[4] == 4) + (p[0] == 1) == 1 && //我第四,A第一
checkData(p) //不能并列
)
{
for (int i = 0; i < 5; i++)
{
printf("%d ", p[i]);
}
putchar('\n');
}
}
}
}
}
}
return 0;
}
练习7:知识补充
c语言本身是没有输入输出语句的,它只规定了各种语法规则,它是引用库函数里的输入输出函数,c语言知识规定了函数的参数、返回类型和功能,至于怎么实现的不重要,每个厂商自己实现。
练习8:求最小公倍数
正整数A和正整数B的最小公倍数是指 能被A和B整除的最小的正整数值,设计一个算法,求A和B的最小公倍数。
输入:5 7 输出:35
其实在之前这个文章里我们说过最大公约数和最小公倍数的关系从0开始学c语言-过渡-练习之猜随机数字游戏、求最大公约数_阿秋的阿秋不是阿秋的博客-优快云博客
最小公倍数=输入的两个数相乘 / 最大公倍数
方法1
这里我们先按照题目给的思路写一个:正整数A和正整数B的最小公倍数是指 能被A和B整除的最小的正整数值
int main()
{
int a, b;
scanf("%d %d", &a, &b);
int m = a > b ? a : b; //找较大的数
while (1)
{
if (m % a == 0 && m % b == 0)
{
printf("%d\n", m);
break;
}
m++;
}
return 0;
}
方法2
最小公倍数=输入的两个数相乘 / 最大公倍数
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int max = a > b ? b : a; //找两个数中较小的那个数
while (1)
{
if (a % max == 0 && b % max == 0)
{
printf("最大公约数是:%d \n", max);
break;
}
max--;
}
int p=(a*b)/max;
printf("最小公倍数%d \n",p);
return 0;
}
练习9:倒置一句话的单词
输入:I like games.
输出:games like I.
可以看到我们输入和输出的结果对比,标点都跟着倒置。
首先写个主函数
int main()
{
char arr[100] = { 0 };
gets(arr); //scanf不读取空格
int len = strlen(arr);
//两步翻转法(左旋会用到三步翻转法
//1·字符串整体逆序
//2·每个单词逆序。
return 0;
}
然后写怎么翻转整个字符串的reverse函数
因为逆序字符串,那就需要传递字符串的地址和长度,分别用来改变字符串内容和确认字符串下标。
void reverse(char* arr, int len)
{
assert(arr);
int left = 0;
int right = len - 1;
while (left<right)
{
char tmp = *(arr+left);
*(arr+left) = *(arr + right);
*(arr + right) = tmp;
left++;
right--;
}
}
然后考虑怎么逆序每个单词,要知道的是我们是用空格来划分单词的,那就很重要了。
利用好空格,来使用reverse函数就可以实现了。
在写逆序每个单词函数的时候,我发现上面的reverse函数不太好用了,得每次确定长度,确定长度还得自己想想该怎么设才正好,不如弄两个指针来作为reverse函数的参数方便。
reverse函数就改写成这样了。
void reverse(char* left, char*right)
{
assert(left&&right);
while (left<right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
这是我的思考过程,之后就很快写好了~
注意:我的表达式是以每次arr+i指向空格和\0位置为基础进行的运算,为的是确定这次的终点以及下次的起点。
#include <string.h>
#include <assert.h>
#include <stdio.h>
void reverse(char* left, char*right)
{
assert(left&&right);
while (left<right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
void reverse_word(char* arr, int len)
{
int i = 0;
char* ret = arr;
char* end = NULL;
for (i = 0; i < len; i++)
{
if ((*(arr+i) == ' ')||(*(arr + i) == '\0'))
{
end = arr + i-1;
reverse(ret, end);
ret = end +2; //为了让下一个ret指向下次reverse的起点
}
}
}
int main()
{
char arr[100] = { 0 };
gets(arr); //scanf不读取空格
int len = strlen(arr);
//两步翻转法(左旋会用到三步翻转法
//1·字符串整体逆序
reverse(arr, arr+len-1);
//2·每个单词逆序。
reverse_word(arr, len);
printf("%s\n", arr);
return 0;
}