C语言知识点自学总结


前言

自学C语言,做一个知识总结
参考教程地址中国大学mooc 翁凯C语言


一、计算机和编程语言

一、计算机语言
程序是用特殊的编程语言写出来表达如何解决问题的。
编程语言不是和计算机交谈的语言,而是描述要求计算机做事情的过程或方法。
二、算法
我们要让计算机做计算,就要找出计算的的步骤,然后用编程语言写出来。
计算机做的所有事情都叫做计算。
三、程序的执行
解释:借助一个程序,那个程序能试图理解你的程序,然后按照你的要求执行。
编译:借助一个程序,就像一个翻译,把你的程序翻译成计算机能懂的语言——机器语言——写到程序,然后,这个机器语言写的程序就能直接执行了。
四、解释语言和汇编语言
计算机不能直接的理解高级语言,只能直接理解机器语言,所以必须要把高级语言翻译成机器语言,计算机才能执行高级语言的编写的程序。翻译的方式有两种,一个是编译,一个是解释。
解释性语言是指它常用的执行机制是使用一个“解释器”来执行,解释器对于程序是一句一句“翻译”成机器语言来一句一句执行。解释型语言有特殊的计算能力
编译型语言是指它常用的执行机制是使用一个“编译器”来编译成机器语言,然后你就可以直接运行(执行)这个编译成的“可执行文件”。编译型语言有确定的运算性能。
各种编程语言本质没什么不同。所谓的“解释性”和“编译”指的是执行机制上的不同。


二、C语言基础入门

0.C语言概述

一、为什么是C
C很强
现代编程语言差异很小,几乎都是类C语言。
语言的能力和适用领域主要由库和传统所决定的。

二、C的用途
操作系统
嵌入式系统
驱动程序
底层驱动(图像引擎,图像处理,声音效果)

三、C的编程环境
C需要被编译才能被运行,所以需要编辑器,编译器或者IDE((Integrated Development Environment)
推荐编程软件:DevC++(简单好安装,,体积小,新手首选),MS Visual studio(宇宙第一IDE,功能太强,组件太丰富,占内存大),VScode(小巧轻便,支持多种语言,但要自己配置相应环境,麻烦)


1.变量和常量

一、输入输出

1、输入格式:scan(“”); 如果输入数字,用scan(“%d”,&number),%d表示要输入的是数字,输入的数字存给number这个变量,""内是要输入的内容。输入是以行为单位的,行的结束标志是你按下回车键,在按下回车键之前,程序不会读到你输入的任务东西。

2、输出格式:printf(“”); 如果输出数字,用printf(“%d”,number),%d表示要输出的是数字,输出的数字是number这个变量,用number替换%d,""内是要输出的内容。【注意】有无&的区别

二、变量

1、定义:顾名思义,一个可以变的量,用来保存数据的地方,数据可变。定义格式 <数据类型><变量名称>; 例:int amount;

2、名称:变量名叫标识符。用来区别标识。

3、命名规则:标识符只能由字母、数字和下划线组成的,首字符不能是数字,C语言的关键字不能用作标识符,标识符严格区分大小写。

4、常见关键字
C语言一共有32个关键字,根据关键字的作用,可分其为数据类型关键字、控制语句关键字、存储类型关键字和其它关键字四类。

12个数据类型关键字

关键字意义
char声明字符型变量或函数
short声明短整数类型变量或函数
int声明整型变量或函数
long声明长整型变量或函数
float声明字符型变量或函数
double声明双精度变量或函数
signed声明有符号类型变量或函数
unsigned声明无符号类型变量或函数
enum声明枚举类型
struct声明结构体变量或函数
union声明共用体(联合)数据类型
void声明函数无返回值或无参数,声明无类型指针
12个控制语句关键字
关键字意义
:-:-
if条件语句
else条件语句否定分支(与 if 连用)
for一种特别的循环语句
do循环语句的循环体
while循环语句的循环条件
switch用于开关语句
break结束循环
continue结束当前循环,开始下一轮循环
case开关语句分支
default开关语句中的“其他”分支
goto无条件跳转语句
return返回语句(可以带参数,也可不带参数)
4个存储类型关键字
关键字意义
:-:-
auto声明字符型变量或函数
extern对外声明引用
register声明寄存器变量
static声明静态变量
4个其他类型关键字
关键字意义
:-:-
const常类型,即声明一个只读变量
sizeof计算数据类型容量
typedef定义别名
volatile取消编译器优化,不使用缓存,说明变量在程序执行中可被隐含地改变

三、常量

常量是固定不变的量,不能赋值修改,变量可以赋值修改,(赋值符号是=,注意与数学里=含义不同,C中两个=才是等于,一个=是赋值)

const 是一个修饰符,例 const int i=1,i就变成了一个常量

给个C样例

#include <stdio.h>
int main()
{
printf("请分别输入身高的英尺和英寸," "如输入\"5 7\"表示5英尺7英寸:");
double foot;
double inch;
scanf("%lf %lf", &foot, &inch);
printf("身高是%f米。\n", ((foot + inch / 12) *0.3048));
return 0;
}

补充\n是转义字符。表示换行


2.计算

一、数据类型

1、浮点数:带小数点的数。人们借用浮点数来表达所有带小数点的数。两个整数的计算结果只能是整数,当浮点数和整数在一起,C会将整数转换成浮点数,进行浮点数的计算。
double表示双精度浮点数,float表示单精度浮点数。

2、格式
整数输入输出格式printf("%d",..) scanf("%d",&...)
带小数点的数printf("%f",...) scanf("%lf",&...)

一个样例

#include <stdio.h>
int main()
{
int a,b;
scanf("%d %d", &a, &b);
double c = (a+b)/2.0;
printf("%d和%d的平均值=%f\n", a, b, c);
return 0;
}

二、表达式

1定义:一个表达式是一系列运算符和算子的组合;运算符是指运算的动作,如+、-;算子是参与运算的量,可以是常量,变量和返回值。

2、四则运算符号

C符号意义
+
-
*
/除法取整
%除法取余

3、C语言运算符优先级

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

--

()

圆括号

(表达式)/函数名(形参表)

--

.

成员选择(对象)

对象.成员名

--

->

成员选择(指针)

对象指针->成员名

--

2

-

负号运算符

-表达式

右到左

单目运算符

~

按位取反运算符

~表达式

++

自增运算符

++变量名/变量名++

--

自减运算符

--变量名/变量名--

*

取值运算符

*指针变量

&

取地址运算符

&变量名

!

逻辑非运算符

!表达式

(类型)

强制类型转换

(数据类型)表达式

--

sizeof

长度运算符

sizeof(表达式)

--

3

/

表达式/表达式

左到右

双目运算符

*

表达式*表达式

%

余数(取模)

整型表达式%整型表达式

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

5

<<

左移

变量<<表达式

左到右

双目运算符

>>

右移

变量>>表达式

6

>

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

<

小于

表达式<表达式

<=

小于等于

表达式<=表达式

7

==

等于

表达式==表达式

左到右

双目运算符

!=

不等于

表达式!= 表达式

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

13

?:

条件运算符

表达式1?

表达式2: 表达式3

右到左

三目运算符

14

=

赋值运算符

变量=表达式

右到左

--

/=

除后赋值

变量/=表达式

--

*=

乘后赋值

变量*=表达式

--

%=

取模后赋值

变量%=表达式

--

+=

加后赋值

变量+=表达式

--

-=

减后赋值

变量-=表达式

--

<<=

左移后赋值

变量<<=表达式

--

>>=

右移后赋值

变量>>=表达式

--

&=

按位与后赋值

变量&=表达式

--

^=

按位异或后赋值

变量^=表达式

--

|=

按位或后赋值

变量|=表达式

--

15

逗号运算符

表达式,表达式,…

左到右

--

说明:
同一优先级的运算符,运算次序由结合方向所决定。

简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符

3.判断

1、判断的格式

if(条件成立)
{...
}
else
{...
}

if 和else 的后面都可以不跟大括号,不跟大括号的时候只有后面紧跟的一句有效。
2、判断的条件
计算两个值之间的关系叫做关系运算

运算符含义
= =相等
!=不等于
>大于
> =大于或等于
<小于
< =小于或等于

3、关系运算的结果
当两个值的关系符合关系运算符的预期时,关系运算的结果为整数1,否则为整数0
4、关系运算的优先级
所有关系运算符的优先级比算术运算的低,但比赋值运算的高。
==和!=比其他的低,连续的关系运算是从左向右有进行的。

补充
注释//,连续多行的注释用/* …*/
5、嵌套的判断
•当if的条件满足或者不满足的时候要执行的语句也 可以是一条if或if-else语句,这就是嵌套的if语句
• else总是和最近的那个if匹配 ,•缩进格式不能表示else的匹配
6、级联的if

if ( exp1 )	
st1;	
else if ( exp2 )	
st2;	
else	
st3;

7、if语句的常见错误
•忘了大括号(做好永远在if和else 后面加上大括号,即使后面只有一条语句)
•if 后面的分号(if 后面不要加分号)
•等号==和赋值=混淆(if 只要求里面的值是0或者非0)
8、多路分支 switch-case

switch ( 控制表达式 )
 {	
 case 常量:			语句			……	
 case 常量:			语句			……	
 default:			语句			……	
 }

• 控制表达式只能是整数型的结果
• 常量可以是常数,也可以是常数计算的表达式
• 根据表达式的结果,寻找匹配的case,并执行case后⾯面的 语句,一直到break为止
• 如果所有的case都不匹配,那 么就执行default后⾯面的语句; 如果没有default,那么就什么 都不做
break
• switch语句可以看作是⼀一种基于计算的 跳转,计算控制表达式的值后,程序会 跳转到相匹配的case(分⽀支标号)处。 分支标号只是说明switch内部位置的路标,在执行完分支中的最后一条语句后, 如果后面没有break,就会顺序执行到下面的case里去,直到遇到一个break,或 者switch结束为止

4.循环

1、while循环
格式

while ( x > 0 ) 
{	
	x /= 10 ;	
	n++;
	} 

流程图
在这里插入图片描述

• 如果我们把while翻译作“当”,那么一个 while循环的意思就是:当条件满足时,不断地重复循环体内的语句。
• 循环执行之前判断是否继续循环,所以有可能循环一次也没有被执行;
• 条件成立是循环继续的条件

2、do -while 循环
格式

do 	
{	
<循环体语句>	
} while ( <循环条件> );

流程图
在这里插入图片描述
• do-while循环和while循环很像,区别是在循环体执行结束的时候才来判断条件。也就是说,无论如何,循环都会执行至少一 遍,然后再来判断条件。与while循环相同的是,条件满足时执行循环,条件不满足时结束循环。注意do-while 循环后面的while要加分号
3、循环的应用
• 猜数游戏

  1. 计算机随机想一个数,记在变量number里;
  2. 一个负责计次数的变量count初始化为0;
  3. 让用户输入一个数字a;
  4. count递增(加一);
  5. 判断a和number的大小关系,如果a大,就输出 “大”;如果a小就输出“小”;
  6. 如果a和number是不相等的(无论大还是小),程序转回到第3步;
  7. 否则,程序输出“猜中”和次数,然后结束
    代码示例
#include <stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
	srand(time(0));
	int number=rand()%100+1;
	int count=0;
	int a=0;
	printf("我已经想好了一个1到100之间的数\n");
	do
	{
		printf("请输入你猜的数\n");
		scanf ("%d",&a);
		count++;
		if (a>number)
		{
			printf("大了\n");
		}
		else if (a<number)
		{
			printf("小了\n");
		}

	}
	while(a!=number);
	printf("你用了%d次猜对.\n",count);
	system("pause");
	return 0;
}

•求平均值

  1. 初始化变量sum和count的值为0
  2. 读入number;
  3. 如果number的值小于0,结束循环,输出sum/count,否则,count加1,number值加入sum,回到2步

代码示例

#include <stdio.h>
#include<stdlib.h>
int main()
{
	int number=0;
	int count=0;
	int sum=0;
	printf("请输入数\n");
	scanf ("%d",&number);
	while(number>0)
	{
		sum=sum+number;
		count++;
		scanf ("%d",&number);
	}
	printf("平均值是%f\n",1.0*sum/count);
	system("pause");
	return 0;
}

•整数逆序

  1. ⼀个整数是由1⾄多位数字组成的,如何分解出整数
    的各个位上的数字,然后加以计算
  2. 对⼀个整数做%10的操作,就得到它的个位数;
  3. 对⼀个整数做/10的操作,就去掉了它的个位数;
  4. 然后再对2的结果做%10,就得到原来数的十位数了;
  5. 依此类推。

代码示例

#include <stdio.h>
#include<stdlib.h>
int main()
{
	int number=0;
	int a=0 ;
	int ret=0;
	printf("请输入数\n");
	scanf ("%d",&number);
	while(number>0)
	{
		a=number%10;
		ret=ret*10+a;
		number/=10;
	}
	printf("逆序后的数是%d\n",ret);
	system("pause");
	return 0;
}

5.循环控制

5.1第三种循环(for 循环)

for ( 初始动作; 条件; 每轮的动作 )
{ }
• for中的每⼀个表达式都是可以省略的
for (; 条件; ) == while ( 条件 )

for( int i=1; i<=n; i++)
{ 	fact*=i;
}

等价于

int i=1;
while (i<=n)
{	fact*=i;
	i++;
}

• 如果有固定次数,⽤for
• 如果必须执行⼀次,⽤do_while
• 其他情况⽤while

5.2循环控制

break和continue辨析
break是跳出循环体
而continue 是跳过这轮循环剩下的语句进入下一轮循环。break和continue只对所在的那层的循环有效。

素数是只能被1和其自身整除的数,不包含1

goto最好只用于多重循环的跳出,其他情况不要用,因为其很容易破坏代码结构。
样例

#include <stdio.h>
#include<stdlib.h>
int main()
{
	int x;
	int one,two,five;
	scanf("%d",&x);
	for (one=1; one<x*10; one++)
	{
		for(two=1; two<x*10/2; two++)
		{
			for(five=1; five<x*10/5; five++)
			{
				if(one+two*2+five*5==x*10)
				{
					printf("可以用%d个一角,%d个2角,%d个5角构成%d元\n",one,two,five,x);
					goto out;//找到一个符合条件即跳出
				}
			}
		}
	}
out:
	system("pause");
	return 0;
}

5.3循环的应用

1、求前n项和(1+ 1 2 \frac 12 21+ 1 3 \frac 13 31+…+ 1 n \frac 1n n1

代码示例

#include <stdio.h>
#include<stdlib.h>
int main()
{
	int n;
	scanf("%d",&n);
	double sum=0;
	double sign=1.0;
	int i;
	for  (  i=1; i<=n; i++)
	{
		sum=sum+sign/i;
		sign=-sign;//控制奇偶变号
	}
	printf("f(%d)=%f",n,sum);
	system("pause");
	return 0;
}

2、将一个非负整数分解逐个输出
代码示例

#include <stdio.h>
#include<stdlib.h>
#include <math.h>
int main()
{
	int x,n=0;
	scanf("%d",&x);
	int t=x;
	do
	{
		t/=10;
		n++;
	}
	while(t>0);  //这个循环用来求这个数的位数
	printf("位数是%d位\n",n);
	int num,x1;
	for (; n>0; n--)
	{
		num=pow(10,n-1);
		x1=x/num;//得到首位数
		x%=num;//x变成去掉首位后剩余的数
		printf("%d\n",x1);//输出首位数
	}
	system("pause");
	return 0;
}

3、求最大公约数(用辗转相除法)

#include <stdio.h>
#include<stdlib.h>
int main()
{
	int a,b;
	int t;
	scanf("%d %d",&a,&b);
	int origa =a;
	int origb =b;
	while(b!=0)
	{
		t=a%b;
		a=b;
		b=t;
	}
	printf("%d和%d的最大公约数是%d",origa,origb,a);
	system("pause");
	return 0;
}

6.数据类型

6.0几道编程练习题解析

1、求符合给定条件的整数集
给定不超过6的正整数A,考虑从A开始连续4个数字,输出所有由他们组成的所有无重复数字的3位数
输入格式
输入在一行中给出A
输出格式
输出满足条件的三位数,要求从小到大,每行 6个,整数间以空格隔开,行末不能有多余空格。
代码样例

#include <stdio.h>
#include<stdlib.h>
int main()
{
	int a,b,c,d;
	int i=0;
	scanf("%d",&a);
	b=a;
	while(b<=a+3)
	{
		c=a;
		while(c<=a+3)
		{
			d=a;			
			while(d<=a+3)
			{
				if (b!=c &&c!=d && d!=b)
				{
					i++;
					printf("%d",b*100+c*10+d);
					if (i%6==0)
					{
						printf("\n");
					}				
					else 
					{
						printf("  ");
					}
				}
				d++;
			}
			c++;
		}	
		b++;
	}
	system("pause");
	return 0;
} 

2、求水仙花数,水仙花数指一个N位正整数,它的每个位上的数字的N次幂之和等于它本身。
输入格式:
输入在一行中给出正整数N(3=<N<=7)

输出格式:
按递增顺序输出所有水仙花数,每个数字占一行。
代码样例

#include <stdio.h>
#include<stdlib.h>
int power(int m, int n)
{
	int i = 1;
	int result = 1;
	if (n == 0)
	{
		return result;
	}
	for (i = 1; i <= n; i++)
	{
		result *= m;
	}
	return result;
}
//计算乘方的函数

int panduan(int a,int n)
{
	int a1=a;
	int n1=n;
	int b;
	int sum=0;
	do
	{
	b=a1%10;
	a1/=10;
	sum+=power(b,n1);
	n=n-1;	
	}while(n!=0);
	if (sum==a)
	{return 1;
	}
	else 
	{return 0;
	}

}
//用来判断是不是水仙花数,是的话输出1
int main()
{

	int n;
	int first;
	scanf("%d",&n);
	first=power(10,n-1);
	while(first<first*10)
	{
		if (panduan(first,n)==1)
		printf("%d\n",first);
		first++;
	}//n位数从小到大遍历,是水仙花数就输出

	system("pause");
	return 0;
} 

3、输出九九乘法表
对于任意给定的正整数N(1<N<10),输出从11到NN的乘法口诀表
输入格式:输入在一行中给出正整数N
输出格式:输出下三角式部分口诀表,其中等号右边数字占四位,左对齐。

代码样例

#include <stdio.h>
#include<stdlib.h>
int main()
	{
	int n;
	int x,y;
	x=1;
	scanf ("%d",&n);
	while 
		(x<=n)
		{
		y=1;
		while (y<=x)
			{
			printf ("%d*%d=%d",x,y,x*y);
			if (x*y>=10)
				{ 
				printf("   ");
				} 
			else
				{
				printf("    ");
				}
			y=y+1;
			}
		printf("\n");
		x=x+1;
		}
	system("pause");
	return 0;
	}

4、统计区间内的素数并对它们求和
输入格式
输入在一行中给出正整数M和N
输出格式
在一行中顺序输出M和N区间内素数的个数以及它们的和,数字间以空格分隔
代码样例

#include <stdio.h>
#include<stdlib.h>
int panduan(int a)
	{
	int b;
	int i;
	int sushu=1;
	if (a<=2)
		{
		return 1;
		}//1,2都是素数,不用判断了
	for ( i=2;i<a;i++)
		{
		b=a%i;
		if (b==0)//被整除但不是自身或1,说明不是素数
			{
			sushu=0;
			break;
			}
		}	
	//如果sushu=1,说明是素数,返回1,否则不是素数,返回0
	if (sushu)
		{
		return 1;
		} 
	else
		{
		return 0;
		}
	}
int main()
	{
	int m,n;
	int k=0;
	int sum=0;
	scanf ("%d %d",&n,&m);
	for(;n<=m;n++)
		{
		if (panduan(n)==1)
			{
			k=k+1;
			sum+=n;
			printf("%d\n",n);//是素数的话可以输出看看
			}

		}
	printf("%d %d",k,sum);
	system("pause");
	return 0;
	}

补充一些问题
1.return用法,附带一个返回值,返回调用处,并结束函数。
2.本来想定义一个bool 函数,但发现C中没有bool函数,C++才有,作个提醒
3.遇到了个“不是所有的控件路径都有返回值”错误,查资料知道是因为分支语句可能没有return,带返回值的函数如果在函数尾没有return语句,则在程序运行到函数尾部时,自动将离函数尾部最近的return语句作为该函数的结尾return。详见这篇文章
不是所有的控件路径都有返回值

5.猜数游戏
输入格式:
输入第一行给出2个不超过100的正整数,分别是系统产生的随机数,以及猜测的最大次数N,随后每行给一个用户输入,直到出现负数为止。
输出格式:
在一行中输出每次猜测相应的结果,直到输出猜对的结果或“Game Over!”则结束。
代码样例

#include <stdio.h>
#include<stdlib.h>
int main()
	{
	int a,n,b;
	int i;
	scanf("%d %d",&a,&n);
	for (i=1;i<=n;i++)
		{
		scanf("%d",&b);
		if (b<0)
			{
			printf("Game Over\n");
			break;
			}
		else if (b==a)
			{
			if (i==1)
				{
				printf("Bing Go");
				}
			else if (i<=3)
				{
				printf("LUCK you!");
				} 
			else
				{
				printf("Good Guess!\n");
				}
			break;
			}
		else if (b>a)
			{
			printf("大了\n");
			}
		else 
			{
			printf("小了\n");
			}
		}
	if (i==n+1)
		{
		printf("Game Over");
		}//判断是超次退出,还是猜对退出
	system("pause");
	return 0;
	}

6.约分最简分式
输入格式:
12/34
输出格式同上
代码样例:

#include <stdio.h>
#include<stdlib.h>
int main()

	{
		int a,b;
		int c=1;
		int a1,b1;
		scanf("%d/%d",&a,&b);
		a1=a;
		b1=b;
		while (c!=0)
		{
		c=a%b;
		a=b;
		b=c;
		}
	printf("%d/%d",a1/a,b1/a);//辗转相除法,求出最大公约数,然后约分
	system("pause");
	return 0;
	}

7.输入一个整数,输出每个数字对应的拼音,当整数为负数时,先输出一个fu.
输入格式:1234
输出格式:fu yi er san si。每个数字的拼音之间用空格分开,行末没有最后的空格。
代码样例:

#include <stdio.h>
#include <stdlib.h>
int main()
	{
	int a;
	scanf("%d",&a);
	if (a<0)
		{
		printf("fu ");
		a=-a;
		} //先把负的变成正的
	int b=1;
	int c=a;
	while (c>9)
		{
		c/=10;
		b*=10;
		}//获得10的n-1次方
	do 
		{
		int d=a/b;//取整,获得首位数字
		switch(d){
		case 0:printf("ling");break;
		case 1:printf("yi");break;
		case 2:printf("er");break;
		case 3:printf("san");break;
		case 4:printf("si");break;
		case 5:printf("wu");break;	
		case 6:printf("liu");break;
		case 7:printf("qi");break;	
		case 8:printf("ba");break;
		case 9:printf("jiu");break;	
			}
		if (b>9)
			{
			printf(" ");
			}
		a%=b;//取余,获得去掉第一位后的剩下位数
		b/=10;//10的n-1次方递推/10
		} while (b>0);//一直到最后一位结束循环
		printf("\n");
		system("pause");
		return 0;
	}

8、输入两个整数a和n,求数列之和S=a+aa+aaa+…aaa…a(n个a).

代码样例

#include <stdio.h>
#include <stdlib.h>
int main()
	{
	int a,n;
	scanf("%d %d",&a,&n);
	int sum=0,t=0;
	for (int i=1;i<=n;i++)
		{
		t=10*t+a;
		sum=sum+t;
		}
	printf("%d\n",sum);
	system("pause");
	return 0;
	}

6.1数据类型

C语言的变量必须在使用前定义,并且确定类型。
C语言的类型
•整数
char、short、int、long、(long long)
•浮点数
float、double、(long double)
•逻辑
(bool)
•指针
•自定义类型
(括号里的是C99类型)
类型名称:int、long、double
• 输⼊入输出时的格式化:%d、%ld、%lf
• 所表达的数的范围:char < short < int < float <
double
• 内存中所占据的⼤大⼩小:1个字节到16个字节
• 内存中的表达形式:二进制数(补码)、编码

• Size of是一个运算符,给出某个类型或变量在内存中所占的字节数
• 是静态运算符,它的结果在编译时刻就决定了
• 不要在sizeof的括号⾥里做运算,这些运算不会做的

整数
• char:1字节(8比特)
• short:2字节
• int:取决于编译器(CPU),通常的意义是“1个字”
• long:取决于编译器(CPU),通常的意义是“1个字”
• long long:8字节

计算机内部一切数都是二进制
补码意义就是拿补码和原码可以加出一个溢出的0
• 因为0 - 1 —> -1,所以,-1 =
• (1)00000000 - 00000001 —> 11111111
• 11111111被当作纯⼆二进制看待时,是255,被当作补码看待时是
-1
• 同理,对于-a,其补码就是0-a,实际是2n - a,n是这种类型的位数

数的范围
• 对于一个字节(8位),可以表达的是:
• 00000000 - 11111111
• 其中
• 00000000 —> 0
• 11111111 ~ 10000000 —> -1 ~ -128
• 00000001 ~ 01111111 —> 1 ~ 127

• char:1字节:-128 ~ 127
• short:2字节:-32768 ~ 32767
• int:取决于编译器(CPU),通常的意义是“1个字”
• long:4字节
• long long:8字节

unsigned
• 在整形类型前加上unsigned使得它们成为无符号的整数
• 内部的二进制表达没变,变的是如何看待它们
• 如何输出
• 11111111
• 对于char,是-1
• 对于unsigned char,是255
• 如果一个字面量常数想要表达自己是unsigned,可以在后面加u或U
• 255U
• 用l或L表示long(long)
• *unsigned的初衷并非扩展数能表达的范围,而是为了做纯二进制运算,主要是为了移位

整数越界
• 整数是以纯二进制方式进⾏行计算的,所以:
• 11111111 + 1 —> 100000000 —> 0
• 01111111 + 1 —> 10000000 —> -128
• 10000000 - 1 —> 01111111 —> 127

整数的输入输出
• 只有两种形式:int 或long long
• %d:int (字节数小于int 的都有%d)
• %u:unsigned
• %ld:long long
• %lu:unsigned long long

8进制和16进制
• 一个以0开始的数字字面量是8进制
• 一个以0x开始的数字字面量是16进制
• %o⽤用于8进制,%x用于16进制

浮点数

类型字长范围有效数字
float32±(1.20x10^-38 ~3.40*10^38),0,±inf,nan7
double64±(2.2*10^-308~ 1.79 *10^ 308),0,±inf,nan15
(有效数字表示只有几位有效的,后面的数字是不准确的 )
输入输出格式
类型scanfprintf
float%f%f,%e
double%lf%f,%e

在这里插入图片描述

输出精度
• 在%和f之间加上.n可以指定输出小数点后几位,这样
的输出是做4舍5入的
• printf输出inf表示超过范围的浮点数:±∞
• printf输出nan表示不存在的浮点数
• 带小数点的字面量是double而非float
• float需要用f或F后缀来表明身份

浮点数直接判断 f1 == f2 可能失败
一般用差的绝对值 fabs(f1-f2) < 1e-12,因为有效位数后面的数字是不准确的

字符
char是一种整数,也是一种特殊的类型:字符。这是因为:
• 用单引号表示的字符字面量:‘a’, ‘1’
• ''也是一个字符
•也可以用ASCII编码表示,ASCII编码在形式上是整数
• printf和scanf里用%c来输入输出字符

字符计算
• 一个字符加⼀一个数字得到ASCII码表中那个数之后的字符
• 两个字符的减,得到它们在表中的距离

大小写转换
• 字母在ASCII表中是顺序排列的
• 大写字母和小写字母是分开排列的,并不在一起
• ‘a’-‘A’可以得到两段之间的距离,于是
• a+’a’-‘A”可以把一个大写字母变成小写字母,而
• a+’A’-‘a’可以把一个小写字母变成大写字母

转义字符
反斜杠“ \”加上后面的一个字符组成一个特殊字符
在这里插入图片描述
制表位
• 每行的固定位置
• 一个\t使得输出从下一个制表位开始
• 用\t才能使得上下两行对齐

逻辑类型
• #include <stdbool.h>
• 之后就可以使⽤用bool和true、false
• bool实际上还是以int的手段实现的,所以可以当作int来计算
• 也只能当作int来输入输出

自动类型转换
• 当运算符的两边出现不一致的类型时,会自动转换成较大类型
• 大的意思是能表达的数的范围更大
• char —> short —> int —> long —> long long
• int —> float —> double
• 对于printf,任何小于int的类型会被转换成int;float会被转成double
• 但是scanf不会,scanf 要明确知道变量的大小,要输short,需要%hd

在这里插入图片描述
• 强制类型转换的优先级高于四则运算

6.2其他运算(逻辑,条件,逗号)

短路

• 逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算
• a== 6 && b== 1
• a==6 && b+=1
• 对于&&,左边是false时就不做右边了
• 对于||,左边是true时就不做右边了
不要把赋值,包括复合赋值组合进表达式!

• 条件运算符的优先级高于赋值运算符,但是低于其他运算符

逗号运算符
• 逗号用来连接两个表达式,并以其右边的表达式的值作为它的结果。逗号的优先级是所有的运算符中最低的,所以它两边的表达式会先计算;逗号的组合关系是自左向右,所以左边的表达式会先计算,而右边的表达式的值就留下来作为逗号运算的结果。
主要在for中使用,没啥大用
•for ( i=0, j=10; i<j; i++, j- - ) ……

7.函数

7.1函数的定义和使用

1、什么是函数
• 函数是一块代码,接收零个或多个参数,
做一件事情,并返回零个或一个值
• 可以先想像成数学中的函数:
• y = f(x)
在这里插入图片描述
2、函数的使用在这里插入图片描述
在这里插入图片描述
• return停止函数的执行,并送回一个值
• return有两种用法
直接 return;
return+ 表达式;
• 一个函数里可以出现多个return语句
•函数的返回值可以赋值给变量,可以再传递给函数,甚至可以丢弃,有时候要的是副作用。
在这里插入图片描述

7.2函数的参量和变量

1、函数原型

如果把调用的函数放到下面了,可能会报错

2、参数传递

• 调用函数时给的值与参数的类型不匹配是C语言传统上最大的漏洞
• 编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的
• 后续的语言,C++/Java在这方面很严格

★C语言在调用函数时,永远只能传值给函数

本地变量
• 函数的每次运行,就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量
• 定义在函数内部的变量就是本地变量
• 参数也是本地变量

变量的生存期和作用域
• 生存期:什么时候这个变量开始出现了,到什么时候它消亡了
• 作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)
• 对于本地变量,这两个问题的答案是统一的:大括号内——块
在这里插入图片描述
其他细节

没有参数时
• void f(void)还是• void f();
• 在传统C中,void f()表示f函数的参数表未知,并不表示没有参数;void f(void)明确表示没有任何参数。

逗号运算符
• 调用函数时的圆括号里的逗号是标点符号,不是运算符
• f(a,b)
• f((a,b))再加一个括号就表示逗号运算符了。

• C语言不允许函数嵌套定义,但是可以在一个函数里面放另一个函数的声明。
• int main ()也是一个函数,可以写成 int main(void),表示没有任何参数。
• return 0是有意义的。

8.数组

8.1数组的定义和使用

定义数组
• <类型> 变量名称[元素数量];
• int grades[100];
• double weight[20];
• 元素数量必须是整数
• C99之前:元素数量必须是编译时刻确定的字面量

数组
• 是一种容器(放东西的东西),特点是:
• 其中所有的元素具有相同的数据类型;
• 一旦创建,不能改变大小
• *(数组中的元素在内存中是连续依次排列的)
在这里插入图片描述
数组的单元
• 数组的每个单元就是数组类型的一个变量
• 使用数组时放在[]中的数字叫做下标或索引,下标从0开始计数:
• grades[0]
• grades[99]
• average[5]

有效的下标范围
• 编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写
• 一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃
• segmentation fault!
• 但是也可能运气好,没造成严重的后果
• 所以这是程序员的责任来保证程序只使用有效的下标值:[0,数组的大小-1]

长度为0的数组?
• int a[0];
• 可以存在,但是无用。

数组的例子:投票统计
• 写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束。
在这里插入图片描述

8.2数组运算

8.2-1 数组的运算

数组的大小
• sizeof给出整个数组所占据的内容的大小,单位是字节
• sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数

• 通常都是使用for循环,让循环变量i从0到<数组的长度,这样循环体内最大的 i 正好是数组最大的有效下标
常见错误是:
• 循环结束条件是<=数组长度,或;
• 离开循环后,继续用 i 的值来做数组元素的下标!

数组作为函数参数时,往往必须再用另一个参数来传入数组的大小
数组作为函数的参数时:
• 不能在[ ]中给出数组的大小
• 不能再利用sizeof来计算数组的元素个数!

8.2-2数组的例子:素数
例1.判断是否能被已知的小于x的素数整除
代码示例

#include <stdio.h>
#include <stdlib.h>

int isprime(int x, int knownprimes[],int numberofknownpimes);
int main(void)
{
	const int number=10;
	int prime[number]={2};
	int count =1;
	int i=3;
	while (count<number)
	{

		if (isprime(i,prime,count))
		{
			prime[count++]=i;
		}

		{
			printf("i=%d \tcnt=%d\t",i,count);
			int a;
			for (a=0;a<number;a++)
			{
				printf("%d\t",prime[a]);
			}
			printf("\n");
		}
		i++;
	}
	for (i=0;i<number;i++)
	{
		printf("%d",prime[i]);
		if ((i+1)%5)
		{
			printf("\t");
		}
		else printf("\n");
	}

	system("pause");
	return 0;
}

int isprime(int x, int knownprimes[],int numberofknownpimes)
{
	int ret=1;
	int i;
	for (i=0;i<numberofknownpimes;i++)
	{
		if (x%knownprimes[i]==0)
		{
			ret=0;
			break;
		}
	}
	return ret;
}

运行结果

例2,构造素数表
代码示例

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	const int maxnumber=25;
	int isprime[maxnumber];
	int i;
	int x;
	for (i=0;i<maxnumber;i++)
	{
		isprime[i]=1;
	}
	printf("\t");
	for (i=2;i<maxnumber;i++)
	{
		printf("%d\t",i);
	}
	printf("\n");
	for (x=2;x<maxnumber;x++)
	{
		if (isprime[x])
		{
			for (i=2;i*x<maxnumber;i++)
			{
				isprime[i*x]=0;
			}
		}
		printf("%d\t",x);
		for (i=2;i<maxnumber;i++)
		{
			printf("%d\t",isprime[i]);
		}
		printf("\n");

	}

	for (i=2;i<maxnumber;i++)
	{
		if (isprime[i])
		{
			printf("%d\t",i);
		}
	}
	printf("\n");
	system("pause");
	return 0;
}

8.2-3 二维数组

• int a[3][5];
• 通常理解为a是一个3行5列的矩阵


• a[i][j]是一个int
• 表示第i行第j列上的单元

代码示例
代码有些问题

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	const int size=3;
	int board[size][size];
	int i,j;
	int numofX;
	int numofo;
	int result=-1;
	//读入矩阵
	for (i=0;i<size;i++)
	{
		for (j=0;j<size;j++)
		{
			scanf("%d",&board[i][j]);
		}
	}
	//检查行
	for (i=0;i<size && result==-1;i++)
	{
		numofo=numofX=0;
		for (j=0;j<size;j++)
		{
			if (board[i][j]==1)
			{
				numofX++;
			} 
			else
			{
				numofo++;
			}
		}

		if (numofo==size)
		{
			result=0;
			printf("O赢了\n");
			break;
		}
		else if(numofX==size)
		{
			result=1;
			printf("X赢了\n");
			break;
		}
	}
	//检查列
	if (result==-1)
	{
		for (j=0;j<size&&result==-1;j++)
		{
			numofo=numofX=0;
			for (i=0;i<size;i++)
			{
				if (board[i][j]==1)
				{
					numofX++;
				} 
				else
				{
					numofo++;
				}
			}

			if (numofo==size)
			{
				result=0;
				printf("O赢了\n");
				break;
			}
			else if(numofX==size)
			{
				result=1;
				printf("X赢了\n");
				break;
			}
		}
	}
	//检查对角线
	numofo=numofX =0;
	for (i=0;i<size;i++)
	{
		if (board[i][i]==1)
		{
			numofX++;
		} 
		else
		{
			numofo++;
		}
	}
	if (numofo==size)
	{
		result=0;
		printf("O赢了\n");
		return 0;
	} 
	else if (numofX==size)
	{
		result=1;
		printf("X赢了\n");
		return 0;
	}
	numofo=numofX =0;
	for (i=0;i<size;i++)
	{
		if (board[i][size-i-1]==1)
		{
			numofX++;
		} 
		else
		{
			numofo++;
		}
	}
	if (numofo==size)
	{
		result=0;
		printf("O赢了\n");
		return 0;
	} 
	else if (numofX==size)
	{
		result=1;
		printf("X赢了\n");
		return 0;
	}

	if (result==-1)
	{
		printf("没人赢");
	}

	system("pause");

	return 0;
}

9.指针

9.1指针的定义和使用

1、取地址运算符&
• scanf(“%d”, &i);里的&
• 获得变量的地址,它的操作数必须是变量
• int i; printf(“%x”,&i);
• 地址的大小是否与int相同取决于编译器
• int i; printf(“%p”,&i);
•&后面跟的一定是变量,不能对没有地址的东西取地址
如 &(a+b)、 &(a++)、&(++a)
2、指针
指针就是保存地址的变量。

int i;
int* p = &i;
int* p,q;
int *p,q;

无论 *号是靠近 int 还是靠近 p都只表示p是一个指针,q不是。若想让q也是一个指针,那么在q前面也加一个 * 号
• 指针变量的值是内存的地址
• 普通变量的值是实际的值
• 指针变量的值是具有实际值的变量的地址

访问那个地址上的变量 *
• *是一个单目运算符,用来访问指针的值所表示的地址上的变量
• 可以做右值也可以做左值
• int k = *p;
• *p = k+1;

指针最常见的错误
• 定义了指针变量,还没有指向任何变量,就开始使用指针,未初始化之前使用 *p 是没有任何意义的。

3、指针与数组

数组参数
• 以下四种函数原型是等价的:

int sum(int *ar, int n);int sum(int *, int);int sum(int ar[], int n);int sum(int [], int);

4、指针与const (C99 Only!)

指针是const
• 表示一旦得到了某个变量的地址,不能再指向其他变量

 int * const q = &i; //q 是 const
 *q = 26; // OK
 q++; // ERROR

所指是const
• 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)

 const int *p = &i;
 *p = 26; // ERROR! (*p) 是 const
 i = 26; //OK
 p = &j; //OK

判断哪个被const了的标志是const在*的前面还是后面,const在 * 号前面是所指不能修改,在 * 后面是指针不能修改

const数组
• const int a[] = {1,2,3,4,5,6,};
• 数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
• 所以必须通过初始化进行赋值
• 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
• 为了保护数组不被函数破坏,可以设置参数为const
• int sum(const int a[], int length);

9.2指针的运算

1、指针运算
• 给一个指针加1表示要让指针指向下一个变量

int a[10];
int *p = a;
*(p+1)> a[1]

• 如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义

• 这些算术运算可以对指针做:
• 给指针加、减⼀一个整数(+, +=, -, -=)
• 递增递减(++/—)
• 两个指针相减

*p++
• 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
• *的优先级虽然高,但是没有++高
• 常用于数组类的连续空间操作
• 在某些CPU上,这可以直接被翻译成一条汇编指令

指针比较
• <, <=, ==, >, >=, != 都可以对指针做
• 比较它们在内存中的地址
• 数组中的单元的地址肯定是线性递增的

0地址
• 当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
• 所以你的指针不应该具有0值
• 因此可以用0地址来表示特殊的事情:
• 返回的指针是无效的
• 指针没有被真正初始化(先初始化为0)
• NULL是一个预定定义的符号,表示0地址
• 有的编译器不愿意你用0来表示0地址

指针的类型转换
• void* 表示不知道指向什么东西的指针
• 计算时与char*相同(但不相通)
• 指针也可以转换类型
• int * p = &i; void * q = (void * )p;
• 这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
• 我不再当你是int啦,我认为你就是个void!

2、动态内存分配

malloc
#include <stdlib.h>
void* malloc(size_t size);
• 向malloc申请的空间的大小是以字节为单位的
• 返回的结果是void*,需要类型转换为自己需要的类型
• (int*)malloc(n*sizeof(int))

• 如果申请失败则返回0,或者叫做NULL
• 你的系统能给你多大的空间?

free()
• 把申请得来的空间还给“系统”
• 申请过的空间,最终都应该要还
• 出来混的,迟早都是要还的
• 只能还申请来的空间的首地址
• free(0)?

常见问题
• 申请了没free—>长时间运行内存逐渐下降
• 新手:忘了
• 老手:找不到合适的free的时机
• free过了再free
• 地址变过了,直接去free

10.字符串

10.1字符串定义

在这里插入图片描述
字符串
• 以0(整数0)结尾的一串字符
• 0或’\0’是一样的,但是和’0’不同
• 0标志字符串的结束,但它不是字符串的一部分
• 计算字符串长度的时候不包含这个0
• 字符串以数组的形式存在,以数组或指针的形式访问
• 更多的是以指针的形式
• string.h 里有很多处理字符串的函数

字符串变量

char *str = “Hello”;
char word[] = “Hello”;
char line[10] = “Hello”;

字符串常量
• “Hello”
• ″Hello″ 会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0
• 两个相邻的字符串常量会被自动连接起来
• 行末的\表示下一行还是这个字符串常量

• C语言的字符串是以字符数组的形态存在的
• 不能用运算符对字符串做运算
• 通过数组的方式可以遍历字符串
• 唯一特殊的地方是字符串字面量可以用来初始化字符数组
• 以及标准库提供了一系列字符串函数

char* s = “Hello, world!”;
• s 是一个指针,初始化为指向一个字符串常量
• 由于这个常量所在的地方,所以实际上s是 const
char* s ,但是由于历史的原因,编译器接受不带const的写法
• 但是试图对s所指的字符串做写入会导致严重的后果
• 如果需要修改字符串,应该用数组:
char s[] = “Hello, world!”;

char * 是字符串?
• 字符串可以表达为char * 的形式
• char * 不一定是字符串
• 本意是指向字符的指针,可能指向的是字符的数组(就像int *一样)
• 只有它所指的字符数组有结尾的0,才能说它所指的是字符串

字符串赋值

char *t = “title”;
char *s;
s = t;

• 并没有产生新的字符串,只是让指针s指向了t所指的字符串,对s的任何操作就是对t做的

字符串输入输出

char string[8];
scanf(%s”, string);
printf(%s”, string);

• scanf读入一个单词(到空格、tab或回车为止)
• scanf是不安全的,因为不知道要读入的内容的长度

安全的输入

char string[8];
scanf(%7s”, string);

• 在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小1

常见错误

 char * string;
 scanf(%s”, string);

• 以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用了,也就是说使用前要初始化。
• 由于没有对string初始化为0,所以不一定每次运行都出错

空字符串
• char buffer[100]=””;
• 这是一个空的字符串,buffer[0] == ‘\0’
• char buffer[] = “”;
• 这个数组的长度只有1!

在这里插入图片描述

10.2字符串函数

putchar 字符输出
• int putchar(int c);
• 向标准输出写一个字符
• 返回写了几个字符,EOF(-1)表示写失败

getchar 字符输入
• int getchar(void);
• 从标准输入读入一个字符
• 返回类型是int是为了返回EOF(-1)

标准库中的字符串函数
string.h
• strlen
• strcmp
• strcpy
• strcat
• strchr
• strstr

strlen

 size_t strlen(const char *s);

• 返回s的字符串长度(不包括结尾的0)

strcmp

int strcmp(const char *str1, const char *str2);

strcmp() 会根据 ASCII 编码依次比较 str1 和 str2 的每一个字符,直到出现不同的字符,或者到达字符串末尾(遇见\0)。
• 比较两个字符串,返回:
• 0:s1==s2
• >0:s1>s2
• <0:s1<s2

strcpy

 char * strcpy(char *restrict dst, const char *restrict
src);

• 把src的字符串拷贝到dst
• restrict表明src和dst不重叠(C99)
复制一个字符串

char *dst = (char*)malloc(strlen(src)+1);
strcpy(dst, src);

strcat

 char * strcat(char *restrict s1, const char *restrict
s2);

• 把s2拷贝到s1的后面,接成一个长的字符串
• 返回s1
• s1必须具有足够的空间

strcpy和strcat都可能出现安全问题
• 如果目的地没有足够的空间?
安全版本

char * strncpy(char *restrict dst, const char *restrict
src, size_t n);
char * strncat(char *restrict s1, const char *restrict
s2, size_t n);
int strncmp(const char *s1, const char *s2, size_t n);

字符串中找字符

char * strchr(const char *s, int c);
char * strrchr(const char *s, int c);

• 返回NULL表示没有找到

字符串中找字符串

 char * strstr(const char *s1, const char *s2);
 char * strcasestr(const char *s1, const char *s2);

11.结构类型

11.1枚举

• 枚举量可以作为值
• 枚举类型可以跟上enum作为类型
• 但是实际上是以整数来做内部计算和外部输入输出的

#include <stdio.h> 
enum COLOR { RED, YELLOW, GREEN, NumCOLORS };
int main(int argc, char const *argv[])
{
	int color = -1;
	char *ColorNames[NumCOLORS] = {
		"red", "yellow","green",
	};
	char *colorName = NULL;
	printf("输入你喜欢的颜色的代码: ");
	scanf("%d", &color);
	if (color >= 0 && color < NumCOLORS) {
		colorName = ColorNames[color];
	}
	else {
		colorName = "unknown";
	}
	printf("你喜欢的颜色是%s\n",colorName);
	return 0;
}

枚举量
• 声明枚举量的时候可以指定值
• enum COLOR { RED=1, YELLOW, GREEN = 5};

#include <stdio.h>
enum COLOR { RED = 1, YELLOW, GREEN = 5, NumCOLORS };
int main(int argc, char const *argv[]){
printf("code for GREEN is %d\n", NumCOLORS);
return 0;
}

• 虽然枚举类型可以当作类型使用,但是实际上很(bu)少(hao)用
• 如果有意义上排比的名字,用枚举比const int⽅便
• 枚举⽐比宏(macro)好,因为枚举有int类型

11.2结构

●和本地变量一样,在函数内部声明的结构类型只能在函数内部使用
●所以通常在函数外部声明结构类型,这样就可以被多个函数所使用了

对于第一和第三种形式,都声明了结构point。但是第二种形式没有声明point,只是定义了两个变量

结构的初始化

#include <stdio.h>
struct date {
	int month;
	int day;
	int year;
};
int main(int argc,char const *argv[])
{struct date today = { 07 ,31,2014 };
struct date thismonth = { .month = 7,.year = 2014 };
printf("Today's date is %i-%i-%i. \n",
	today.year, today.month, today.day);
printf("This month is %i- %i- %i. \n",
	thismonth.year, thismonth.month, thismonth.day);
return 0;
}

结构成员
●结构和数组有点像
●数组用 [ ] 运算符和下标访问其成员
●a[0]= I0;
●结构用 . 运算符和名字访问其成员

结构运算
●要访问整个结构,直接用结构变量的名字
●对于整个结构,可以做赋值、取地址,也可以传递给函数参数
●pl = (struct point){5, I0}; //相当于pl.x= 5;pl.y= 10;
●pI =p2; // 相当于pl.x=p2.x;pl.y= p2.y;
数组无法做这两种运算

复合字面量
●today = (struct date) {9,25,2004};
●today = (struct date) {.month=9, .day=25,.year=2004};

结构指针
●和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
●struct date *pDate = &today ;

结构作为函数参数

int number0fDays (struct date d)

●整个结构可以作为参数的值传入函数
●这时候是在函数内新建一个结构变量,并复制调用者的结构的值
●也可以返回一个结构
●这与数组完全不同

结构指针作为函数参数

//指向结构的指针
	struct date {
		int month;
		int day;
		int year;
	} myday;
	struct date *p = &myday;
	(*p).month = 12;
	p->month = 12;

用->表示指针所指的结构变量中的成员

11.3联合

	typedef int Length; //Length就等价于int类型
	typedef *char[10] Strings; // Strings是10个字符串的数组的类型
	typedef struct node 
	{
		int data;
		struct node *next;
	} aNode;
	//或
	typedef struct node aNode; //这样用aNode就可以代替
	struct node;

联合
联合和结构非常相似,
●存储
●所有的成员共享一个存储空间
●同一时间只有一个成员是有效的
●union的大小是 其最大的成员

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值