【C语言】C语言总结

声明,本文来自中国mooc中的翁凯C语言总结

第一章介绍

变量定义

  • 变量定义的一般形式为:<类型名称><变量名称>
  • 变量需要一个名字,变量的名字是一种表示符,意思是用来识别不同的标识符
  • 标识符的基本构造规则为:标识符只能由字母、数字和下划线组成,标识符不能以数字开头,关键字(保留字)不能做标识符。

变量赋值和初始化

  • 变量在被使用之前要赋一次值,这就是初始化
  • 所有的变量在使用之前必须定义或声明,所有变量必须具有确定的数据类型

常量

  • 固定不变的常数,直接写在程序里,我们称之为直接量
  • const int coiunt = 100;
  • const是一个修饰符,加在int前面,表示初始化后就不可以再修改

浮点数

  • 两个整数的运算结果只能是整数
  • 浮点数是带小数点的数值,表示小数点是可以浮动的,与定点数小数点永远出现在第几位不一样
  • 浮点数和整数同时进行运算时,c会将整数转换成浮点数
  • 用scanf输入的时候float用%f,double用%lf;用printf输出的时候无论是float类型还是double类型,输出都需要用%f,在有些系统中用%lf也不会出错,但是尽量还是按照标准来。

运算符的优先级

60a2aff4e1e94987a50839a15cf8da9f.png

 复合赋值

6807a3dac3d6461a981b87569c5b1c00.png

第二章介绍

关系运算符优先级

所有的关系运算符的优先级比算术运算的低,但是比赋值运算的高。

  • 7>=3+4
  • int r=a>0
#include<stdio.h>
int main()
{
	printf("%d\n",7>=3+4);
	return 0;
}
#include<stdio.h>
int main()
{
//	printf("%d\n",7>=3+4);
	int a=5;
	int r=a>0;
	printf("%d\n",r); 
	return 0;
}

二者的输出结果都为1

23df4ab0138849b8b3ff8d58696e7924.png

 判断是否相等的 == 和 != 的优先级比其他的低,而连续的关系运算是从左到右进行的

  • 5>3 == 6>4
  • 6>5>4
  • a == b == 6
  • a == b>0
#include<stdio.h>
int main()
{
	int a=3,b=5;
	printf("%d\n",5>3 == 6>4);
	printf("%d\n",5>6>4);
	printf("%d\n",6>5>4);
	printf("%d\n",5>6>0);
	
	printf("%d\n",a==b==6);
	printf("%d\n",a==b>0);
	return 0;
}

b0f7a697aa114ba596a927e644554221.png

 if和else语句

        一个基本的if语句由一个关键字if开头,跟上在括号里的一个表示条件的逻辑表达式,然后是一对大括号“{}"之间的若干条语句。如果表示条件的逻辑表达式的结果不是零,那么就执行后面跟着的这对大括号中的语句,否则就跳过这些语句不执行,而继续下面的其他语句。

        如果没有一对大括号“{}",那么后面跟上一条语句之后加上分号表示if语句的结束。else也是同样的道理。但else需要注意的是else总是和最进的if进行配对,除非加了大括号,特别说明的是缩进格式是不能够暗示else匹配的。

扫描二维码关注公众号,回复: 17271821 查看本文章

switch-case语句

switch语句可以看作是一种基于计算的跳转,计算控制表达式的值后,程序会跳转到相匹配的case (分支标号)处。分支标号只是说明switch内部位置的路标,在执行完分支中的最后一条语句后 ,如果后面没有break,就会顺序执行到下面的case里去,直到遇到一个break,或者switch结束为止。
 

#include<stdio.h>
int main()
{
	int type;
	scanf("%d",&type);
	
	switch(type){
		case 1:
			printf("你好!");
			break;
		case 2:
			printf("早上好!"); 
			break;
		case 3:
			printf("晚上好!"); 
			break;
		case 4:
			printf("再见!"); 
			break;
		default:
			printf("啊,什么啊!") ; 
	}
	
	return 0;
}

a2a82bf9b6154219ba79067a0a41262b.png

 while循环与do-while循环的区别

        while循环先判断执行条件,也就是while后面括号中的条件,如果为1,则会执行循环体;do-while循环则先执行循环体,然后再进行判断是否循环条件。

44c271defae34072b7b6e4f18158cbfe.png

         很明显,上面所示的流程图中,左边的是do-while,右边的是while,因为左边的先进循环体,右边先判断条件。

for循环

        关于for循环,比较重要的地方就是循环次数问题了,for(i=0;i<n;i++)中,循环次数是n,循环结束以后,i的值是n。循环变量的控制,是选择从0开始还是从1开始,判断i<n还是i<=n,对循环的次数和循环变量的值都有影响。

#include<stdio.h>
int main()
{
	int i;
	for(i=0;i<5;i++)
	{
		printf("i=%d ",i); 
	} 
	printf("最后的i=%d",i);
	
	return 0;
}

d58766e74da448ea88dca2bbeacebc2b.png

 修改循环条件中i 的值,可以看到仍然有5次循环,但是最后输出的结果不一样。

for(i=1;i<=5;i++)

fe2665baf314489d8fefc0a56a9f6926.png

 while循环和for循环是可以相互改造的。因此它们的流程图也是相同的。

bef0d3e413f14c5d9de6396267efd7b0.png

73f430d255bb462a94ad3786dd6d67e1.png

 break和continue语句

  • break跳出循环;
  • continue跳过循环(即跳过循环,执行下一次循环);
  • break从嵌套循环中跳出时,break只能跳出其所在的循环。

第三章介绍

数据类型

  • 整数:char,short,int,long,long long
  • 浮点数:float,double,long double
  • 逻辑:bool
  • 指针
  • 自定义类型

所表达的数的范围:char<short<int<float<double

Sizeof可以做什么

  • 在C语言中,sizeof是一个运算符,给出某个类型或变量在内存中所占的字节数。
  • sizeof是静态运算符,他的结果在编译的时刻就决定好了

282e0c5700b3400a82af1a526805ef8b.png

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

整数的范围

  • char:1字节:-128~127
  • short:2字节:-32768~32767
  • int: 取决于编译器(CPU) 通常的意义是“1个字’:gif.latex?-2%5E%7B32%7D~gif.latex?2%5E%7B32%7D-1

整数的输入输出

整数只有两种形式:int (%d)或 long long(%ld)

八进制和十六进制

  • 一个以0开始的数字字面量是八进制
  • 一个以0x开始的数字字面量是十六进制
  • %o用于8进制,%x用于16进制
  • 8进制和16进制只是如何把数字表达为字符串,与内部如何表达数字无关

浮点类型

1e2f5f80f0e34b4389f3a36e2ed904c7.png

 在靠近0出有一小部分是浮点数无法表达的,即大约在0~gif.latex?1.2%5Ctimes10%5E%7B-38%7D左右。±inf表示正负无穷大,nan表示不是一个有效的数字。

2854c01106fc484e9e2563a4f297c1a1.png

字符类型

char是一 种整数,也是一种特殊的类型:字符。这是因为:

  • 用单引号表示的字符字面量:'a','1'
  • ''也是一个字符
  • printf和scanf里用%c来输入输出字符

如何输入'1'这个字符给char c?

901c187b98b442f29cb950a056276499.png

 很明显,这样是不行的,因为当成一个字符的话明显“1”的ASCII码,并不是数字1。

74d5f138abf74bdea654e6cfe08812fd.png

bb2373a80b3046979c07d7491d7dd9e0.png

 字符计算

9d8a17496e0e4d4695e6563a755d11ca.png

 类型转换

        自动类型转换:当运算符的两边出现不一致的类型时,会自动转换成较大的类型。(大的意思是能表达更大范围的数)

  • char--->short--->int--->long--->long long
  • int--->float--->double
  • 对于printf,任何小于int的类型会被转换成int;float会被转换成double
  • 但是scanf不会,要输入short.需要%hd

        强制类型转换:要把一个量强制转换成另一个量(通常是较小的类型),需要注意这时候的安全性,小的变量不总能表达大的量。

  • 强制类型转换的优先级高于四则运算

布尔类型

ture和false,这个布尔类型是在c99之后确定下来的,要引入头文件#include<stdbool.h>
 

逻辑运算

  • 逻辑运算是对逻辑量进行的运算,结果只有0或1
  • 逻辑量是关系运算或逻辑运算的结果

584123895129479e9129e55e44774bc1.png

 优先级:

  • !> && > ||

ea8c705d6ced4b06ab28a994eea6ceff.png

 短路

  • 逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算
  • 对于&&,左边是false时就不做右边了
  • 对于||,左边是true时就不做右边了

deda94e6895c43d0abebe0e3886831fb.png

 条件运算

9227fb6c4ac1411784b5f1d20c5f2a1e.png

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

70809d107030493dba9c964a57eaac7f.png

 条件运算和逗号运算

1a8f723e2b5b4522996b79722b64f93c.png

这里会有警告出现

56837fa6973a4112ae306879e1fdba24.png

 目前逗号表达式的作用:(for循环中放多个计算)

01f6d9bf95134569b8f0d5c163dd15ee.png

基本ASCII码参照表:(此图片资源来源于网络)

89a5e9ae090d4e2697d57958ae4a2b94.png

第四章介绍

函数

  • 函数是一块代码,接收零个或多个参数,做一件事情,并返回零个或一个值
  • 函数名(参数值);
  • ()起到了表示函数调用的重要作用,即使没有参数也需要()
  • 如果有参数,则需要给出正确的数量和顺序
  • 这些值会被按照顺序依次用来初始化函数中的参数

函数的返回值

 

无返回值的函数:

  • void函数名(参数表)
  • 不能使用带值的return
  • 可以没有return
  • 调用的时候不能做返回值的赋值

有返回值的函数:

  • 如果函数有返回值,就必须使用带值的return

函数先后关系

函数要先声明再使用,因为C编译器是自上而下的顺序分析你的代码的。

  • 函数头以分号“;”结尾,就构成了函数原型;
  • 函数原型的目的是告诉编译器函数长什么样;
  • 函数原型中声明可以不写参数的名字的;

3587720748c34da787d5c443c3dd5a24.png

 dc406e139e464c69a7008569f8089ad9.png

 3529654f78974ec5aeeba60ddfb75349.png

参数传递

 调用函数:

  • 如果函数有参数,调用函数时必须传递给它数量、类型正确的值
  • 可以传递给函数值是表达式的结果
#include<stdio.h>
int max(int,int);
int main()
{
	int a,b,c;
	a=5;
	b=6;
	c=max(10,12);
	c=max(a,b);
	c=max(c,23);
	c=max(max(23,45),a);
	c=max(23+45,b);
	printf("c=%d\n",c);
	return 0;
}

int max(int a,int b)
{
	if(a>b)
	{
		return a;
	}
	else
		return b;
	//return a>b?a:b; 
}

类型不匹配:

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

值传递:

  • 每个函数都有自己的变量空间,参数也位于这个独立空间中,和其他函数没有关系
  • 过去,对于函数参数表中的参数,叫做“形式参数”,调用函数时给的值,叫做“实际参数
  • 现在为了更好的区分,我们将这些称呼为参数

5eaf72548f7d40bda831d033838c85b5.png

 局部变量

有时候叫本地变量,

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

局部变量的规则

局部变量是定义在块内

  • 他可以是定义在函数的块内,
  • 也可以定义在语句的块内,
  • 甚至可以随便拉一对大括号来定义变量

程序运行进入这个块之前,其中的变量不存在,离开这个块,其中的变量就消失了
块外面定义的变量在里面仍然有效
块里面定义了和外面同名的变量则掩盖了外面的(局部变量优先原则)
不能在一个块内定义同名的变量
局部变量不会被默认初始化

参数在进入函数的时候被初始化了

372717cb93474fc4ac3f863c52e45135.png

注意事项

6fedea481228405cbf7222829323cb74.png

 一定要在函数里面写上参数,不然会出现一些错误。

41cb1bfce8604a9e9f36baa64a1b3f5d.png

 调用函数时的逗号和逗号运算符的区别

调用函数时的圆括号里的逗号是标点符号,不是运算符。

  • f(a,b)这里的逗号是标点符号,不是运算符
  • f((a,b))这里的逗号是运算符
  • f(a,b)和f((a,b))的区别就是前者传入了两个参数,而后者只传入了一个参数

函数嵌套问题

C语言不允许函数的嵌套定义,可以函数里面可以放函数的声明,但不能放函数的定义。

第五章介绍

定义数组

<类型> 变量名称 [元素数量]  

  • int grades[100];
  • double weight[20];
  • 元素数量必须是整数

数组特点

  • 数组是一种容器(放东西的东西)
  • 数组中的元素具有相同的数据类型
  • 一旦创建,不能改变大小
  • 数组中的元素在内存中是连续依次排列的

34b5c135cd78446b8ed06e00d55b4736.png

 数组的单元

  • 数组的每个单元就是数组类型的一个变量
  • 使用数组时放在[ ]中的数字叫做下标或索引,下标从0开始计数:

        数组下标的有效范围:

        编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写

         一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃

写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束
 

#include<stdio.h>
int main()
{
	const int number = 10; 
	int x;
	int i;
	int count[number];
	for(i=0;i<number;i++)
	{
		count[i]=0;//初始化数组 
	} 
	scanf("%d",&x);
	while(x!=-1)
	{
		if(x>=0&&x<=9)
		{
			count[x]++;
		}
		scanf("%d",&x);
		
	}
	for(i=0;i<number;i++)
	{
		printf("%d:%d\n",i,count[i]);
	}
	return 0;
}

数组的集成初始化

int a[] = {2,4,6,7,1,3,5,9,11,13,23,14,32};

因此上面在初始化count数组时也可以不用写循环,直接使用数组集成初始化即可。

const int number = 10; 
	int x;
	int i;
	int count[number]={0}
	//for(i=0;i<number;i++)
	//{
	//	count[i]=0;//初始化数组 
	//} 

数组的大小

sizeof给出整个数组所占据的内容的大小,单位是字节
 

2bfa9c4ce248412a94832fd5d7ec5b85.png

sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
sizeof(a)/sizeof(a[0])得到的就是数组大小,这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码。

ca6695b976784a78ac0be44a0082181e.png

数组的赋值

c8ccfc3ace0f4b7da0b1761292afdd31.png

 数组变量本身不能被赋值,要把一个数组的所有元素交给另一个数组,必须采用遍历。(唯一方法,没有其他方法将一个数组赋给另一个数组了)

c975d6c1e59e4188a97cef33b6301971.png

         关于for循环中i的问题,一般情况下i是从0开始的,然后小于数组长度,这样恰好能从第一个数组元素开始进行遍历。

数组作为函数的参数时:

  • 不能在[]中给出数组的大小
  • 不能再利用sizeof来计算数组的元素个数!---->此部分会在后面数组中解释
     

二维数组的初始化

int a[][5]={
		{0,1,2,3,4},
		{2,3,4,5,6},
	};
  • 列数是必须给出的,行数可以由编译器来数
  • 每行一个{},逗号分隔
  • 最后的逗号可以存在,有古老的传统
  • 如果省略,表示补0
  • 也可以使用定位(only c99)

第六章介绍

取地址运算

8c5a6d18359143b08544f5e16e147a76.png

 这说明int在内存中占了4个字节,一个字节等于8个比特,所以一共是32个比特。

运算符&:

scanf("%d",&i);里面的&符号:

  • &作用是获取变量的地址,他的操作数必须是变量
  • int i;printf("%x",&i);

d5fc957757d846578da8d3718c26871b.png

 输出结果如下:(结果一样,这是在32位编译环境下)1655693c005c4b30a34d7778cf54420e.png

 换成64位编译环境就会输出结果不一样:56b6bbbcba264952ac0066e354c501db.png

 e08ecbe6738c4af5ac6ffaaca8cbbbcf.png

  •  变量的地址
  • 相邻的变量的地址
  • &的结果的sizeof
  • 数组的地址
  • 数组单元的地址
  • 相邻的数组单元的地址

309e1f6d6d804a438b79e5f25192bc27.png

32233037a741459bbd18e26859570a20.png

 指针

指针就是保存地址的变量,

0e06427229f84a5b91d687743915069e.png

指针变量:

  • 指针变量的值是内存的地址
  • 普通变量的值是实际的值
  • 指针变量的值是具有实际值的变量的地址

作为参数的指针:

b421f6fd005c4576826149586fc8556f.png

 ​​​​d961488afa2a433aad3e14c933182c5d.png

c663a14df7534af9bd882b481cc6f45a.png

 访问那个地址上的变量*

  • *是一个单目运算符,用来访问指针的值所表示的地址上的变量
  • 可以做右值也可以做左值
    • int k = *p
    • *p = k+1
  • 因此*p作为一个整体可以看做一个整数

    1b9bb18d762e4c5c877ab7284b1b0b86.png

da905e43fee2496cb438c1908cb305c4.png

指针的使用

#include<stdio.h>
void swap(int *pa,int *pb);

int main()
{
	int a=5;
	int b=6;
	swap(&a,&b);
	printf("a=%d,b=%d\n",a,b);
		
	return 0;
}
void swap(int *pa,int *pb)
{
	int t=*pa;
	*pa=*pb;
	*pb=t;
}

此代码就是使用了指针进行的两个数的交换,就是交换两个变量值

#include<stdio.h>
void minmax(int a[],int len,int *max,int *min);
 
int main(){
	int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,47,24,55,89};
	int min,max;
	minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
	printf("min=%d,max=%d\n",min,max);
	
	return 0; 
}
 
void minmax(int a[],int len,int *min,int *max){
	int i;
	*min = *max = a[0];
	for(i=1;i<len;i++){
		if(a[i]<*min){
			*min=a[i];
		}
		if(a[i]>*max){
			*max=a[i];
		}
	}
}
 
 

此外,函数返回多值时,某些值就只能通过指针返回传入的参数实际上是需要保存带回的结果的变量。

指针常见错误

c720da06ba254b6abe3220f158dff4c7.png

 定义了指针变量,还未指向任何变量就直接开始使用。

指针与数组问题

为什么数组传进函数后的sizeof不对了,传入函数后的数组变成了什么

68396fde8a234f2dbc06841ad8843a26.png

  •  函数参数表中的数组实际上是指针
  • sizeof(a) ==  sizeof(int *)
  • 但是可以用数组的运算符[ ]进行运算

数组参数

fa69db74edba47d4949683c38171857c.png

 数组变量是特殊的指针,数组变量本身表达地址,所以:

  • int a[10];  int * p = a;  //无需使用取地址符&
  • 但是数组的单元表达的是变量,需要使用&取地址
#include<stdio.h>
void minmax(int *a,int len,int *max,int *min);

int main(){
	int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,47,24,55,89};
	int min,max;
	printf("main sizeof(a)=%lu\n",sizeof(a));
	printf("main   a=%p\n",a);
	minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
	printf("min=%d,max=%d\n",min,max);
	int *p = a;
	printf("*p=%d\n",*p);
	printf("p[0]=%d\n",p[5]);


	return 0; 
}

void minmax(int *a,int len,int *min,int *max){
	int i;
	printf("minmax sizeof(a)=%lu\n",sizeof(a));
	printf("minmax a=%p\n",a);
	a[0]=1000;
	*min = *max = a[0];
	for(i=1;i<len;i++){
		if(a[i]<*min){
			*min=a[i];
		}
		if(a[i]>*max){
			*max=a[i];
		}
	}
}


12b952aa65124dfba86671b2fb5993f8.png

 数组变量是特殊的指针

 数组变量本身表达地址,所以

  • int a[10];int *p=a;//无需使用&取地址
  • 但是数组的单元表达的是变量,需要用&取地址
  • a=&a[0]

 [ ]运算符可以对数组做,也可以对指针做:

  • p[0]  <==> a[0]

 *运算符可以对指针做,也可以对数组做:

  • *a=1;

 数组变量是const指针,所以不能被赋值

570ab4b6096b4519971f6d6217b8d4ff.png

  • int a[] <==> int * const a   不能再赋值
    43b286a23bf24415982bb5bbff77fc5c.png

 

#include<stdio.h>
int main(){
	int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,47,24,55,89};
	int *p = a;
	printf("*p=%d\n",*p);
    printf("p[0]=%d\n",p[0]);
	printf("p[5]=%d\n",p[5]);
	return 0; 
}

72f818bc15854200b0daa9359864e166.png

 对着个代码运行的结果可以这样理解:p指针指向的是数组a,p[0]是指将所指的地方当做一个数组,取该数组的第1个元素,p[5]表示取数组a的第6个元素。

34b03f9fa17c4ef0bd95cbf0554fb171.png

 指针运算

#include<stdio.h>
int main(){
	char ac[] = {0,1,2,3,4,5,6,7,8,9};
	char *p = ac;
	printf("p  =%p \n",p);
	printf("p+1=%p \n",p+1);
	
	int ai[] = {0,1,2,3,4,5,6,7,8,9};
	int *q = ai;
	printf("q  =%p \n",q);
	printf("q+1=%p \n",q+1);
	return 0;
}

3fb53fd5d7d84628bbf703d09c90c589.png

f36c4475f85a46c8b29faacd97d7c0bf.png

 地址变化:sizeof(char)=1,sizeof(int)=4.当我们给一个指针+1的时候,我们的地址变化并不是在地址值上+1,而是在地址值上加一个sizeof(数据类型)。 

d33bfaf1923e47c88221c5bef2c613a8.png

26b0e09c2b2c41bbb16d1030446fb781.png

这些算术运算可以对指针做:

  • 给指针加、减一个整数(+, +=,-,-=)
  • 递增递减(++/- -)
  • 两个指针相减
     

两个指针相减的结果是什么?通过以下程序可以很清楚的了解到。

656cf8c4841b427088599b97e1cdaad4.png

 这里也并不是直接的通过地址值相减,而是减完后除以了sizeof(数据类型),得到的是这两个指针之间有多少个这样的地址,或者能放几个这样的地址。、

*p++

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

277fbe2170bc45f3963444f4fcf45d36.png

 指针类型

  • 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
  • 但是指向不同类型的指针是不能直接互相赋值的
  • 这是为了避免用错指针

ecdb211f24424045a59327fed16bd86a.png

第七章介绍

动态内存分配

91477cb2cd0e428bb4a8043b62d3e032.png

free() 

bd17d4b26ce44e7d8994a584fd09be59.png

字符串

字符数组不是字符串

char word[]={'H','e','l','l','o','!','\0'}; 

3a3e660dcbbc42089c38bcab06d68462.png

 那么什么是字符串呢?

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

字符串变量

	char *str = "hello";
	char word[] = "hello";
	char line[10]= "hello";

其中char *str = "hello";是指有一个叫str的指针,它指向了一个字符数组,这个数组的内容是hello;

char word[] = "hello";是指就是有个一叫做word的数组;

char line[10]= "hello";是指line这个数组长度为10,里面放的内容为hello(hello有五个字符,在line中占据6个位置)

字符串常量

f067f67d83164204a3702e5e96e03f3a.png

char *s="hello world";

3f56be3f8e3e4aa79e531df8f4a82922.png

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

6a2e4cf5b9a340b7a26f0b0058e83983.png

 457fb30137d34d3d94951b4f7e273232.png

使用指针还是数组? 

e23565f7a9214952bc92541cc7409d82.png

char * 是不是字符串

6e6147dc2031450ebc057b7469384dde.png

 

字符串的赋值

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

5bbe5646ce754618a39aac60a56d7128.png

 字符串的输入输出

  • char string[];
  • scanf("%s",string);
  • printf("%s",string);
  • scanf读入一个单词(到空格、Tab或回车为止)

0a5bc2c0a5f94d04b1900a6e22a25d15.png

53ac7fd0ad7f42ccbb87e38cfe2422c6.png

 输出结果:2220b8c235e24dc89911022d42ef9337.png

安全的输入

  • char string[8];
  • scanf("%7s",string);
  • 在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小1

常见错误:

  • char *string;
  • scanf("%s",string);
  • 以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用了
  • 由于没有对string初始化为0,所以不一定每次运行都出错(没有初始化,可能这个时候string指针指向的是某个内存中不能写入,程序就会崩溃

空字符串

  • char buffer[100]="";
  • 这是一个空字符串,buffer[0]='\0'
  • char buffer[]="";
  • 这个数组的长度是1(这个buffer数组里头放不下任何字符串)

字符串数组

  • char **a;
    • a是一个指针,指向另一个指针,那个指针指向一个字符(串)
  • char a[][];
    •  

706158d8bf7c4187b0c7b8e51073b75f.png

字符的输入输出

putchar

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

getchar

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

字符串函数strlen

string.h中的函数:

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

550d92ffb4f944fd944930980a0321c2.png

 可以看到strlen输出结果表示不带结尾0的字符串长度,而sizeof表示的是带0的占据的内存空间的字符个数

  • strcmp:比较两个字符串
    • 返回0:s1==s2
    • 返回1:s1>s2
    • 返回-1:s1<s2

63c5cc60379d4f3abf8d611c5c64fd0b.png

  • strcpy:
    • char * strcpy(char *restrict dst,const char *restrict src);
    • 把src的字符串拷贝到dst
    • restrict表名src和dst不重叠
    • 最后返回的是dst
  • strchr:字符串中寻找字符
    • 返回NULL表示没找到

25205b9e84b64c92baf6e610fcef920c.png

1321be3429824efc8ddb478e7c113c3a.png

  • 1

第八章介绍

枚举

用符号而不是具体的数字来表示程序中的数字

  • 枚举是一种用户定义的数据类型,它用关键字enum来声明
    • enum 枚举类型名字 {名字0, ....名字n};
  • 枚举类型的名字通常并不真的使用,要用的是在大括号里的名字,因为它们就是就是常量符号,它们的类型是int,值则依次从0到n。如:
    • enum colors { red, yellow, green } ;
  • 就创建了三个常量,red的值是0,yellow是1, 而green是2。
  • 当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量值名字。
     

虽然枚举类型可以当做类型使用,但是实际上很少用(不好用)。

结构类型

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

声明结构的形式1:

struct point
{
	int x;
	int y;	
};
struct point p1,p2;

p1,p2都是point,里面有x和y的值。


声明结构的形式2:

struct point
{
	int x;
	int y;	
}p1,p2;

p1,p2都是point,里面有x和y的值。


声明结构的形式3:

struct
{
	int x;
	int y;	
}p1,p2;

p1,p2是一种无名结构,里面有x和y的值。


结构的初始化

111d56f699144675a3d79d42332762b6.png

 给的值填进去,没给的填0,类似于数组

#include <stdio.h>

struct Time
{
	int hour1;
	int min1;
	int sec1;
};
//必须按照结构体顺序
struct Time t1={
	17,
	49,
	54
};
//与结构体顺序无关
struct Time t2={
	.min1=49,
	.sec1=54,
	.hour1=17
};
//与结构体顺序无关
struct Time t3={
	min1:49,
	sec1:54,
	hour1:17
};


int main()
{
	printf("t1:%d:%d:%d\n", t1.hour1, t1.min1, t1.sec1);
	printf("t2:%d:%d:%d\n", t2.hour1, t2.min1, t2.sec1);
    printf("t3:%d:%d:%d\n", t3.hour1, t3.min1, t3.sec1);
   return 0;
}
//结果输出
t1:17:48:54
t2:17:48:54
t3:17:48:54

结构成员

  • 结构和数组有点类似
  • 数组用方括号[]和下标访问其成员,如a[0]=10;
  • 结构用 . 运算符和名字访问其成员
    • today.day
    • student.firstName
    • p1.x
    • p2.y

结构运算

  • 要访问整个结构,直接用结构变量的名字
  • 对于整个结构,可以做赋值、取地址,也可以传递给函数参数
    • p1=(struct point) {5,10};        //相当于p1.x=5,p1.y=10
    • p1=p2;                                    //相当于p1.x=p2.x,p1.y=p2.y

0d21fc0108f549f7a319603db281aefb.png

#include<stdio.h>
struct date
{
	int month;
	int day;
	int year;
};
 
int main()
{
	struct date today ;
	today=(struct date){07,31,2014};
	struct date day;
	day=today;
	printf("Today's date is %i-%i-%i.\n",today.year,today.month,today.day);
	
	printf("Thismonth is %i-%i-%i.\n",day.year,day.month,day.day);
	return 0;
}

结构指针

  • 和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
  • struct date *pDate = &today;(不用&符号取地址编译错误)

结构作为函数参数

  • int numberOfDays(struct date d)

指向结构的指针

  • struct date
    {
    	int month;
    	int day;
    	int year;	
    }myday;
    struct date *p = &myday;
    (*p).month = 12 ;
    p->month=12;
  • gif.latex?%5Crightarrow表示指针所指的结构变量中的成员

7435c235b43f4613aee0486aa2e27784.png

结构数组

struct date dates[100];
struct date dates[]={
	{4,5,2005},{2,4,2006}
};

274f0f19b4e04b239cc5991770935b8e.png

自定义数据类型Typedef

4b17a4c755234beebc12142e8ac8c597.png

全局变量

  • 定义在函数外面的变量是全局变量
  • 全局变量具有全局的生存期和周期性
    • 它们与任何函数都无关
    • 在任何函数内部都可以使用它们
  • 没做初始化的全局变量会得到0值
    • 指针会得到NULL值
  • 只能用编译时刻已知的值来初始化全局变量
  • 它们的初始化发生在main函数之前

静态局部(本地)变量

  • 在本地变量定义时加.上static修饰符就成为静态本地变量
  • 当函数离开的时候,静态本地变量会继续存在并保持其值
  • 静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值

2e57d9790a2547f5bac4c258dc4ff6cc.png

 加上static使之称为静态局部变量之后每次进入就不会重新初始化了

590425b6c4404fdd802679921b39efb5.png

  • 静态本地变量实际上是特殊的全局变量
  • 它们位于相同的内存区域
  • 静态本地变量具有全局的生存期,函数内的局部作用域
  • static在这里的意思是局部作用域(本地可访问)

79d9b6a4193743ac98eb0a743d73c58d.png

猜你喜欢

转载自blog.csdn.net/weixin_55848732/article/details/129854452