C语言知识点基础总结

这里写自定义目录标题

  • Linux操作指令
  • 2.数据类型运算符
  • 3.流程控制语句
  • 4.数组
  • 5.指针
  • 6.函数
  • 7.1共用体
    • 7.2枚举
  • 8链表

Linux操作指令

Ctrl+Shift+T
注意:前提是必需已经打开了一个终端
指令:
ls 显示当前文件夹下的内容
ls -l 显示文件的详细信息
ls -a 显示当前路径下所有内容(包括隐藏文件)
ls 路径 显示指定路径下的内容
特殊路径:
. 指代当前路径
… 指代当前路径的上一级路径
~ 指代当前用户的家目录
/ Linux下起始路径
创建路径:
mkdir 创建目录(文件夹)
mkdir 文件夹名 是在当前路径下创建一个文件夹
mkdir 路径/文件夹名 是在当前路径下创建一个文件夹
rmdir 文件夹名 删除空文件
创建普通文件
gedit 如果文件不存在,就创建并且打开 如果文件存在,就打开
gedit 文件名.c (ctrl+s(保存),ctrl+Q(退出))
一般来说我们创建的是.c
注意:对于新建的文件,要保存
保存:Ctrl+S
退出:Ctrl+Q
touch
创建一个文件并保存,并且不打开
touch 文件名
如果touch创建一个已经存在的文件,就不做任何操作
cat 文件名 在终端显示文件中的内容
清屏 clear Ctrl+L
复制:cp 1.c 2.c (复制文件夹)
剪切:mv
删除: rm 文件名 (删除文件) rm 文件名 -r(删除文件夹)
统配符
? 替换其中一个字符
* 替换一整个字符串(所有)
压缩 tar
压缩:tar -cvf 压缩包名称.tar 被压缩的内容
解压:tar -xvf 压缩包名称.tar
用户相关指令
用户切换 su
更改密码 passwd
权限不够 sudo
sudo不能单独使用,没有意义,要和其它指令结合使用
gcc编译器的作用:将程序编译成为机器可以识别的二进制文件gcc 被编译的文件
gcc 被编译的文件 -o 自定义名字
安装gcc的指令
sudo apt-get install gcc
可执行文件的执行
gcc编译之后默认生成a.out可执行文件
./a.out 运行可执行文件

2.数据类型运算符

标准输出
printf(“”);
\和%转义字符
\n、\t、\、\”、%d、%c、%f、%%、%lf、%ld
标准输入
scanf(“格式控制符”,&变量名);
注意:scanf双引号之间只写格式控制符
数据类型:字符型、整型、浮点型
格式控制符:
char %c
int %d
float %f
double %lf
数据类型的大小
char 1字节
int 4字节
float 4字节
double 8字节
变量名的命名规则:
1、变量名以数字、字母、下划线组成
2、变量名区分大小写
3、变量名不能以数字开头
4、变量名不能和关键字重复
运算符:
赋值运算符 = 逗号运算符 ,
算术运算符 +、-、、/、%
/ 求商舍余 % 求余舍商
注意:表达式的计算结果向精度更高的数据类型偏移
浮点型不能求余
自增和自减 ++、–
(++i) ++在前,数据先自增1,然后参与运算
(i++) ++在后,数据先参与运算,然后再自增1
–同理
关系运算符 >、<、>=、<=、==、!=(不等于)
注意:关系运算符的结果只有真(1)和假(0)
关系等是双等号 ==
逻辑运算符 &&与、||或、!非
注意:常数中除了0,都为真
&& 遇假则假,全真为真
&&优化:如果表达式1为假,整个逻辑表达式判定为假,表达式2不再执行
|| 遇真则真,全假为假
||优化:如果表达式1为真,整个逻辑表达式判定为真,表达式2不再执行
! 真变假,假变真
位运算符
把数据换成二进制来计算
& 按位与 遇0则0,全1为1
| 按位或 遇1则1,全0为0
^ 按位异或 相同为0,不同为1
<< 按位左移 数据扩大2^N倍,N是左移的位数
>> 按位右移 数据缩小2^N倍,留的是商
~ 按位取反 0变1,1变0
复合运算符:
+=、-=、
=、/=、%=
a+=1 a=a+1
a-=1 a=a-1
a*=1 a=a*1
a/=1 a=a/1
a%=1 a=a%1

3.流程控制语句

流程控制语句
单分支
if(条件)
{
语句;
}
双分支
if(条件)//如果成立,执行if里的语句
{
语句;
}
else//否则执行else里的语句
{
语句;
}
多分支
if(条件1)
{
语句1;
}
else if(条件2)
{
语句2;
}
else if(条件3)
{
语句3;
}

else{
语句n;
}

		switch(表达式)
		{
			case 常量1: 语句1;break;
			case 常量2: 语句2;break;
			case 常量3: 语句3;break;
			case 常量4: 语句4;break;
			case 常量5: 语句5;break;
			default:语句6;
		}

	循环语句
		for(起始条件;条件判断;条件改变)
		{
			循环体;
		}
		循环嵌套
			循环里套循环
			for()
			{
				for()
				{
					for()
					{
						
					}
				}
			}
		while(条件)
		{
			循环体;
			条件改变;
		}
		do
		{
			循环体;
			条件改变;
		}while(条件);
		while和do..while区别就似乎while在执行的时候会先进行
		判断,然后在确定是否执行,而do..while他会先执行一次
		然后在进行条件的判断。
		死循环
		while(1)
		{
			
		}
		for(;;)
		{
			
		}
		for(;1;)
		{
			
		}
		流程控制
			break; 主要就是用于循环语句 for switch;
			他是直接跳槽整个循环体;
			for()
			{
				for()
				{
					break;这里的break他结束的是第二个for
				}
			}
			continue;
			含义:跳过本次循环,进入下次循环。
			return;
			直接结束你正在调用的这个函数。
			他结束的时候也可带回一个数值
			goto 它也可以实现循环
			格式:
				标识符:
					goto 标识符:

4.数组

4.1 一维数组
1.一维数组
所谓的数组就是存储相同数据类型集合。
int num[10];
并且数组的地址是连续的,数组名就是数组的
首地址,且他和数组的第一个元素的地址相同。
打印地址的符号是%p.默认是以十六进制打印的。
2.数组的定义
格式:类型 数组名[元素个数];
a)定义的时候不初始化
int num[5];//系统会随机分配随机数。
b)定义的时候只初始化一部分
int num[5]={1,2,3};//不初始化的部分系统会
默认的赋值为0
c)定义的时候整体初始化
int num[5]={1,2,3,4,5};
定义数组的时候数组的大小可以省略,
系统会自动的去计算。
也可以手动的去计算数组的大小
数组的大小=元素的个数*元素的大小
int num[]={1,2,3,4,5};
num大小=sizeof(num)/sizeof(num[0]);

int num[10];
num[10]={1,2,12}//不允许这样赋值
一般定义数组都是这样的
int num[5]={0};
d)数组的取值
数组名+下表;
数组的取值范围 0-n-1;//n代表
数组的大小。
使用数组的时候一定要注意数组越界。
冒泡排序
一组数据进行数据比较每一个数字都和他后边的数字相比,最小往后走,每比较一轮都会出现一个最小值。
1 5 9 3 8 7 一共是6个数字 第一轮比较了 5次
5 1 9 3 8 7
5 9 1 3 8 7
5 9 3 1 8 7
5 9 3 8 1 7
5 9 3 8 7 1 第二轮比较了 4次
9 5 3 8 7 1
9 5 3 8 7 1
9 5 8 3 7 1
9 5 8 7 3 1
9 5 8 7 3 1
9 8 5 7 3 1
9 8 7 5 3 1
9 8 7 5 3 1
9 8 7 5 3 1
#include
int main()
{
int num[10]={0};
int i,j,tep=0;
for(i=0;i<10;i++)
{
scanf(“%d”,&num[i]);
}
for(i=1;i<=9;i++)//轮数
{
for(j=0;j<10-i;j++)//两两比较的次数
{
if(num[j]>num[j+1])
{
tep=num[j];
num[j]=num[j+1];
num[j+1]=tep;
}
}
}
for(i=0;i<10;i++)
{
printf(“%-3d”,num[i]);
}
printf(“\n”);
return 0;
}
4.2字符数组
1.字符数组类型就是char
定于:类型 数组名[大小]=初始化;
char buf[12]={“hello world”};
char buf[12]={‘h’,’e’,’l’,’l’,’o’,’ ’,’w’,’o’,’r’,’l’,’d’,’\0’};
2.定义数组
1.定义数组并且初始化
char buf[12]={0};
char buf[12]={‘\0’};//系统会把\0自动转换成0
int i;
#include
int main()
{
//char buf[12]={0};
char buf[12]={‘\0’};
int i;
for(i=0;i<12;i++)
{
printf(“%d”,buf[i]);
}
return 0;
}
2.定义的时候初始化内容
char buf[12]={“hello”};//char buf[12]=“hello”;这样定义也是你可以的
char buf[12]={‘h’,‘e’,‘l’,‘l’,‘o’,‘\0’};
输入字符串的格式可以使用%s
3.定义字符数组的时候可以省略字符数组的大小,系统会自动计算。
char buf[12]={“hello”};
手动计算:
sizeof(buf);=12 计算的是你的数组的整体大小,和你的赋值的大小没有关系 strlen(buf);=5他计算的是你数组的实际存储数组的大小,
strlen他遇到\0就停止就算了
#include
int main()
{
char buf[12]={“adfasd”};
long len1=sizeof(buf);
int len2;
printf(“len1:%d\n”,len1);
len2=strlen(buf);
printf(“len2:%d\n”,len2);
return 0;
}
3.输入字符串
scanf();输入字符串的时候不能在字符串里空格,有空格就意味着
输入结束。
#include
int main()
{
char buf[12]={0};
scanf(“%s”,buf);
printf(“buf:%s\n”,buf);
return 0;
}
putchar();
函数功能:输出一个字符
函数原型:int putchar(int c);
函数参数:c就是你要输出字符
函数返回值:成功返回0失败返回-1
getchar();
函数功能:从键盘获取一个字符
函数原型:int getchar(void);
函数参数:无
函数返回值:成功返回得到的字符,失败-1
#include
int main()
{
char ch;
ch=getchar();
putchar(ch);
return 0;
}

gets();
	函数的功能:从键盘获取一个字符串,这个字符串可以
	包括空格
	函数原型:char *gets(char *s);
	函数的参数:s就是你要获取的字符串
	函数返回值:成功返回字符串的地址,失败返回NULL。
	注意:使用gets编译会报警告,不管他。
puts();
	函数功能:输出一个字符串
	函数原型:int puts(const char *s);
	函数参数:s就是你要输出的字符串
	函数返回值:成功返回字符串的大小
	注意:puts函数自带换行。

#include
int main()
{
char buf[12];
gets(buf);//从键盘上获取一个字符串给buf
int n = puts(buf);
printf(“n:%d\n”,n);
return 0;
}

#include
int main()
{
char buf[12]=“hello world”;
int i;
for(i=0;buf[i]!=‘\0’;i++)
{
printf(“%c”,buf[i]);
}
return 0;
}
字符串操作函数(这些函数只能对字符串进行操作)
1.字符串拼接
strcat()
函数原型:char *strcat(char *dest, const char *src);
函数头文件:#include
函数功能:将src的内容拼接到dest的后边,注意:dest的大小
一定要足够大
函数的参数:dest原来的字符串也就拼接之后要保存的字符串
src:要拼接的内容的字符串
函数返回值:成功返回指向dest的字符串,失败返回NULL
#include
#include
int main()
{
char buf1[12]=“hello”;
char buf2[5]=" ABC";
strcat(buf1,buf2);
printf(“buf1:%s\n”,buf1);
return 0;
}
2.字符串复制
strcpy()
函数功能:将src地址上的字符串复制到dest的地址空间上,并且把
src字符串的\0一起复制到dest里
函数原型:char *strcpy(char *dest, const char *src);
函数头文件:#include
函数的参数:dest:就是你要复制到的地址空间
src:就你要复制的字符串,包括\0
函数的返回值:成功指向dest的地址,失败NULL
#include
#include
int main()
{
char buf1[12]=“helloworld”;
char buf2[5]=“abcd”;
strcpy(buf1,buf2);
printf(“buf1:%s\n”,buf1);
return 0;
}
3.字符串比较
strcmp()
函数功能:比较s1字符串s2字符串的大小
函数原型:int strcmp(const char *s1, const char *s2);
函数头文件 :#include
函数参数:s1第一个比较的字符串
s2第二个比较的字符串
函数的返回值:有三种结果
1.正数–第一个字符串大于第二个字符串
2.负数–第一个字符串大于第二个字符串
3.0–第一个字符串等于第二个字符串
字符串比较的规则:是按照字符串第一个不相同的字符的ASCII
的数值来进行比较的。
比如:s1=“hello”;
s2=“helld”;
#include
#include
int main()
{
char s1[6]=“hello”;
char s2[6]=“world”;
//int n = strcmp(s1,s2);
printf(“%d\n”,strcmp(s1,s2));
return 0;
}

#include
#include
int main()
{
char s1[6]=“hello”;
char s2[6]=“hello”;
int n = strcmp(s1,s2);
if(n>0)
{
printf(“s1>s2\n”);
}
else if(n<0)
{
printf(“s1 }
else
{
printf(“s1=s2\n”);
}
return 0;
}
4.3二维数组
其实二维素数组的本质还有一个一维数组,只不过
这个一维数组里保存了多个数据。
二维数组的定义
类型 变量名[行号][列号];
二维数组的初始化
int num[3][4];
int num[3][4]={0};
int num[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int num[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int num[3][4]={{1,2},{5,6},{9,10}};
//没有进行赋值的位置系统会默认的赋值为0
定义数组的时候行号可以省略,系统会根据你定义的列号来计算
行号。
int num[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
x=sizeof(num)/sizeof(num[0][0])
数组的大小=数组的个数*元素的大小
二维数组取值
数组名+数组下标;
数组的下标 0-n-1(行号和列号是一样的)
比如num[3][4] 最小是num[0][0]—num[2][3]
//二维数组的输出使用嵌套循环
#include
#include
int main()
{
int num[3][4]={{1,2},{5,6},{9,10}};
int i,j;
for(i=0;i<3;i++)//行号
{
for(j=0;j<4;j++)//列号
{
printf(“num[%d][%d]=%d\n”,i,j,num[i][j]);
}
}
return 0;
}

//二维数组的输入使用嵌套循环

#include
#include
int main()
{
int num[3][4]={0};
int i,j;
for(i=0;i<3;i++)//行号
{
for(j=0;j<4;j++)//列号
{
scanf(“%d”,&num[i][j]);
}
}
for(i=0;i<3;i++)//行号
{
for(j=0;j<4;j++)//列号
{
printf(“num[%d][%d]=%d\n”,i,j,num[i][j]);
}
}
return 0;
}

5.指针

指针:指针他也是一种数据类型,他是用来保存地址的这么量。
基本数据类型:int char float double
构造数据类型:
数组、指针、结构体、枚举、共用体。
指针的定义:
数据类型 变量名;
变量名:除去数据类型和
,剩下的就是变量名。
数据类型:把变量名去掉,剩下的就是数据类型。
指针指向的空间类型:去掉变量名,去掉*就是指针
指向的空间类型。
int *p;//p int *; 指针指向的空间类型是int
char *q;//p char *指针指向的空间类型是char
char ch; &ch char *
int *p=&ch;//编译会报警告
p+1;//0x4//指针偏移了多少是在你定义指针时候就已经
确定了指针指向的空间类型。
一般定义:
int num=100;//num变量名, //&num int *
int *p=#
操作num可以通过num本身也可以通
p变量进行操作,因为p已经指向了num
的地址,你操作p就相当于操作num
打印:printf(“%d\n”,num);
printf(“%d\n”,*p);
输入:scanf(“%d”,&num);
scanf(“%d”,p);
修改:num=200;
*p=200;
&—取地址符号
int num; char a; double x;
&num int * &a char * &x double *
int num[5];
&num[0] – int *
num//代表首元素地址
&num//代表整个数据的地址
其他定义方式
int num=100;
int *p;//野指针
p=#
野指针
就是在定义指针的时候,没有给指针进行
赋值操作。这个时候指针就随意的指向了某一个
地址,如果这个地址上存放了比较重要的数据,
后边不小心进行解指针赋值操作了,那么就比较
危险了。避免就直接给他赋值为NULL.
解指针
格式:*变量名
通过指针指向的地址空间进行数据的操作。
指针大小:
固定大小8字节 – (64位操作系统)
固定大小4字节 – (32位操作系统)
指针偏移:
是按照指针指向的空间类型进行偏移的。
int p;
p+1;//0x4
二级指针
指针指向了,指针的指针。
指针空间里保存了另一个指针的地址。
格式:
数据类型 **变量名;
二级指针偏移大小一定是8 — 64位操作系统。
int **p;
char **p
double **x;
数组指针—本质就是一个指针,这个指针指向了
一个数组。
int num[5]={1,2,3,4,5};
int p=num;
打印num里的3
num[2],p[2],
(p+2),
(num+2)
指针指向二维数组
int num[3][4];
元素的个数:3个
元素的类型:int [4]
int (p)[4]=num;//num 首元素地址
//num[0] int [4]
//&num[0] int (
)[4]

int num[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
打印数组里的7 .
num[1][2],((p+1)+2),((num+1)+2),p[1][2]
(*(p+1))[2]
下标是i和j
num[i][j] ((p+i)+j)
C语言的5大区
栈区:系统自动开辟,自动释放
这里是对局部变量进行操作的
堆区:是留给程序员自己申请和自己释放的
malloc 申请空间
free 释放空间
全局区/静态区:全局变量和加static修饰的变量
生命期是整个.c
常量区:就是不可以改变的区域 --只读
12 不能对常量进行&
&12;不对
代码段:就是存储代码的二进制、
使用一个常量来对指针进行初始化
char *p=”hello”;hello 就是一个常量,对p进行初始化。
对e进行修改
*(p+1)=’E’;//不可以,因为他是一个常量
char buf[]=”hello”;
char *p=buf;
*(p+1)=’E’;
const
他是一个关键字,它可以对变量进行修饰,可以将变量
修饰成常量。
int num=100;
num=200;//可以随意修改
const int num=100;
num=200;//不可以修改,因为const已经把num修饰成了
常量,所有就只能读,不能改。const修饰指针
int num=100;
int *p=#//p指向了num的地址
int x=200;
p=&x;//此时p的指向就是x的地址了
int num=100;
const int p=#//p指向了num的地址
此时const 是修饰的
p
p是不能改变的
p的指向可以改变
p=500;//不可以,因为p已经被修饰成了常量了,不是可以改变的,但是p的指向可以改变。
int x=200;
p=&x;//此时p的指向就是x的地址了
int num=100;
int * const p=#//p指向了num的地址
此时const 是修饰的p
p是能改变的
p的指向不可以改变
p=500;//不可以,因为p已经被修饰成了常量了,不是可以改变的,但是p的指向可以改变。
int x=200;
p=&x;//不可以因为const修改了p,所有p的指向不能改变。
int num=100;
const int * const p=#//p指向了num的地址
此时第一个const修饰了
p,第二个修饰了p
所有
p和p都不可以改变。
*p是能改变的
p的指向不可以改变
p=500;//不可以,因为p已经被修饰成了常量了,不是可以改变的,但是p的指向可以改变。
int x=200;
p=&x;//不可以因为const修改了p,所有p的指向不能改变。

指针数组
本质是一个数组,只不过这个数组里保存的都是相同
类型的指针。
你现在想定义是个指针,怎么定义?
int *p1,*p2,………….;
此时你就可以使用指针数组来进行定义。
定义格式:
数据量类型 *变量名[元素个数];
int *arr[5];//相当于定义了五个int类型的指针变量。
int *arr[5]={NULL};
定义的时候初始化
int *arr[5]={“qwe”,”trr”,”adf”,”zcv”,”qrt”,};

6.函数

1.函数:用来封装代码,减少代码的重复
2.函数的分类:
1》主函数:main函数,一个工程他总是
开始于main函数,结束于main函数,且
只能有一个main。
子函数:
库函数:就是系统给你提供的可以实现
某一个功能的函数,你可以直接调用,
比如printf() scanf() strlen() putchar()
自定义子函数:第三方库函数,自己封装的函数。
3.自定义子函数
函数从定义到执行的过程:
1.定义函数:-- 就是你实现函数功能的过程。
2.声明函数:告诉调用的函数,这个被调的函数是存在的。
3.调用函数:在你使用的地方直接调用函数即可。
1、函数定义
格式:
函数返回值值类型 函数名(形参类型 形参名)
{
函数体;
return 返回值;
}
函数名在取名的时候最好,见名知意,驼峰式定义。比如: int Gipo_Init() 函数名要遵循变量的命名规则
1.数字字符下划线组合
2.但是数字不能开头
3.不能和关键字相同。
4.区分大小写

2、函数的声明
格式:函数返回值值类型 函数名(形参类型 形参名);
函数的声明一般都是放在main函数之前,也就是头文件下边。
3.函数的调用:
格式:
格式1:有返回值有形参
定义:返回值类型 函数名(实参)
{
函数体;
return 变量;
}
调用:返回值类型 变量=函数名(实参);
格式2:有返回值无形参
定义:返回值类型 函数名(void)
{
函数体;
return 变量;
}
调用:返回值类型 变量=函数名();
格式3:无返回值有形参
定义:void 函数名(形参)
{
函数体;
}
调用:函数名(实参);
格式 4:无返回值无形参
定义:void 函数名(void)
{
函数体;
}
调用:函数名();
函数返回值使用的是return,他每次只能返回一个值,
也可在返回一个表达式。
实参:
实参就是你调用函数的时候传递参数就是实参
1》如果没有形参,实参就不用提供,什么都不写
函数名();
2》实参可以是变量,表达式,常量,地址,数组
3》实参不能写数据类型
形参:
函数接收实参传递过来的变量就是形参。

4.形参和实参的关系:
1》形参的起始值来自于实参!-- 参数传递的过程
2》实参的个数和位置必须和形参一一对应,传参也按照对应位置传递。
3》形参和实参各自占用不同的内存空间
4》形参名和实参名可以一样,也可以不一样。
学习函数的方法
1.函数的功能
2.函数的参数代表的含义
3.函数的返回值
4.找一个代码示例

5.值传递和地址传递:
1》值传递:
子函数实现数值交换

这里没有实现的原因是因为你主函数里的x和y和人家的子函数里的m 和 n的地址空间没有关系。他们都是属于各自
函数的局部变量。这里只是值传递。

2》地址传递:
子函数实现数值交换

3.》数组传参 – 数组传参传递的是数组首元素的地址,并不是数组的整个大小。

4.》数组作为返回值(返回首元素地址) – 需结合static

局部变量:就是定义在某一个函数的内部
生存期:只是在该函数内,这个函数一旦运行
结束了,他就会被释放。
作用域:只能在该函数内部使用。
static:可以修饰变量和函数
修饰变量的时候,可以延长变量的生存期,但是不会改变变量的作用域。
在整个.c结束之后这个变量才会释放。
递归函数:
所谓的递归就自己调用自己的函数。
递归函数一般要有结束条件。
指针函数:本质就是函数,只不过这个函数需要
返回地址。
格式:函数返回值类型 *函数名(形参类型 形参名)

函数指针:本质是一个指针,只不过这个指针指向了
函数。

7结构体
结构体他是一个构造数据类型。
之前定义的这些数据类型的共同特点都是存储相同类型的数据。
比如数组。
结构体他里边可以存储各种数据类型的变量。
int float char double 指针。
结构体定义:
格式 :关键字 struct
struct 名字{
成员变量1,
成员变量2,
…………………
};
struct 名字:他代表新的数据类型,
你想访问结构体里的成员变量,第一步你要做到就是定义一个
结构体变量,然后利用这个结构体变量进行访问结构体里的成员变量。
1》先定义结构体,然后在定义结构体变量。
struct Std{
char name[20];
int ID;
int sorce;
};
struct Std std,std2; struct Std结构体数据类型 std:结构体变量名。
定义结构体的时候直接个体定义结构体变量
struct Std{
char name[20];
int ID;
int sorce;
}std1,std2;
取结构体的成员变量
a.如果你定义的是普通变量直接就是 结构体变量名.成员变量名。
b.如果你定义的是一个指针,那么取值就是结构体变量名->成员变量名.
结构体初始化
struct Std{
char name[20];
int ID;
int sorce;
};
struct Std std={“xiaohong”,1,80};

struct Std std={“xiaohong” };//后边的成员系统会默认的赋值位0

结构体求大小:
1.sizeof
2.自己算:
第一步:确定对齐方式-取以下两个的最小的
1》看系统
linux 32 对齐方式为4
linux 64 对齐方式为8
2》看结构体中最大的成员的对齐方式(只看基础的数据类型)
第二步:除第一个成员之外,其他成员距离首地址的偏移量,必须是对齐方式或者自身大小中较小者的整数倍。
第三步:结构体的整体大小得是是对齐方式的整数倍

7.1共用体

求大小:1.确定对齐方式,2.找最大成员,满足对齐方式的整数倍即可
共用体:就是共用体里的成员都公用一个空间,同一时刻只能有一个成员使用该空间。
关键字:union
格式:union 名字{
成员1;
成员2;
…….
};
定义并且取名;
union 名字{
成员1;
成员2;
…….
}std;
先定义在取名:
union 名字{
成员1;
成员2;
…….
};
union 名字 变量名;

#include
union Std{
int a;
int b;
}std;
int main()
{
std.a=100;
std.b=200;
printf(“a:%d\n”,std.a);
printf(“b:%d\n”,std.b));//打印的结果都是200 原因是他们
都公用一块空间,同一时刻只能有一个成员变量使用。
这里200就把100给覆盖了。
return 0;
}

求大小:
1.确定对齐方式
2.找最大成员,满足对齐方式的整数倍即可

7.2枚举

枚举是整形常量的一个集合。
关键字:
enum{a,b,c,d,e};
定义的时候如果没有给成员变量赋值,那么默认
第一个成员变量的值是0,后边依次加1
#include
enum{a=1,b,c,d,e,f,g};
int main()
{
int n;
printf(“请输入1-7的整数\n”);
scanf(“%d”,&n);
switch(n)
{
case a:
printf(“今天是星期1\n”);
break;
case b:
printf(“今天是星期2\n”);
break;
case c:
printf(“今天是星期3\n”);
break;
case d:
printf(“今天是星期4\n”);
break;
case e:
printf(“今天是星期5\n”);
break;
case f:
printf(“今天是星期6\n”);
break;
case g:
printf(“今天是星期7\n”);
break;
default:printf(“你输入的数字有误\n”);
break;
}
return 0;
}

typedef:类型重定义
格式:typedef 原数据类型名 新的数据类型名;

8链表

1.数组:存储相同数据的一个集合,并且他们的地址
空间是连续的,空间大小一般都是在使用的时候指定
了,如果指定空间不够,那么就无法在去扩大空间了,
如果你指定的空间太大,而你存储的数据比较小,就
会造成空间浪费。当你去删除一个数据或者是添加一个
数据的时候,你要移动整个数组,这就会给计算机造成
大量工作。
2.链表:
1》线性结构:顺序表,链表,栈,队列
2》非线性结构:树,二叉树
顺序结构:数组
链式结构:链表
链表和数组相比他的使用要不数组方便,在你去删除一个
数据和插入一个数据的时候他要比数组方便快捷,从空间
方面来说,他的空间利用是非常大的,就是你用多少就开辟
多少。
3.链表的组成:
1》节点组成:
2》节点的组成:
头节点:链表中的第一个节点,头节点的数据域一
般是不存放内容的。
尾节点:节点的最后一个,他的指针域一定是NULL
数据域:–就是存储数据的
指针域:–存放下一个节点的地址的
每一个链表节点的地址不一定是连续的。系统随机分配的。
4.第一种定义节点的方法
struct NODE{
char name1[0];
int ID;
float score;//数据域
struct NODE *addr;//指针域
};这种定义一般很少使用

第二种定义的方法
struct Stu{
char name[20];
int ID;
float score;
};

struct NODE{
struct Stu stu;//数据域
struct NODE *addr;//指针域
};

这种方法使用的是比较多的。

5.关于节点的几个重要的函数:
malloc 和 free 一定是配对使用的。
空间申请函数:
函数的原型:void *malloc(size_t size);
函数功能:申请空间,malloc申请的空间一定是在堆上。
堆区:有程序员自己申请–malloc,自己释放–free。
函数的头文件:#include
函数形参:size 就是你要开辟空间的大小
函数的返回值:成功返回一个指向申请空间的地址,失败NULL。
void * – 万能的类型,你可以将他转换成任何的数据类型。
空间释放:
函数原型:void free(void *ptr);
函数功能:释放malloc申请的空间
函数头文件:#include
函数形参:ptr 就是malloc函数的返回值。
函数返回值:无
清空空间的函数
函数原型:void *memset(void *s, int c, size_t n);
函数功能: 清空你指定的空间
函数头文件:lude
函数参数:s 指针指向的地址(清空的空间)
c 你要把这块空间里清空位什么东西。一般是零
n 空间大小 可以使用sizeof计算
函数返回值:成功返回一个地址,失败NULL

节点链接的方式这里采取的是尾插法。
节点的插入:尾插法

判断某一个学号是否存在

1.链表:
使用链表你要动态开辟空间,并且
也要动态的释放空间。
2.数组:你在使用的时候是不需要你动态的开辟
和动态的释放。
链表你申请每一个节点的地址,不一定是连续的
数组的地址一定是连续的。
顺序结构:数组
链式结构:链表
3.链表的组成
节点组成:
头节点:-- 就是一个链表的第一个节点。
头节点里的数据域一般是不存放
数据的。
尾节点:就是链表的最后的一个节点,指针域
一定是NULL

    数据域:就是保存你要存储的数据。
            指针域:存储下一个节点的地址

4.定义的第一种方式
struct NODE{
char name[20];
int ID;
float score;//数据域
struct NODE *addr//指针域
};
定义的第二种方式
struct Stu{
char name[20];
int ID;
float score;
};
struct NODE {
struct Stu stu;//数据域
struct NODE *addr;//指针域
};
5.和节点相关的几个重要的函数
1.申请空间的函数
malloc
函数功能:在堆区申请空间
函数的头文件:#include
函数的参数:size:申请空间的大小
函数返回值:成功返回空间的地址,失败NULL
2.释放空间的函数free
函数的功能:释放malloc申请的空间
函数头文件:#include
函数参数:pri:就是malloc函数的返回值
函数返回值:无
3.清空空间的函数memset
函数功能:清空你指定的空间
函数头文件:#include
函数的参数:pri:你要清空空间的地址
c 你要把这块空间里清空为什么,一般是0
n 你要清空的大小,这里一般是sizeof()
函数返回值:成功返回一个地址,失败返回NULL
双链表:
双链表就比单链表多了一个指针域
双链表组成:
节点:
数据域:存放数据的
前指针域:存放指向前一个节点的地址
后指针域:存放指向后一个节点的地址

双向循环链表:就是最后一个节点和头节点进行首尾相连了
双向链表的插入:

核心代码
s->prior=p;
s->next=p->next;
p->next->prior=s;
p->next=s;
循环链表的删除:删除之前先判断删除的是尾节点还是中间的节点

核心代码:这里只适合删除中间的节点
p->prior->next=p->next;
p->next->prior=p->prior;
free§;
删除为节点
p->prior->next= NULL;
单项循环链表:就是最后一个节点和头节点进行首尾相连了
变量循环结束的条件
核心代码:
struct Std *tep = head->next;
while(tep->next!=head->next)
{
printf(“%-2d”,tep->data);
tep=tep->next;
}

你可能感兴趣的:(C语言,linux,c语言)