C程序设计语言基础

机器语言与高级语言

计算机硬件只能够识别电平信号,正电平或负电平,计算机的的各种按钮触发各种电平与计算机交互。随着随着操作系统的发展,人们用1,0分别表示正电平和负电平,并由0,1所组成的一系列指令指挥计算机工作。

一种由二进制所组成的指令集合称为机器语言。机器语言依赖于硬件,因此也称为低级语言,机器语言全由二进制指令组成难学,难计,难写。
20世纪50年代左右出现了高级语言,高级语言依赖于操作系统,使用字符作为计算机指令,而字符正是自然语言的一部分,更贴合人类的使用习惯。高级语言依赖于操作系统,由操作系统将其转化为字节,在转化为二进制,最后转为硬件指令。

C语言是最流行的系统语言,诞生于贝尔实验室,随着C语言的发展,其移植到各种计算机上,已成为世界上应用最广泛的语言。

后来的许多语言都受到c的影响,大多都是用c来开发,如Java,Python等。高级语言学习更方便也更快捷。

C语言示例

#include<stdio.h>

int main(){
	//声明调用函数
	int max(int a,int b);
	int a,b,c;
	scanf("%d,%d",&a,&b);
	c = max(a,b);
	printf("max=%d",c); 
}

int max(int a,int b){
	int z;
	if(a>b) z =a;
	else z = b;	
	return z;
}


/*
1. c语言程序主要有函数组成,函数是c程序的基本单位
2. 变量是程序中传递的方式,有值传递和引用传递
3. 函数的定义语法为:
	函数返回值类型   函数名 (参数类型 参数 ...) {
		//变量和方法组成 
	
	}
	例如: int max (int a,int b) {
		//
		return 0;
	}
4. 变量的定义语法为:
	变量类型 变量名  [= 数据 ]   []中为可省略内容
5. #include<stdio.h> 是c语言的标准输入输出库,可以直接用到内部的函数
6. int max(int a,int b) 为函数名部分,{}中的内容为函数的执行体,当函数定义在函数调用之后时需要在调用前声明函数名部分。
7. c语言函数总是从main(主函数开始执行)
8. c语言书写格式自由,语句用;隔开
9. c语言库函数丰富,使用库函数需要# 引入,如  #include<stdio.h>
10.c语言使用 //作为注释,注释不会参与到编译,多行注释  
11.c语言是强类型的编译语言,运行时需编译成二进制的可执行文件 
*/

多行注释是/*...*/

内存存储的方式

c语言的代码会编译成二进制文件,操作系统据可以使用这里二进制指令文件来控制计算机。

在数据存储时,对于计算机来说二进制是可执行的指令集合,计算机的存储器是用半导体集成电路构成的,包含众多脉冲电路,二极管元件,每个二极管元件相当于开关。所以每次都会用两种状态,对电平来说就是正电平和负电平,用1,0表示。如下图

请添加图片描述
于是由这些二进制组成的计算机指令控制者计算机的硬件。

在计算机中每一个二极管元件成为二进制位,是存储的最小单元,成为比特(bit),或者。在二进制中,规定每8位组成一组,称为字节(byte),即一个字节占8位有8个0,1,共有2^8中状态。

地址是计算机的存储单元,计算机存储器包含很多存储单元,操作系统将这些单元编号,统称为地址。

在程序设计中,不同类型的数据占据的存储空间不一样,因此语言对占用不同的数据类型进行了分类,成为数据类型

如c语言中一般存在如下数据类型:

在这里插入图片描述

语法定义法则

变量与常量

c语言中的变量是一个存储单元的地址,变量名代表该地址存储的数值,可以通过&获取变量的地址。

变量定义以数字,字母或下环线完成,数字不能做首位;变量必须先定义分配存储空间后再使用;

变量定义时可以不初始化可以初始化赋值,如下:

//定义实数
int a1 = 100;
int a2;
a2 = 200;

//定义浮点数(小数)
float a3 = 12.21
double a31 = 23.33333333333

//定义指数,指数必须为整数,用e,E表示10的阶乘 (%e)
int a4 = 2.3e10 


//定义字符
char a5 = "c"

//c语言没专门的字符串定义,使用字符数组定义
char a6[10] 

变量的格式化打印时%d,%s,%c,%f,%d,%e和打印的变量类型要对应。
%.2表示保留小数点几位。

字符常用用反引号包裹;c中提供了很多转义字符,用来格式化符号,如:

\t 表格分区
\b 退格
\r 回车
\f 换页
\0 字符串的结束标志
\\ 表示一个\

字符再c中是ASCII码中的数据,打印时通过格式化打印对应的数字或者字符,%c,%d。每一个小写字母比大写字母大32。

c语言再处理自字符时是用''包裹,而字符串是用""包裹,后者系统会在字符串后默认加\0作为结束符,因此字符串占用字符比实际多一个字符。

c语言中使用# define [常量名] [常量值]的方式定义常量。

#include<stdio.h>
#include<math.h>
//定义常量
#define PI 3.1415926 

int main(){
	float a = 12.43;
	printf("%f\n",a);	
	
	int b = 'c';
	printf("%c\n",b );
	
	char c = 'a';
	printf("%c\n",c);
	
	//数学计算库
	
	double r =3,d,e,f;
	
	//圆周率 
	d = 2*3.1415926*r; 
	//面积
	e = 2*3.1415926*r*r;
	
	// 体积
	f = (4.0/3.0)*(3.1415926*r*r*r);
	
	printf("c=%f,s=%f,v=%f\n",d,e,f); 
	
	
	//使用数学计算库pow方法 
	double v;
	v=(4.0/3.0)*(3.1415926*pow(r,3));
	printf("数学计算库:%f\n",v);
	
	//定义常量 计算
	double v1;
	v1 = 4.0/3.0*PI*pow(r,3);
	printf("常量计算:%f\n",v1); 
	
	return 0; 
} 

指针变量,是c语言提供直接操作内存地址的变量,通过类型 * 变量名来定义,表示该指针变量。通过&取出其地址。(指针章节详述)

输入输出函数是printf,scanf分别使用%d,%s,%c,%f,%d,%e作为输入和输出的格式控制符,另外scanf函数格式控制后的变量是变量地址,而不是变量名,且在输入是,格式控制符没有任何符号就默认用空格隔开,声明了相应符号输入时就要在对应位置输入相同的符号。

运算符注意事项

  1. 除法运算/时两个整数的相除结果仍然是整数,c自动去除小数部分。
  2. 运算符的优先级与数学规定的相同。
  3. 当运算时出现了flaot和double类型,所有的数据都会转为double,提高精度。
  4. c提供自增和自减运算,++i表示使用变量i前先加一,i++表示使用后变量加一。
#include<stdio.h>

int main(){
	int i = 3;
	printf("%d\n",++i);
	int j = 3;
	printf("%d\n",j++);
	return 0; 
}

在这里插入图片描述

自增和自减完成后都会重新赋给变量,另外自增与自减只用用变量来操作不能用数字,而且变量不能参杂任何表达式 (a+b) ++,这也是错误的。

  1. 在混合计算的过程中,字节少的数据会自动转化为字节多的数据。
  2. 在类型转换过程中字节少的数据会自动转化为字节多的数据,多字节转化为少字节的需要强制转换,(类型名) (表达式)强制转化会丢失精度。
  3. *用于定义指针变量,&取变量地址。

库与函数的使用

c语言提供了丰富的库函数,只要导入了相关库就可以使用库方法。在其他语言中也叫内置方法。在使用库函数时,要在库函数中使用预编译指令#include加载库。目的时将相关的 ”头文件“的内容引入到头文件中。#include也被称为头文件。

库的存在使代码不至于都写在一个文件中,将一部分代码写在库文件中,需要时引入头文件即可。引入头文件主要包含# include 文件目录和文件名,如

#inlcude <stdio.h>

这是c语言的提供的标准输入输出库,导入该头文件后可以使用:putchar,getchar,printf,scanf,puts,gets等库方法。

#include是预编译指令,为导入头文件的关键字,<stdio.h>是文件名,用<>包裹起来,表示编译器会在编译系统所在的子目录查找stdio.h文件。如

#include "C:\tmp\c_header_file\test.h"

除了c语言自带的标准库外,可以引入自定义的库文件用""代替<>。区别在于""会在用户当前目录下寻找库文件,而<>会在c编译环境目录下寻找库文件。

因此,C语言提供了两种文件命令方式.h.c。在C语言中,数据结构及函数的声明与实现进行分离有且仅有一种搭配方式:.h .c

.h 用于数据结构及函数的声明
.c 用于数据结构的初始化及函数的具体实现

在C++中,数据结构及函数的声明与实现进行分离的方式有两种:①.h、.cpp;②.hpp。具体可以参考游戏开发之.h、.c、.hpp及.cpp的区别

因此当程序仅仅是数据结构或者工具函数时,将函数保存为.h文件,通过头文件导入后就可以使用其数据结构和工具函数了。

自定义addSum头文件
在这里插入图片描述
在主类引入头文件,并调用函数
在这里插入图片描述
调用成功
在这里插入图片描述

malloc函数
malloc(长度)是动态开辟内存空间函数,表示向系统申请分配一个占参数长度字节的空间。

程序与算法

在程序中指定数据的类型和数据的组织形式就是数据结构(data structure)。算法是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。

算法可以用多种方式表示,自然语言,流程图,N-S图,伪代码等。程序中的基本结构包括,顺序结构,选择结构和循环结构。

选择结构

#include<stdio.h>

int main(){
	int tmp;
	scanf("%d",&tmp);
	if(tmp>60){
		printf("%s","及格了!");
	}else{
		printf("未及格!"); 
	}
	return 0;
}

//else总是与其上面一个if匹配

//如果有多个选择还可以用  else if (表达式) 继续判断

#include<stdio.h>

int main(){
	int tmp;
	scanf("%d",&tmp);
	if( tmp = 0 ||tmp<60){
		printf("%s","未及格!");
	}
	else if ( 60 <= tmp && tmp <80){
		printf("%s","良好!");
	} 
	else if (  80<= tmp){
		printf("%s","优秀!");
	}
	else printf("%s","未查到分数!");
	return 0;
}

switch语句

int main(){
	int tmp;
	scanf("%d",&tmp);
	switch(tmp){
		case 60 : printf("未及格!"); 
		case 80 :printf("%s","良好!");
		case 100 : printf("%s","优秀!");
		default :  printf("%s","未查到分数!");
	}
} 

switch括号的表达式只能时数值类型或者字符类型;当表达式的值与某个case的值相等时,就会执行其后的语句,如果没有匹配的就执行default;每个表达式的值必须互不相同。执行完一个case后,流程会转到下一个case继续继续执行,因此需要控制流程找到分支后退出,通过break语句·。

switch(tmp){
	case 60 : printf("未及格!"); break;
	case 80 :printf("%s","良好!"); break;
	case 100 : printf("%s","优秀!"); break;
	default :  printf("%s","未查到分数!");
}

循环结构
循环结构的条件:1、需要重复执行的操作(循环体);2、循环结束条件。

while 表达式 语句

#include<stdio.h>
int main(){
	int i,range;
	i = 0; range =100;
	while (i<range){
		i++;
		range--;
	}
	printf("i=%d,range=%d",i,range);
}
do
循环体语句

while(表达式)

循环体语句超过一个要用{}包裹,循环体中要有结束循环的条件。
第一个循环为先判断表达式后执行循环体;第二个为先执行循环体再判断表达式是否成立。相同点时表达式为真时均继续执行循环。

for循环

for(表达式1,表达式2,表达式3) 语句

for(循环变量赋初值;循环条件;循环增量) 语句

#include<stdio.h>
int main(){
	int sum = 0;
	int i =0;
	for(i = 0;i<=100;i++){
		int tmp = i;
		sum+=i;	
	}
	printf("%d",sum);
	return 0;
}

break用于结束循环,continue结束本次循环。
for循环的表达式都可以省略for(;;)

数组

数组是具有同一属性的数据集合。在c语言中,定义数组时,需要指定变量的类型,数组名称和数组中包含多少个元素。
定义数组的格式:

类型 数组名[常量表达式]

定义数组时,需要指定数组元素的个数,括号中表达式表示元素的个数,即数组的长度;数组的大小不依赖于运行程序的变量;数组的元素只能变遍历;通过数组的下标取出数组的元素。

数组也可以初始化定义,再初始化定义时如何长度和初始化长度一致

//冒泡算法

#include<stdio.h>

int main(){
	int a[10] = {10,9,8,7,6,5,4,3,2,1};
	int i,j,t;
	for(i=0;i<=9;i++){
		for(j = 0; j<9-i;j++){
			if(a[j]>a[j+1]){
				t = a[j];
				a[j] = a[j+1];
				a[j+1] =t;
			}
		}
	}
	
	int z;
	for(z=0;z<10;z++){
		printf("%d",a[z]);
	}
}

二维数组

二维数组中元素的排序顺序是按行存放的,即在内存中存放第一行的元素在存放第二行元素。

定义二维数组

数组名 [下标] [下标]

初始化定义
a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};   //完整赋值
a[1][2] = {1,2,3,4};     //顺序赋值
a[3][4] = {{1},{2},{3}}   //部分赋值

字符串

c语言中字符数组表示字符串,字符数组和一个字符串的区别在于字符串以\0结尾。这个字符串的结束符编译器会自动加上,无需认为处理。

字符数组也可以直接通过%s直接输出。

#include<stdio.h>
int main(){
	char a[10] = {'C','h','i','n','a'};
	int i;
	for(i=0;i<10;i++){
		printf("%c",a[i]);
	}
	
	printf("\n");
	
	printf("%s\n",a); 
	
	printf("%s","China");
}

在这里插入图片描述

字符数组逐个字符输出用%c,将整个字符串统一输出用格式符%s

对于数值型数组,未赋值部分元素默认值未0,对于字符数组未赋值元素默认值为空字符。

注意:

  1. 在使用字符串输入输出时,输出字符不包括\0
  2. %s输出字符串时,变量时数组名而不是元素名;
  3. 如果数组长度大于字符串实际长度,也只输出到\0
  4. scanf方法可以输入一个字符串,输入字符长度需小于字符数组长度,且输入项变量不用加地址符,因为数组名就表示数组起始地址。
  5. printf("%d",a)a为数组,表示输出数组的起始地址。

字符串函数

引入头文件#include<string.h>

函数名描述
gets(字符数组)终端输入一个字符串到字符数组
pust(字符数组)将一个字符串输出到终端
strcat(字符数组1,字符数组2)连接两个字符数组
strcpy(字符数组1,字符数组2)复制字符数组
strcmp(字符数组1,字符数组2)比较两个字符数组
strlwr(字符串)将字符数组换成小写字母
strupr(字符串)将字符数组换成大写字母

模块化程序设计

在程序设计中随着代码量的不断增加,单个文件的的结构已不满足需要,因此需要将代码分块,与主业务无关的代码分散到其他文件中,就是模块化编程。

在之前已经介绍了对于工具方法,或者与业务无关的方法,声明为.h的文件,通过#include "路径地址"引入到主文件中。

在c语言中基础模块时函数,则不同的模块就是将若干函数分模块。

#include<stdio.h>
#include<string.h>

void main(){
	//函数定义在调用之后,需要在调用之前声明函数 
	void showName(char a[15]);
	void conStr(char a[10],char b[10]); 
	printf("%s\n","Hello World");
	
	char str1[10] = {'i','a','m','h','e','l','l','o'};
	showName(str1);
	
	char str2 = {'m','e','e','t','i','n','g'};

	conStr(&str1,&str2);
}


//同文件函数的分块

void showName(char a[15]){
	printf("%s\n",a);
}


void conStr(char a[10],char b[10]){
	printf("%s%s\n",a,b);
}

一个c程序可以由一个或者多个程序模块组成,每一个模块作为一个源程序文件;对于较大的程序,不将内容完全放在一盒源程序文件中,而是放在多个若干文件中,由多个源程序文件组成一个完整的c程序。

C语言程序都是以main()函数开始执行,在mian函数调用其他函数完毕后再回到main函数。

所有函数都是平行的,函数的执行顺序取决于mian函数的调用顺序,函数不能嵌套定义但却可以嵌套调用。

函数可以分为三种:库函数,即系统自带函数,在编译环境中,通过#include<模块名>引入,之后就可以通过方法名引用。

函数

在c语言中,函数是程序的基本单位,函数必须先定义再调用,函数应包含:函数名,函数值类型,函数的参数名称和类型,函数需要完成什么任务。

函数定义

类型名 函数名(){
	函数体
}

函数值通过return关键字返回。

函数调用

函数名()无参数调用

函数名(实参列表)有参数的调用,顺序对应。

注意事项

  1. 函数定义在函数调用之后时,需要在调用前声明,声明即把除函数体的部分在使用前表示即可。(函数定义在调用之前可以不用声明)
  2. 定义函数的形参,在使用前并未分配内存,在调用函数时将实参传递给形参。
  3. 通过return关键字将函数体得到的值返回给函数,并在调用处使用。
  4. 调用结束后,形参内存单元被释放。
  5. c语言的实参传递数据的过程时值传递。

函数声明需要满足,(1)函数必须是以定义了的函数,(2)库函数为需要通过#include<模块名>引入,且库函数在预编译过程加载,无需声明。自定义函数需要映入加声明。

编译系统并不检查参数名,只检查参数类型,因此参数名并不强制,在声明函数是可以只声明函数类型即可。

函数类型 函数名(参数类型1 参数名1,参数类型2 参数名2 ....);
函数类型 函数名 (参数类型1,参数类型2);

函数定义可以函数声明区别在于函数定义是对函数类型,参数和功能的定义,是一个完整,独立的函数单位;而函数声明只是将定义的函数名,参数类型等定义时函数的一些信息(不包含函数体)通知编译器。

函数的递归调用

在定义一个函数时,直接或间接调用函数本身称为函数的递归调用。

例如,第3个孩子比第2个孩子大2岁,第2个孩子比第一个孩子大2岁,第一个孩子5岁,问第三个孩子几岁?该问题就是一个递归问题。

解决上述问题必须具备两个条件,递推的条件(大2岁)和结束条件(第一个孩子5岁),根据推理:

f ( 3 ) = f ( 2 ) + 2 f(3) = f(2) + 2 f(3)=f(2)+2

f ( 2 ) = f ( 1 ) + 2 f(2) = f(1) + 2 f(2)=f(1)+2
f ( 1 ) = 5 f(1) = 5 f(1)=5

#include<stdio.h>

void main(){
	int age(int x);
	int c;
	c = age(3);
	printf("%d",c);
}

int age(int x){
	if(x == 1){
		return 5;
	}else{
		return age(x-1) + 2;
	}

}

在这里插入图片描述
这里的思路是,首先对于任意变量,都取决于其前一个变量的值,在age函数中,结束条件是1,当不满足时直接返回前一个函数age(x-1)+2,以此地推,那么函数未到达结束条件时的返回值因该是:

age(x)+age(x-1)+2+age(x-2)+2....+age(1)

而有结束条件age(1) =5,那么xi-1=1的变量就可以计算出来了,之后其他函数一次调用直至到x变量。

使用递归函数处理问题的关键在于:1.子规模比原问题的规模小,且规模是有规律的;2.必须要有递归的结束条件。

数组作为参数
用数组元素作为实参可以向形参传递一个数组元素的值,有时需要在函数中处理整个数组元素,可以用数组名作为函数的实参。和Java等语言不同的是,C中数组名代表的是数组的首地址,所以在数组名做实参时只是将首地址传递了。

因此在数组名做函数参数时,需要另外开辟一个存放形参的空间,这是和用变量作为函数参数时不一样的,由于数组名代表数组的首地址,因此在传递时也只传递了数组的首地址,在形参内部需要存储首地址的数组变量,由于变量和形参具有同样的数组首地址,所以它们占用同一存储空间,即它们的值也是相等的。(数组名作为新参时编译器不会检测数组长度因此不必指定长度。具体长度由实参数组长度决定)

#include<stdio.h>
int main(){
	int c;
	int a[10]={1,2,3,4,5,6,7,8,9,10};
	c = sum(a);
	printf("%d",c);
}

int sum(int xxx[]){
	int sum,i;
	sum =0; 
	for(i=0;i<10;i++){
		sum = sum+xxx[i];
	}
	return sum;
}

在上述代码中,sum函数的形参为一个数组类型变量,调用时传递了a数组,数组做形参时并不分配内存空间,其具体的值由传入的实参决定,在参传递时,xxx接收了a数组的首地址,由于它们共用一个首地址则可以看作数组名的传递传递的数组的地址为引用传递。xxx和a指向同一数组地址。

数组名代表数组的首地址,在作为参数传递时,传递的时数组地址,为引用传递。

变量的作用域、存储方式和生命周期

定义在函数内部或者作为形参的时局部变量,用完即销毁,定义在主函数或者通过#define定义的变量为全局变量。

如果需要定义局部变量且在函数调用结束后不消失保留原值,的需要定义静态变量用关键字static

  • auto
    函数中的形参和内部的局部变量都是此类,函数在被调用时分配空间,调用结束释放空间,该局部变量也叫自动变量。关键字auto可以省略。

+static
static关键字声明变量为静态变量会自动保存上一次调用返回的值。

  • register
    register声明寄存器变量,对寄存器变量的取用速度远高于内存存取变量的速度,提高执行效率,该变量也叫寄存器变量

  • extern
    extern关键字声明外部变量的作用域,在一个文件内扩展外部变量作用域,将外部变量的作用域扩展到其他文件。

访问权限控制

extern函数声明外部函数,关键字可以省略,static声明内部和那护士只能在同一源文件使用。

指针变量

一个变量包含两个部分,变量地址和变量的值。c语言中将地址形象化称为指针,通过指针能快速找到以它为地址的内存单元。引入指针后处理直接访问变量的值外也可以通过地址间接访问变量的值。

指针是C语言的一种特殊变量,用于存放变量的地址。一个变量的地址称为该变量的指针,用来存放地址的变量称为指针变量。

指针的定义*

指针通过*定义,具体表达式为类型 * 指针变量;指针指向的是变量的地址,因此需要将变量的地址赋给指针变量。

int *p;
int a= 10;
p = &a;
printf("%d",p);

定义指针变量需要注意的是*表示该变量是一个指针变量,区别于其他基变量类型;定义指针时必须指定基类型,原因时不同类型的变量的地址所占的内存空间不一样;赋值给指针变量的是地址而不是数据即用&取地址,指针变量只能取地址。

定义指针变量时*表声明类型为指针变量,如 int * p;指针变量是p而不是*p。p = &a;

指针变量的引用

定义指针变量的关键字是*,除了在定义是起声明作用外,*在也可以引用指针变量作取值和赋值使用。

*在定义指针变量时起声明作用,在引用指针变量时起取指针变量指向内存地址的数据的作用。

&为取地址符号&a表示取a变量的地址;*为指针运算符,*p为指针变量p指向的对象的值。

#include<stdio.h>

int main(){
	
	int a = 10;
	int *p;
	p=&a;
	printf("变量的值%d\n",*p);
	printf("指针初始地址%d\n",p);
	*p = 100;
	printf("修改后的值%d\n",*p);
	printf("赋值后指针地址%d\n",p);
	
}

指针变量做函数参数

指针变量做函数参数是时是将变量的地址传输到另一个函数中,但是不能通过形参的指针指向改变实参的指针指向,只是错误的。

#include<stdio.h>

int main(){
	
	int a = 10;
	int *p;
	p=&a;
	printf("变量的值%d\n",*p);
	printf("指针初始地址%d\n",p);
	*p = 100;
	printf("修改后的值%d\n",*p);
	printf("赋值后指针地址%d\n",p);
	
	int a1 = 10;
	int a2 = 100;
	printf("交换前%d,%d\n",a1,a2);
	swap(&a1,&a2);
	printf("swap交换%d,%d\n",a1,a2); 
	swap1(&a1,&a2);
	printf("swap1交换%d,%d\n",a1,a2); 
	
	
	
}

int swap(int *p1,int *p2){
	int tmp;
	tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
} 


int swap1(int *p1,int *p2){
	int *tmp;
	tmp = p1;
	p1 = p2;
	p2 = tmp;
} 

在这里插入图片描述
在上述程序中,swap函数传递两个变量的地址,在函数体中通过*取出指针变量指向的值来交换数据,这个原理和下面的原理时一致的:

void swap3(int a,int b){
	int tmp;
	tmp =a;
	a = b;
	b = tmp;
}

但是swap2,企图修改指针指向的地址即通过交换两个指针变量的地址来间接改变变量的值,这是错误的,因为不能通过形参来改变实参的值。(c语言形参的传递都是值传递,对实参复制一份相同的数据分配空间)

指针引用数组

一个数组包含若干元素,在C语言中数组的地址即数组首个元素的地址,c语言底层实现了数组的查找,取值的操作,因此只需要记录数组的首地址即可,编辑器就可以通过特定算法计算其后元素的地址。

int array[] = {1,2,3,4,5};
printf("数组array的地址为%d\n",array);

printf("数组array[1]的地址为%d\n",&array[0]);

在这里插入图片描述

系统知道数组首地址后可以自动计算其余元素的地址,因此指针变量在操作数组时也只需要记录数组的首地址即可。

C语言中数组的指针就是数值首地址

int *p = &array[0];

printf("%d\n",p); 

由于p指针变量表示&array[0]*p = array[0],p = &array[0],于是得出结论,指针变量是数组首元素的地址,在数组中每个元素同时同一类型,因此占据的内存空间一致,那么p+1就是array[1]的地址,一次类推。

当使用指针表示数组是,指针变量表示数组的首地址,指针变量的累加表示地址块的累加,依次表示数组其他元素的地址。

指针运算

上述操作中,涉及到指针运算,指针运算并不是逻辑上的将地址的值加一,而是将数组元素所占的地址加一个字节,如一个int占4字节,指针加一指针所占内存空间就加4个字节。

指针表示字符数组

用字符数组存放一个字符串,然后用数组下标可以访问数组元素,也可以通过%s直接输出真个字符数组。

也可以同指针指向一个字符串,相对于字符数组更简单,通过指针指向字符串,又编译系统自动分配内存大小。

指针表示字符串时也是将字符串当作字符数组初始,由底层实现,只能初始化赋值。

//定义格式
char *str = "xiaoxu";

//错误定义
char *str;
str = "xiaoxu";

char *str;
*str = "xiaoxu";

下面两个定义都是错误的,字符串底层是用字符数组,指针只能表示一个字符变量的地址,因此下面两个定义都是错误的。底层实现的方式比一样,只能初始化定义。

指向函数的指针

指针也可以指向函数,称为函数指针。

指针在指向函数时需要声明数据类型 (*指针变量名) (参数列表),然后再将指针变量指向函数名。就可以实现指针对函数的引用了。

//定义函数
int max(int a,int b){
	return a+b;
}
//指针声明
int (*p) (int,int);
p = max;

//指针引用函数
int c;
c= (*p)(2,1);
printf("%d",c);

指针数组

如果数组的元素全是指针类型变量,则称为指针数组,指针数组的每一个元素都存放一个地址,相当于指针变量。

如果变量为数组指针,那么对元素的引用就是多重指针**p

结构体

结构体

c语言中提供的基类型有时后并不能满足需求,于是提供了用户自定义的数据类型结构体

结构体定义:

struct student{
	数据类型 变量;
	...
};

结构体由strcut关键字定义,其后紧跟结构体的自定义名称,成员变量为类型和变量名,基于基本类型。成员变量也可以是结构体变量。

结构体定义时也可以同时实例化,在定义结束的分号后追加变量名用,隔开即可。

//结构体定义
struct Student{
	char name[20];
	char sex;
	int age;
};

//结构体初始化定义
struct Student{
	char name[20];
	char sex;
	int age;
} Stu,Stu1;

struct Student{
	char name[20];
	char sex;
	int age;
} Stu={"xiaoxu",'m',21};

对结构体变量的引用方式为结构体变量.成员名。通过.可以获取结构体变量的各个成员的值,如果成员变量也是结构体,就需要一级一级查找。

结构体类型也可以通过声明的方式定义但是要加上struct关键字,除了声明式定义外,也可以初始化定义,就像上面代码一样。

#include<stdio.h>

//初始化结构体变量定义
struct Student{
	char name[20];
	char sex;
	int age;
} Stu={"xiaoxu",'m',21};


int main(){
	printf("%s",Stu);
	
	///声明式结构体变量定义
	struct Student stu2 = {"zhansan",'m',21} ;
	printf("%s",stu2.name);
}

结构体数组

结构体变量也可以作为数组的元素,数组元素全是结构体变量的数组称为结构体数组。

结构体数组的定义步骤:

//1、定义结构体
struct Goods{
	char name[20];
	float size;
};

//2、声明并初始化结构体数组(也可以先声明后初始化)
struct Goods god1[5] ={
	{
		"mianbao",
		0.12
	}
};

结构体指针

结构体指针就是将指针变量指向结构体的地址。

//定义结构体
struct Student{
	char name[20];
	char sex;
	int age;
};

//声明初始化结构体
struct Student stu3={
	"lisi",
	'm',
	21
}; 
//定义结构体指针变量
struct Student *p;
p = &stu3;
printf("%s\n",(*p).name);

c中.的优先级要高于*因此*p需要用括号括起来。

在结构体指针中,为了简化(*指针变量).结构体成员的书写,c语言提供了->符号表示取值符号用来代替*.简化编写。

(*p).name=p->name

所以指向结构体的指针变量也可以用通过->指向结构体成员,也可以指向结构体数组的元素。

结构体变量和结构体指针做参数

将结构体变量作为参数时,可以传递成员变量个数个数据,传递的过程为值传递,将结构体变量的内存空间的所有内容全部传递给形参。也可以使用结构体指针作为参数传递。

//结构体做参数
toString(struct Student stu){
	printf("Student{name=%s,sex=%c,age=%.2f}",stu.name,stu.sex,stu.age);
}

//结构体初始化 
struct Student{
	char name[20];
	char sex;
	int age;
} Stu={"xiaoxu",'m',21},stu100;

//声明结构体
struct Student stu3={
	"lisi",
	'm',
	21
}; 

//调用方法传递结构体变量
toString(stu3); 
//定义结构体指针

//声明结构体
struct Student stu3={
	"lisi",
	'm',
	21
}; 


//结构体指针做参数
pointerString(struct Student *stu){
	printf("Student{name=%s,sex=%c,age=%d}\n",stu->name,stu->sex,stu->age);
}

//定义指针变量
struct Student *p;
p = &stu3;
printf("%s\n",(*p).name);
pointerString(p);

枚举

当变量的种类确定,只有若干几个可能的值时,可以定义为,枚举,枚举就是将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。

枚举类型用enum关键字enum week(sum,mon,tue,wed,thu,fri,sat);内部的元素被称为枚举变量或枚举常量。

#include<stdio.h>

int main(){
	enum week{
		sum,mon,tue,wed,thu,fri,sat
	};
	
	enum week day = mon;
	
	switch(day){
		case sum:printf("sum");break;
		case mon:printf("mon");break;
		case tue:printf("tue");break;
		case wed:printf("wed");break;
		case thu:printf("thu");break;
		case fri:printf("fri");break;
		case sat:printf("sat");break;
		default : printf("null");
	}
}

文件操作

文件名C:\Users\xwh\Desktop\master\test.txt

文件类型ASCII二进制文件,ascii码用于存储字符型数据,文件后缀为txt。

文件指针

每个被使用的文件都在内存中开辟了文件信息区,用于存放文件相关信息,文件有关的类型和方法都在stdio.h的头文件中。

文件名类型为FILE类型,对文件读写需要先打开文件,读写完毕要关闭文件。打开关闭文件的方法是fopen(文件名,使用文件的方式)fclose(文件指针)表示关闭文件。

使用文件的方式描述
rASCII文件只读
wASCII文件只写
aASCII文件追加
rb二进制文件只读
wb二进制文件只写
ab二进制文件追加
r+ascii码文件读写

"r","w","a是最基本的三种方式,在其后面加”b"表示对二进制文件,不加为ASCII文件即txt文件。"+"表示即可读又可写。

一般不对FILE类型的变量命名,而是通过FILE指针来操作文件。

文件读写

对文件读写的函数如下:

函数描述
fgetc(文件指针)从该文件读取一个字符
fputc(文件指针)写一个字符到文件
fgets(str,n,fp)从文件fp中读取一个长度为(n-1)的字符串,存到字符数组str中
fputs(ch,fp)把字符写道文件fp中
fprintf(文件指针,格式化字符,输出列表)格式化输出(字符)
fscanf(文件指针,格式化字符串,输出列表)格式化写入(字符)
fread(buffer,size,count,fp)buffer是二进制地址,size是二进制字节数,count是要写多少个字节,fp文件指针
fwrite(buffer,size,count,fp)同上

读写文件时可以在文件的任何位置写入数据,也可以在任何读取数据。c提供了fseek函数标记文件的位置,就可光标一样的功能。

fseek(文件指针,位移量,起始点)

文件是外部介质上数据的集合,操作系统把所有输入输出数据作为文件来管理。每个文件都必须要又文件路径,文件主干和后缀。c语言中常用文件指针来操作文件。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xvwen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值