小工具      在线工具  汉语词典  dos游戏  css  js  c++  java

C程序设计语言基础

程序、数据结构与算法,c语言,开发语言 额外说明

收录于:112天前

机器语言与高级语言

计算机硬件只能识别电平信号,正或负,计算机上的各种按钮触发各种电平与计算机交互。随着操作系统的发展,人们用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. 运算时出现Flat、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>

函数名称 描述
获取(字符数组) 终端将字符串输入到字符数组中
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);

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

递归函数调用

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

例如,第三个孩子比第二个孩子大2岁,第二个孩子比第一个孩子大2岁,第一个孩子5岁。第三个孩子多大了?该问题是一个递归问题。

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

f (3) = f (2) + 2 f(3) = f(2) + 2F3=F2+2

f(2) = f(1) + 2 f(2) = f(1) + 2F2=F1+2
f(1) = 5 f(1) = 5F1=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函数的形参是一个数组类型变量。调用时,传递一个数组。当数组作为形参时,不分配内存空间。它的具体值由实际传入的参数决定。参数传递时,xxx收到的是a数组的首地址。由于它们共享相同的首地址,因此可以视为数组名称的传递。所传递数组的地址是通过引用传递的。 xxx和a指向同一个数组地址。

数组名代表数组的首地址。作为参数传递时,传递的是数组地址,并且是按引用传递。

变量范围、存储方式及生命周期

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

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

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

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

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

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

访问控制

extern函数声明一个外部函数,关键字可以省略。静态声明internal和nurse只能在同一个源文件中使用。

指针变量

一个变量包含两个部分,变量地址和变量的值。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(文件指针)表示关闭文件。

如何使用文件 描述
r ASCII 文件只读
w ASCII 文件只写
A ASCII 文件追加
RB 二进制文件只读
工作组 二进制文件是只写的
ab 二进制文件追加
r+ ascii文件读写

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

一般情况下,FILE类型的变量没有命名,而是通过FILE指针来操作文件。

文件读写

读写文件的函数如下:

功能 描述
fgetc(文件指针) 从此文件中读取一个字符
fputc(文件指针) 将一个字符写入文件
fgets(str,n,fp) 从文件fp中读取长度为(n-1)的字符串,存入字符数组str中
fputs(通道,fp) 将字符写入文件fp
fprintf(文件指针、格式字符、输出列表) 格式化输出(字符)
fscanf(文件指针、格式字符串、输出列表) 格式化书写(字符)
fread(缓冲区,大小,计数,fp) buffer是二进制地址,size是二进制字节数,count是要写入多少字节,fp文件指针
fwrite(缓冲区,大小,计数,fp) 与上面相同

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

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

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

在这里插入图片描述

. . .

相关推荐

额外说明

类型错误:序列项 0:预期的 str 实例,找到 int

在使用python 将列表转为字符串的时候,报这个错误。 TypeError: sequence item 0: expected str instance, int found 目录 一、问题复现 二、原因分析 三、解决办法 一、问题复现 #!/usr

额外说明

HBase存储和读取过程详解

简介 本文将全面解析HBase中数据的存储过程,以及数据的查询解析过程,帮你从底层了解HBase内部的工作原理和工作流程。 一.HBase数据存储过程解析 先来看一张HBase存储过程的全貌图,下文将分为两个部分来讲解HBase的存储过程。 客户端的请求

额外说明

python中如何方便地加载dataframe数据到gui界面(pyqt)中

背景需求 基本上Python中都是习惯用Dataframe处理数据,效率高,读写多样并且方便。但是拿到了正确的数据还需要展示到界面上,毕竟大多数应用都是需要交互界面的。那么如何方便地把df数据展示到gui界面呢? 解决方案 我这里用的是Pyqt做gui界

额外说明

Cisco交换机作业1.5 server

新增设备server0,并入服务器,实现全网联通 server0配置: vlan:152 IP:192.168.152.1 网关:192.168.152.254 一、三层交换机switch0设置 新增vlan152 vl dat vlan 152 配

额外说明

使用kubeadm快速部署一个K8s集群

kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具。 这个工具能通过两条指令完成一个kubernetes集群的部署: # 创建一个 Master 节点 $ kubeadm init # 将一个 Node 节点加入到当前集群中

额外说明

[Hibernate系列—] 1. 下载与试用Hibernate(MySQL与Oracle 配置)

Hibernate是什么? 对于学习Java的开发者来说,这个问题不应该是一个问题。 一句话: Hibernate 是针对Java环境的对象/关系映射的解决方案,或者说是第三方库。 它的作用就是省去了开发者花在Java类对应数据库表,Java数据对应到S

额外说明

springboot 实现权限管理(一)

一、背景 1、 为什么进行权限管理? 生活在形形色色的世界之中,我们各自扮演着各自的角色,拥有不同的权利和义务。映射在计算机系统之中,也一样需要 角色、权限 来进行对用户的分类,限制访问资源,保证资源地合理被使用,使人各司其职。 2、应用场景 假设 管理

额外说明

系统缺失mfcm100u.dll文件导致游戏软件无法启动怎么办?

其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个mfcm100u.dll文件

额外说明

Linux下常用scp,tail,grep命令详解

文章编辑于:Linux下常用命令 scp命令 用于在本地主机和远程主机之间复制文件或目录,支持加密传输。它可以通过ssh协议传输数据,因此传输过程是安全的。 注意:使用scp命令时,如果权限验证失败,可能需要检查本地主机与远程主机之间的ssh配置是否正确

额外说明

wordpress用户注册_如何在WordPress批准用户评论时通知用户

WordPress 用户注册 Comment moderation is the most effective way to keep discussions safe and 无垃圾邮件. However, when a user has left c

ads via 小工具