Bootstrap

站在巨人的肩膀上 C语言理解和简单练习(包含指针前的简单内容)

1.格式化的输入/输出

1.1printf函数

printf函数你需要了解的就是转换说明,转换说明的作用是将内存中的二进制转换成你所需要的格式入%d就是将内存中存储的变量的二进制转化为十进制并打印出来,同时我们可以在%X的转换说明对精度和最小字段宽度的指定,更多内容可以了解C语言程序设计现代方法这本书。

1.2scanf函数

输入部分的内容可以看前面我写的博客你真的懂scanf函数吗

1.3练习

题目和代码如下所示
注意因为一个项目只能包含一个main函数因此博主为了一个源文件能进行多次练习同时避免注释
作者将main函数写成了main1,main2的形式。如果你想运行的话麻烦将数字去掉

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//purpose:下面的printf函数调用产生的输出分别是什么?这部分内容可以看我写的转换部分区别的博客
//date:24/6/24
int main1()
{

	printf("%6d,%4d\n", 86, 1040);
	printf("%12.5e\n", 30.253);
	printf("%4.f\n", 83.162);
	printf("%-6.2e\n", .00000009979);
	printf("%-6.2g\n", .00000009979);
	return 0;
}
//purpose:编写printf函数调用以下列格式来显示float型变量x
//date:24/6/24
int main2()
{
	float x = 1.f;
	printf("%-8.1e\n", x);
	printf("%10.6e\n", x);
	printf("%-8.3f\n", x);
	printf("%6.f\n", x);
	return 0;
}
//purpose:说明下列每对scanf格式串是否等价?如果不等价,请指出他们的差异?这部分内容可以看我写的你真的懂scanf函数吗
//date:24/6/24
int main3()
{
	int a,b,c;
	int ret = scanf("%d", &a);
	printf("%d %d\n", ret, a);
	ret = scanf(" %d", &a);
	printf("%d %d\n", ret, a);
	ret = scanf("%d-%d-%d", &a, &b, &c);
	printf("%d %d %d %d\n", ret, a,b,c);
	ret = scanf("%d -%d -%d", &a, &b, &c);
	printf("%d %d %d %d\n", ret, a, b, c);
	return 0;
}
//purpose:编写一个程序,接受用户录入的日期信息并且将其显示出来。其中,输入日期的形式为月/日/年(即mm/dd/yy),输出日期的形式为年月日(即yymmdd)。格式如下所示:
//date:24/6/24
int main4()
{
	int mm, dd, yy;
	printf("Enter a date(mm/dd/yy):");
	int ret = scanf("%d/%d/%d", &mm, &dd, &yy);
	printf("You entered the date %d%d%d", yy, mm, dd);//将月转化一下如果输出2输出02,如果是10,11,12则不必这里就不这样了
	return 0;
}
//purpose:编写一个程序,对用户录入的产品信息进行格式化。格式运行后需要如下会话:自己执行一下这里就不写出了
//date:24/6/24
int main6()
{
	int mm, dd, yy, number;
	float price;
	printf("Enter item number:");
	scanf("%d", &number);
	printf("Enter unit price:");
	scanf("%f", &price);
	printf("Enter a date(mm/dd/yy):");
	scanf("%d/%d/%d", &mm, &dd, &yy);
	printf("Item\tUnit\tpurchase\n");//\t真是一个好工具,很容易就保持纵向的对齐
	printf("\tPrice\tDate\n");
	printf("%-d\t$%7.2f\t%d/%d/%d", number, price, mm, dd, yy);
	return 0;
}
//purpose:输入10.3 5 6变量存储的值是什么,scanf函数是一个模式匹配的函数
//date:24/6/24
int main7()
{
	int i, j;
	float x;
	int ret = scanf("%d%f%d", &i, &x, &j);
	printf("%d\n%d %f %d", ret, i, x, j);
	return 0;
}
//purpose:输入12.3 45.6 789变量存储的值是什么,scanf函数是一个模式匹配的函数
//date:24/6/24
int main8()
{
	float x, y;
	int i;
	int ret = scanf("%f%d%f", &x, &i, &y);
	printf("%d\n%f %d %f", ret, x, i, y);
	return 0;
}

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//purpose:描述
/*给定两个整数a和b(0 < a, b < 10, 000),计算a除以b的整数商和余数。
	输入描述:
	一行,包括两个整数a和b,依次为被除数和除数(不为零),中间用空格隔开。
	输出描述:
	一行,包含两个整数,依次为整数商和余数,中间用一个空格隔开。
*/
//date:24/9/25
int main1()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	int ret1 = a / b;
	int ret2 = a % b;
	printf("%d %d", ret1, ret2);
	return 0;
}
/*purpose:
描述
据说智商140以上者称为天才,KiKi想知道他自己是不是天才,请帮他编程判断。输入一个整数表示一个人的智商,如果大于等于140,则表明他是一个天才,输出“Genius”。
输入描述:
多组输入,每行输入包括一个整数表示的智商。
输出描述:
针对每行输入,输出“Genius”。
*/
//date:24/9/25
int main2()
{
	int a = 0;
	while (scanf("%d", &a) != EOF)
	{
		if (a >= 140)
			printf("Genius");
	}
	return 0;
}
/*purpose:
描述
KiKi想知道从键盘输入的两个数的大小关系,请编程实现。
输入描述:
题目有多组输入数据,每一行输入两个整数(范围 - 231~231 - 1),用空格分隔。
输出描述:
针对每行输入,输出两个整数及其大小关系,数字和关系运算符之间没有空格,详见输入输出样例。
*/
//date:24/9/25
int main3()
{
	int a = 0;
	int b = 0;
	while (scanf("%d %d", &a, &b) != EOF)
		if (a == b)
			printf("%d=%d\n", a, b);
		else if(a > b)
			printf("%d>%d\n", a, b);
		else
			printf("%d<%d\n", a, b);
			
	return 0;
}
/*purpose:
描述
判断一个整数是否能5整除是一个挺简单的问题,懒惰的KiKi还是不想自己做,于是找到你帮他写代码,你就帮帮他吧。
输入描述:
输入包括一个整数M(1≤M≤100, 000)。
输出描述:
输出包括一行,如果M可以被5整除就输出YES,否则输出NO(结果大小写敏感)。
*/
//date:24/9/25
int main4()
{
	int a = 0;
	while (scanf("%d/n", &a) != EOF)
		if (a % 5 == 0)
			printf("YES");
		else
			printf("NO");

	return 0;
}
/*purpose:
描述
KiKi学习了循环,BoBo老师给他出了一系列打印图案的练习,该任务是打印用“*”组成的线段图案。
输入描述:
多组输入,一个整数(1~100),表示线段长度,即“*”的数量。
输出描述:
针对每行输入,输出占一行,用“*”组成的对应长度的线段。
*/
//date:24/9/25
int main5()
{
	int a = 0;
	while (scanf("%d", &a) != EOF)
	{
		int i = 0;
		while (i < a)
		{
			printf("*");
			i++;
		}
		printf("\n");

	}
	return 0;
}
/*purpose:
写一个代码打印1 - 100之间所有3的倍数的数字
*/
//date:24/9/25
int main6()
{
	for (int i = 3; i <= 100; i += 3)
		printf("%d ", i);

	return 0;
}
/*purpose:
写代码将三个整数数按从大到小输出。
例如:
输入:2 3 1
输出:3 2 1
*/
//date:24/9/25
int main7()
{
	int a,b,c,tmp;
	while (scanf("%d %d %d", &a, &b, &c) != EOF)
	{
		if (a < b)
		{
			tmp = a;
			a = b;
			b = tmp;
		}
		if (a < c)
		{
			tmp = a;
			a = c;
			c = tmp;
		}
		if (b < c)
		{
			tmp = b;
			b = c;
			c = tmp;
		}
	}
	printf("%d %d %d", a, b, c);

			return 0;
}

2.表达式

这部分的内容主要是有关结合性和优先级。可以看我的博客恼人的结合性和结合性,副作用。
C语言的线束特征之一就是它更多地强调表达式而不是语句,最简单的表达式是变量和常量,简单来说5也是个表达式。
赋值操作符=
赋值操作符要求它左边的操作数必须是左值,变量是已知唯一的左值。
在C语言中任何表达式都可以作为语句,但是并不是所有表达式语句都是有意义的。
如5;i+1;j+k,这些表达式语句的值没有任何作用会被丢弃,其实i++;原本也没有意义但是由于表达式副作用会导致i自增因此该表达式语句有用。
练习

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//purpose:读入三位数将其反向,这里提供两次方法
//date:24/06/24
int main1()
{
	int num;
	printf("Enter a three-digit number:");
	scanf("%d", &num);
	printf("The reversal is:%d%d%d\n", num % 10, num / 10 % 10, num / 10 / 10 % 10);//模10除10的办法
	return 0;
}
int main1_1()
{
	int a, b, c;
	printf("Enter a three-digit number:");
	scanf("%1d%1d%1d", &a, &b, &c);//我们使用scanf函数时可以指定读入的大小
	printf("The reversal is:%d%d%d\n", c, b, a);
	return 0;
}
//purpose:测试优先级结合性
//date:24/06/24
int main2()
{
	int i, j, k;
	i = j = k = 1;
	i += j += k;
	printf("%d %d %d", i, j, k);
	return 0;
}
int main3()
{
	int i, j, k;
	i = 3; j = 4; k = 5;
	printf("%d ", i++ - j++ + --k);
	printf("%d %d %d", i, j, k);
	return 0;
}

在这里插入图片描述
这道题大家自己思考一下,讲一下逗号表达式,逗号表达式只有最后一个才会起作用也就是b=a++。前面的表达式会执行,没有任何作用的值会丢弃,但由于副作用(自增自减带来的操作数的改变)因此c和a都会改变

这块内容一定要考虑可移植性的问题,尽量写出不会引起歧义的表达式

3.选择语句

除了表达式语句外,C语言的其他语句大多属于以下3大类

  • 选择语句 if语句和switch 语句
  • 循环语句 while语句、do语句和for语句支持重复(循环)操作
  • 跳转语句 break语句、continue语句和goto语句引起无条件地跳转到程序的某个位置。(return语句也属于此类)
    除此之外还有两类语句复合语句和空语句。
    关系运算符,逻辑运算符,判等运算符索长生的结果都是0或1
    switch语句语句中往往因包含break语句这是因为switch语句是一种基于计算的跳转,它会跳转到与表达式匹配的情况标号处也就是0,1,2,3,4,5,6等等情况标号处然后执行完语句之后它会忽略下一种情况标号继续执行下一种情况
    练习(这部分练习将联合循环一起)
#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//purpose:practice
//Date:24/6/13
//编写一个单独的表达式,要求这个表达式的值根据i是否小于、等于或大于j而分别为-1、0或+1
int main1()
{
	int i, j;
	for (;;)
	{
		printf("Enter two number:");
		int ret=scanf("%d%d", &i, &j);
		int a = (i > j) - (i < j);
		printf("%d\n", a);
	}
	return 0;
}	

4.循环语句

循环语句包括while语句,do while语句,for语句
其实往往我们可以将for语句和dowhile语句理解为while语句的变种
如for(int i=0;i<n;i++)
可以理解为
int i=0;
while(i<n)
i++;
这里也许会帮助你理解为什么for语句中使用分号而不是,因为这里是单独的语句。
空循环模拟
for(; ;); while(1); 这两条语句模拟了空语句的无限循环
for里面至少包含两个分号
break语句可以用于跳出循环和判断
continue无法跳出循环它是调到循环体结束之前的一点
goto语句可以无限制的进行跳转,但尽量避免使用goto语句随意跳转的程序可能使得程序难以修改
但是面对嵌套的循环的使用break语句仅可以跳出当前循环(跳出一层循环),此时就可以使用goto语句。
练习

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//purpose:编写程序,要求找到用户输入的一串数中的最大数。程序需要提示用户一个一个输入数。当用户输入0或负数时,程序必须显示输入的最大非负数
//date:24/6/13
int main1()
{
	float num,tmp;
	printf("Enter a number:");
	scanf("%f", &num);
	if (num <= 0)
		return 1;
	tmp = num;
	while (num > 0)
	{
		printf("Enter a number:");
		scanf("%f", &num);
		if (tmp < num)
			tmp = num;
	}
		printf("The largest number entered was %g\n", tmp);
	return 0;
}
//purpose:编写程序,要求用户输入两个整数,然后计算并显示这两个整数的最大公约数(GCD)
//date:6/13
int main2()
{
	int m, n,tmp;
	printf("Enter two integers:");
	scanf("%d%d", &m, &n);
	while (n != 0)
	{
		tmp = m % n;
		m= n;
		n = tmp;
	}
	printf("Greatest common divisor: %d\n", m);
	return 0;
}
//purpose:编写程序,要求用户输入一个分数,然后将其约分为最简分式
//date:6/13
int main3()
{
	int m, n, tmp,a,b;
	printf("Enter a fraction:");
	scanf("%d/%d", &m, &n);
	a = m; b = n;
	while (n != 0)
	{
		tmp = m % n;
		m = n;
		n = tmp;
	}
	printf("In lowest terms:%d/%d", a / m, b / m);
	return 0;
}
//purpose:编写程序,要求用户输入一个数n,然后显示出1~n的所有偶数平方。
//date:6/13
int main4()
{
	int n,i;
	printf("Enter a number:");
	scanf("%d", &n);
	for (i = 1; i < n;i++)
	{
		if (i % 2 == 0)
			printf("%d\n", i * i);
	}
	return 0;
}
//purpose:编写程序,要求显示出单月的日历。用户说明这个月的天数和本月起始日是星期几
//date:6/13
int main()
{
	int m, d,i,j;
	printf("Enter number of days in month:");
	scanf("%d", &m);
	printf("Enter starting day of the week(1=Sun,7=Sat):");
	scanf("%d", &d);
	for (j = 1; j < d; j++)
	{
		printf("   ");
	}
	for (i = 1; i <= m; i++)
	{
		printf("%2d ", i);
		if ((i + j - 1) % 7 == 0)
			printf("\n");//判断是否是一个星期的最后一天,若是则进行换行
	}
	return 0;
}
//purpose:测试
//date:6/13
int main6()
{
	int i, j;
	for (i = 5, j = i - 1; i > 0, j > 0; --i, j = i - 1)
		printf("%d ", i);
	return 0;
}
//purpose:重写下面的循环,从而使其循环体为空for(n = 0;m>0;n++) m/=2;
//date:6/13
int main7()
{
	int m, n;
	scanf("%d", &m);
	for (n = 0; m > 0; n++, m /= 2);
	return 0;
}

5.基本类型

5.1可移植性和精确

考虑到可移植性技巧
对不超过32767的整形采用int,二队其他的整数采用long int
十进制数
八进制数必须以0开头
十六进制数总是以0x开头后面字母可以大写也可以小写,大小写混用也行
当然这只是一种存储形式不会影响到内存的存储
我们对整数默认为int
如果想指定长整型可以用L,无符号可以用U,也可以同时要求且大小顺序无所谓
%o指明八进制,%x指明十六进制,短整型前面加h,长整型加l
但是存储浮点型若要存储double类型的值请用%lf来存储
往往我们默认浮点数为double类型,若想指定为float类型后面加f,当读写long double时前面加L(不要用小写)

5.2读写字符

读写字符我们往往使用getchar来读取

while(getchar()!='\n');

上述代码是一个空循环体是用来清空缓存区

5.3练习

代码练习:

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//purpose:编写程序可以把字母格式的电话号码翻译成数值格式
//对应关系:2=ABC,3=DEF,4=GHI,5=JKL,6=MNO,7=PRS,8=TUV,9=WXY
//date:24/6/15
int main1()//用scan来写
{
	int i = 0;
	char a[100] = { 0 };
	printf("Enter phone number:");
	scanf("%s", a);
	for (i; i < 100; i++)
	{
		if (a[i] == 'A' || a[i] == 'B' || a[i] == 'C')
		{
			a[i] = '2';
		}
		else if (a[i] == 'D' || a[i] == 'E' || a[i] == 'F')
		{
			a[i] = '3';
		}
		else if (a[i] == 'G' || a[i] == 'H' || a[i] == 'I')
		{
			a[i] = '4';
		}
		else if (a[i] == 'J' || a[i] == 'K' || a[i] == 'L')
		{
			a[i] = '5';
		}
		else if (a[i] == 'M' || a[i] == 'N' || a[i] == 'O')
		{
			a[i] = '6';
		}
		else if (a[i] == 'P' || a[i] == 'R' || a[i] == 'S')
		{
			a[i] = '7';
		}
		else if (a[i] == 'T' || a[i] == 'U' || a[i] == 'V')
		{
			a[i] = '8';
		}
		else if (a[i] == 'W' || a[i] == 'X' || a[i] == 'Y')
		{
			a[i] = '9';
		}
	}
	printf("%s", a);
	return 0;
}
int main1_2()//用getchar来写
{
	int i = 0;
	char ch;
	char a[100] = { 0 };
	printf("Enter phone number:");
	ch=getchar();
	while (ch != '\n')
	{
		if (ch >= 'A' && ch <= 'C')
			a[i++]=ch = '2';
		else if (ch >= 'D' && ch <= 'F')
			a[i++]=ch = '3';
		else if (ch >= 'G' && ch <= 'I')
			a[i++] = ch = '4';
		else if (ch >= 'J' && ch <= 'L')
			a[i++] = ch = '5';
		else if (ch >= 'M' && ch <= 'O')
			a[i++] = ch = '6';
		else if (ch == 'P' || ch == 'R' || ch == 'S')
			a[i++] = ch = '7';
		else if (ch >= 'T' && ch <= 'V')
			a[i++] = ch = '8';
		else if (ch >= 'W' && ch <= 'Y')
			a[i++] = ch = '9';
		else
			a[i++] = ch;
		ch = getchar();
	}
	printf("%s", a);
	return 0;
}
//purpose:飞机票有冗长的标识数字,例如47115497443.为了有效,最后一位数字必须与以其他为的数字为整体除以7后的余数相匹配。编写程序检查机票号是否有效
//date:24/6/15
int main2()
{
	printf("Enter ticket number:");
	char a;
	long long int b = 0, c;
	while ((a = getchar() )!= '\n')
	{
		a = a - 48;
		b = b * 10 + a;
	}
	c = b % 10;
	b/= 10;//舍弃末位
	if (b % 7 == c)
	{
		printf("VALLD");
	}
	else
		printf("INVALLD");
	return 0;
}
//purpose:使用typedef产生名为Int 8、Int 16和Int 32的类型。定义这些类型以便它们在你的机器上分别表示8位、16位和32位的整数
//date:24/6/15
typedef char Int8;
typedef short Int16;
typedef int Int32;

6.数组

这部分内容基本在我讲的C语言中的数组已经基本涵盖了
练习

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdbool.h>
//purpose:接下来这个程序用来检查书中是否有出现多余一次的数字。用户输入数后,程序显示Repeated digit或No Repeated digit
//若显示Repeated digit则显示重复的数字
//date:24/6/16
int main1()
{
	_Bool flag=true, digit_seen[10] = {false};//初始化数组
	//flag用来判断是否出现重复数字
	int digit,i=0;
	long  n;
	int digit_reapted[10] = { 0 };
	printf("Enter a num:");
	scanf("%ld", &n);
	if (n < 0)
		n *= -1;//另外想说的数组下标不能是负数,导致我们无法做到对负数的情况进行处理。当然这里也非常简单只需要判断所输入的值是否为负数,若是则*(-1)
	while (n != 0)
	{
		digit = n % 10;
		if (digit_seen[digit])
		{
			digit_reapted[digit] = digit;//用数组存储重复的数字
			flag = false;
		}
		digit_seen[digit] = true;
		n /= 10;
	}
	if (flag)
		printf("No Repeated digit");
	else
	{
		printf("Repeated digit(s) :");
		for (int i = 1; i < 10; i++)
		{
			if (digit_reapted[i] == i)//防止重复数字多于三次的时候重复打印
				printf("%d ", i);
		}
	}
	return 0;
}
//purpose:修改程序可以显示出一张列表,表内显示出每种数字在数中出现的次数,并且可以实现多次判断但当用户输入0时,程序终止
//date:24/6/16
int main2()
{
	const int Digit[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int Occurrences[10] = { 0 };
	int digit, a=0,b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;
	long  n;
	int digit_reapted[10] = { 0 };
	for (;;)
	{
		printf("Enter a num:");
		scanf("%ld", &n);
		if (n == 0)
			break;
		if (n < 0)
			n *= -1;
		while (n != 0)
		{
			digit = n % 10;
			switch (digit)
			{
			case 0:Occurrences[digit] = ++a; break;
			case 1:Occurrences[digit] = ++b; break;
			case 2:Occurrences[digit] = ++c; break;
			case 3:Occurrences[digit] = ++d; break;
			case 4:Occurrences[digit] = ++e; break;
			case 5:Occurrences[digit] = ++f; break;
			case 6:Occurrences[digit] = ++g; break;
			case 7:Occurrences[digit] = ++h; break;
			case 8:Occurrences[digit] = ++i; break;
			case 9:Occurrences[digit] = ++j; break;
			}
			n /= 10;
		}
		printf("Digit:\t\t");
		for (i = 0; i < 10; i++)
			printf("%d ", Digit[i]);
		printf("\n");
		printf("Occurences:\t");
		for (i = 0; i < 10; i++)
			printf("%d ", Occurrences[i]);
		printf("\n");
	}
	return 0;
}
//二维数组练习
//purpose:要成程序可以读取一个5*5的整数数组,然后显示出每行的求和结果和每列的求和结果
//date:24/6/16
#define M 5
#define N 5
int main3()
{
	int a[M][N] = { 0 },i,j,sum=0;
	printf("Enter row 1:");
	for (i = 0; i < N; i++)
	{
		scanf("%d", &a[0][i]);
	}
	printf("Enter row 2:");
	for (i = 0; i < N; i++)
	{
		scanf("%d", &a[1][i]);
	}
	printf("Enter row 3:");
	for (i = 0; i < N; i++)
	{
		scanf("%d", &a[2][i]);
	}
	printf("Enter row 4:");
	for (i = 0; i < N; i++)
	{
		scanf("%d", &a[3][i]);
	}
	printf("Enter row 5:");
	for (i = 0; i < N; i++)
	{
		scanf("%d", &a[4][i]);
	}
	printf("Row totals:   ");
	for (i = 0; i < M; i++)
	{
		for (j = 0; j < N; j++)
		{
			sum += a[i][j];
		}
		printf("%d ", sum);
		sum = 0;
	}
	printf("\n");
	printf("Column totals:   ");
	for (i = 0; i < M; i++)
	{
		for (j = 0; j < N; j++)
		{
			sum += a[j][i];
		}
		printf("%d ", sum);
		sum = 0;
	}
	return 0;
}

7.后面的博客

因为类型转换和函数这块内容比较重要我会另开章节讲解

;