C语言学习(个人笔记)
C语言
01-计算机基本概念
什么是计算机
计算机特点
- 计算机是一种电器,有两种状态,**通电,断电 **
- 用0和1表示这两种状态
- 计算机能识别的所有指令也是由0和1组成的
- 计算机的储存和操作数据也是由0和1组成的
0和1更准确的应该是高电平和低电平
计算机程序
- 用计算机语言编写命令集合
计算机语言
- 机器语言
- 所有的代码里只有0和1,0表示不加电,1表示加电
- 优点:直接对硬件产生作用,程序的执行效率非常非常高
- 缺点:指令又多又难记,可读性差,无可移植性
10111000 00000001 00000000 00000101 00000001 00000000 //表示1+1
- 汇编语言
- 符号化的机器语言,用一个符号(英文单词,数字)来代表一条机器指令
- 优点:直接对硬件产生作用,程序的执行效率非常高,可读性好
- 缺点:符号又多又难记,无可移植性
MOV AX,1 ADD AX,1 //表示1+1
- 高级语言
- 非常接近自然语言的高级语言,语法和结构类似于普通英文
- 优点:简单、易用、易于理解、远离对硬件的直接操作、有可移植性
- 缺点:有些高级语言写出的程序效率不高
1+1 //表示1+1
什么是C语言
- C语言是一种用于和计算机交流的高级语言,它既具有高级语言的特点,又具有汇编语言的特点
- 非常接近自然语言
- 程序的执行效率非常高
- C语言是所有编程语言中的经典,很多高级语言都是由C语言衍生出来的
- 如C++、C#、Objective-C、Java、Go等等
- C语言是所有编程语言中的经典,很多著名的系统软件都是由C语言编写的
- 几乎所有的操作系统都是用C语言编写的
- 几乎所有的计算机底层软件都是用C语言编写的
- 几乎所有的编辑器都是C语言编写的
C语言历史
为什么要学习C语言
- 40多年经久不衰
- 了解操作系统、编译原理、数据结构与算法等知识的最佳语言
- 了解其他语言底层实现原理必备语言
- 基础语法与其他高级语言类似
02-C语言程序基础
C语言的定义与执行
C语言程序是由函数组成的
C语言程序启动时,系统会自动调用名称叫做main的函数
C语言规定了函数定义的固定格式
int main()
{return 0;
}
//main函数的其他写法
void main()
- C语言定义其他函数与定义main函数相同,但不能有重复的函数名
- 调用函数的格式:函数名称();
Hello World!
#include <stdio.h>//告诉系统去哪找到printf函数
int main()
{printf("Hello World!\n");return 0;
}
编写C语言程序的注意点
- 每条完整语句后面必须以分号结尾
- 所有符号必须是英文输入法输入
- C语言中必须有且只有一个main函数
03-数据
在C语言中,内存分为5个区,堆、栈、自由存储区、全局/静态存储区和常量存储区
数据分类
- 静态数据
- 永久性的数据,一般存储在硬盘中
- 存储的时长:计算机关闭再开启,数据依旧还在,只要不主动删除或者硬盘没坏,数据永远都在
文档、照片、视频
- 动态数据
- 在程序运行过程中,动态产生的临时数据,一般存储在内存中内存的存储空间都比较小
- 存储的时长: 计算机关闭之后,这些临时数据就会被清除
运行某个软件(程序)时,整个程序就会被加载到内存中
因为内存访问速度比硬盘快N倍,所以不会把所有的应用程序都加载到存储空间大的硬盘中
C语言数据结构
- 为方便数据的运算和操作,C语言对这些数据进行了分类
- C语言中有4大类数据类型:基本类型、构造类型、指针类型、空类型
//计算各数据类型占多少字节byte
#include <stdio.h>
void main()
{printf("%d\n",sizeof(int));return 0;
}
//输出结果为4(byte)
常量
- 整型常量
1;2;3;
- 实型常量(小数)
+单精度/双精度,默认情况下所有的实型常量都是双精度
3.14;6.66;//双精度
10.1 f;2.31 f;//单精度 356 f与356.是等价的
- 字符型常量
- 字符型常量的单引号中只能放一个字符
‘a’; ‘1’; ‘.’;
‘ab’; ‘12’; ‘曾’; //错误
‘\n’; ‘\t’; //正确,特殊情况(转移字符)
- 指数形式
2.1e5(等于2.1*10^5)
- 字符串型常量
“a”; “ab”; “12”; “曾”;
- 符号常量
- 用标识符来表示一个常量——符号常量
- 符号常量的定义
#define 标识符(一般用大写字母) 常量
- 好处:
- 含义清楚
- 能做到“一改全改
#include <stdio.h>
#define PRICE 30;
void main()
{int num,total;num=10;total=num*PRICE;printf("total=%d\n",total);return 0;
}
变量
内存中以二进制存储
一个字节BYTE=8位bit
sizef()计算字符类型的字节数
- 定义变量的格式:数据类型 变量名称;
int a;
float b;
double c;
char d;
- 为什么要指定数据类型?
- 为了告诉操作系统,定义的变量对应的存储空间中,将来能够存储什么样的数据(开辟存储空间的大小)
- 整型数据的溢出
#include<stdio.h>
void main()
{short int a,b;a=32767;b=a+1;printf(%d,%d\n",a,b);
}
输出结果:a=32767,b=-32767
出现了数据溢出
- 舍入误差
#include<stdio.h>
void main()
{float a,b;a=123456.789e5;//属于double的范围b=a+20;printf("%f\n",a);printf("%f\n",b);
}
输出结果:12345678848.0000
12345678848.0000
字符型数据常用的转义字符及其含义
ASCII码表
#include<stdio.h>
void main()
{char a,b;a=120;b=121;printf("%c,%c\n",a,b);printf("%d,%d\n",a,b);
}
输出结果:x,y
120,121
算数运算符和算数表达式
- 运算符的优先级
- 运算符的结合性
- 自增自减运算符
- ++i:i自增1后再参与其他运算
- –i:i自减1后再参与其他运算
- i++:i参与运算后再自增1
- i–:i参与运算后再自减1
#include<stdio.h>
void main()
{int i=8;printf("%d\n",++i);printf("%d\n",--i);printf("%d\n",i++);printf("%d\n",i--);
}
输出结果:9 8 8 9
赋值运算符和赋值表达式
赋值运算符(自右向左)具有右结合性
变量=表达式类型转换
实型赋予整型,舍去小数部分
逗号运算符和逗号表达式
#include<stdio.h>
void main()
{
int a=2,b=4,c=6,x,y;
y=(x=a+b),(b+c);
printf("y=%d,x=%d",x,y);
}
输出结果:y=10,x=6
04-顺序结构
putchar函数(字符输出函数)
头文件:#include<stdio.h>
在显示器上输出单个字符
putchar(‘A’);
putchar(‘\101’);(也是输出A)
putchar(‘\n’);(输出换行)
getchar函数(键盘输入函数)
getchar();
char c;
c=getchar();
scanf函数(格式输入函数)
按用户指定的格式从键盘上把数据输入到指定的变量中
scanf(“格式控制字符串”,地址列表);地址列表:&a,&b
scanf()函数的返回值:如果两个数被读入,返回值为2;如果只有一个数被读入,返回值为1;0个数就是0;若遇到错误或遇到end of file,返回值为EOF(在C语言中是-1)
格式输入与输出
05-分支结构
关系运算符和表达式
比较两个量的运算符称为关系运算符
< | 小于 |
---|---|
<= | 小于或等于 |
> | 大于 |
>= | 大于或等于 |
== | 等于 |
!= | 不等于 |
逻辑运算符和表达式
- && 与运算
- || 或运算
- ! 非运算
&&和||为双目运算符具有左结合性,!为单目运算符具有右结合性
优先级关系:!>&&>||
if 语句的三种形式
- if(表达式)语句
如果表达式的值为真,则执行其后的语句,否则不执行该语句
#include<stdio.h> //比较两个数的大小
void main()
{int a,b,max;printf("\n input two numbers: ");scanf("%d%d",&a,&b);max=a;if(max<b)max=b;printf("max=%d",max);
}
- if-else (表达式)语句
if(表达式)
语句1;
else
语句2;
#include<stdio.h>
void main()
{int a,b,max;scanf("%d%d",&a,&b);if(a>b)printf("max=%d",a);elseprintf("max=%d",b);
}
- if-else-if形式
前两种形式一般适用于两个分支的情况,当有多个分支选择时,可采用if-else-if形式
if(表达式1)
语句1;
else if(表达式2)
语句2;
……
else
语句n;
#include<stdio.h> //输入三个数a b c,要求按从小到大的顺序输出
void main()
{int a,b,c,d;scanf("%d %d %d",&a,&b,&c);if(b>a){d=b;b=a;a=d;}else if(c>a){d=c;c=a;a=d;}else if(c>b){d=c;c=b;b=d;}printf("%d,%d,%d",a,b,c);
}
条件运算符和条件表达式
- 条件运算符为"?和:",三目运算符(有三个参与运算的量)
表达式1?表达式2:表达式3
条件语句:
if(a>b)
max=a;
else
max=b;
条件表达式:
max=(a>b)?a:b
如a>b为真,则把a赋予max,否则把b赋予max
switch语句
switch(表达式)
{
case常量表达式1:语句1;break;
case常量表达式2:语句2;break;
……
case常量表达式n:语句n;break;
default :语句n+1;
}
若没有break,所有语句都将实行
在使用switch语句应注意:
- 在case后的各常量表达式的值不能相同
- 在case后,允许有多个语句,可以不用{}括起来
- 各case和default子句的先后顺序可以变动,而不会影响程序执行结果
- default子句可以省略
#include<stdio.h> //输入三个整数,输出最大数和最小数
void main()
{int a,b,c;scanf("%d,%d,%d",&a,&b,&c);switch(){case 1(a>b>c){printf("max=%d,min=%d",a,c);break;}case 2(b>c>a){printf("max=%d,min=%d",b,a);break;}case 3(a>c>b){printf("max=%d,min=%d",a,c);break;}case 4(b>a>c){printf("max=%d,min=%d",b,c);break;}case 5(c>a>b){printf("max=%d,min=%d",c,b);break;}case 6(c>b>a){printf("max=%d,min=%d",c,a);break;}}
}
06-循环控制结构
循环结构是程序中一种很重要的结构,在给定条件成立时,反复执行某程序段,直到条件不成立为止。
goto语句以及用goto语句构成循环
goto语句是一种无条件转移语句
goto 语句标号:
goto语句通常不用,主要是因为它将使程序层次不清,且不易读,但在多层嵌套退出时,用goto语句则比较合理
#include<stdio.h> //用goto语句和if语句构成循环来计算1+2+……+100的值
void main()
{int i,sum=0;i=1;loop:if(i<=100){sum+=i;i++;goto loop;}printf("%d\n",sum);
}
while语句
while(表达式)语句
表达式是循环条件,语句为循环体
如果表达式的值一开始就为0,则语句一次也不会被执行
#include<stdio.h> //while语句构成循环来计算1+2+……+100的值
void main()
{int i=1,sum=0;while(i<=100){sum+=i;i++;}printf("%d",sum);
}
#include<stdio.h> //统计从键盘输入一行字符的个数
void main()
{int n=0;while(getchar()!='\n'){n++;}printf("%d",n);
}
do-while语句
do
语句
while(表达式);
与while的区别在于:它先执行循环中的语句,再判断表达式是否为真,为真继续循环,若假则终止循环。
do-while至少执行一次循环语句
for语句
for(表达式1[循环变量赋初值];表达式2[循环条件];表达式3[循环变量增量])语句;
- 先求解表达式1
- 求解表达式2,若其值为真,则执行for语句中指定的内嵌语句,然后执行第3步;若为假,结束循环转到第5步
- 求解表达式3
- 转回上面第2步,继续执行
- 循环结束,执行for语句下面的程序
eg:
for(i=1;i<=100;i++)
{sum+=i;
}
注意
表达式1/2/3都可以省略,但==;==不能省略
省略表达式1,表示不对循环控制变量赋初值
省略表达式2,则不做其他处理时该循环变成死循环
省略表达式3,则不对循环控制变量进行操作,可在语句中加入修改循环控制变量的语句
3个表达式都可以省略
for(; ; ;)
相当于while(1)语句
循环的嵌套
#include<stdio.h>
void main()
{int i,j;for(j=1;j<=6;j++){printf("\n");for(i=1;i<=j;i++){printf("*");}}
}
break和continue语句
break语句:
break语句用来从循环体中跳出循环体,提前结束循环
break只能用于switch语句和循环语句
在多层循环中,一个break语句只向外跳一层
continue语句
作为结束本次循环,跳过循环体中下面尚未执行的语句,接着进行下一次是否执行循环的判断
区别:
continue只结束本次循环,而不是终止整个循环的执行
break语句是结束整个循环过程,不再判断循环的条件是否成立
#include<stdio.h> //显示输入字符,如果按Esc,则退出循环;如果按Enter键,则不做任何处理
#include<conio.h> //后边getch函数和putch函数需要
void main()
{char ch;for(;;){ch=getch(); //字符输入函数if(ch==27) //Esc键的ACSII码为27break;if(ch==13) //Enter键的ACSII码为13continue;putch(ch); //显示输入字符}getch(); //让程序停一下,按任意键继续
}
07-数组
- 数组的定义
- 具有相同类型的数据组成的序列,是有序集合
- 数组的数据特点
- 具有相同的数据类型
- 使用过程中需要保留原始数据
- 数组分类
- 数值数组、字符数组、指针数组、结构数组
一维数组
定义方式
- 类型说明符 数组名 [常量表达式]
int a[10]; //定义了一个整型数组,数组名为a,此数组有10个元素,10个元素都是整型变量
int a,b,c=2,d,k1[10],k2[20]; //同一数据类型,可直接定义多个数组和多个变量
- a[10],表示a数组有10个元素,注意下标是从0开始的,a[0],a[1],a[2],···a[9],不存在数组元素a[10]
- C语言不允许对数组的大小进行动态定义
以下是不允许的
int n;
scanf("%d",&n); //在程序中临时输入数组的大小
int a[n];
引用方式
- 数组名 [下标]
a[0]=a[5]+a[7]-a[2*3];a[i+j];a[i++];
例:
- 对10个数组元素依次赋值为0,1,2,3,4,5,6,7,8,9,要求按逆序输出。
#include <stdio.h>
int main()
{int i,a[10];for(i=0;i<=9;i++) //给数组a[10]中每个元素赋值{a[i]=i;}for(i=9;i>=0;i--){printf("%d",a[i]); //倒序输出数组}
}
输出结果:9 8 7 6 5 4 3 2 1 0
- 向数组输入10个整数,找出其中的最大数,将其保存在最后一个元素中,并输出该数组。
#include <stdio.h>
#define N 10
int main()
{int temp,i,a[N];for(i=0;i<N;i++){scanf("%d",&a[i]);}for(i=0;i<N-1;i++){if(a[i]>a[i+1]){temp=a[i];a[i]=a[i+1];a[i+1]=temp;}}for(i=0;i<N;i++){printf("%d",a[i]);}return 0;
}
一维数组的初始化
在定义数组的同时,给元素赋值
int a[10]={0,1,2,3,4,5,6,7,8,9};
int a[10]={0,1,2,3,4};
- int a[10]={0,1,2,3,4,0,0,0,0,0};
int a[10]={0,0,0,0,0,0,0,0,0,0};
- int a[10]={0};
int a[5]={1,2,3,4,5};
- int a[ ]={1,2,3,4,5};
例:
有10个数,要求对它们按由小到大的顺序排列。(起泡法:大数沉淀,小数气泡)
#include <stdio.h>
#define N 10
int main()
{int a[N],temp,i;}
二维数组
二维数组的定义
- 类型说明符 数组名[常量表达式] [常量表达式]
float a [3] [4],b [5] [10]; //定义a为3*4的数组,b为5*10的数组
可以把二维数组看成是一种特殊的一维数组,如a有三个元素a[0],a[1],a[2],每个元素又是一个包含4个元素的一维数组
在实际硬件储存器中是连续编址的,就是储存器单元是按一维线性排列的
衍生:多维数组的定义
定义三维数组:float a[2] [3] [4];
多维数组元素在内存中的排列顺序:第一维的下标变化最慢,最右边的下标变化最快
二维数组的引用和初始化
四种方法
- 直接分行给二维数组赋初值:int a [3] [4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
- int a [3] [4]={1,2,3,4,5,6,7,8,9,10,11,12};
- 可以对部分元素赋初值:int a [3] [4]={{1},{5},{9}};//这样初始化的元素是每一行的第一个
或者 int a [3] [4]={{1},{0,6},{0,0,11}};
- 如果对全部元素都赋初值,则定义数组时对第一维的长度可以不指定,但第二维的不可省int a [] [4]={1,2,3,4,5,6,7,8,9,10,11,12};
例题
学习小组有5个人,有三门课的考试成绩,求全组分科的平均成绩和总平均成绩
张 | 王 | 李 | 赵 | 周 | |
---|---|---|---|---|---|
Math | 80 | 61 | 59 | 85 | 76 |
C | 75 | 65 | 63 | 87 | 77 |
English | 92 | 71 | 70 | 90 | 85 |
#include<stdio.h>
void main()
{int a[5][3]={{80,75,92},{61,65,71},{59,63,70},{85,87,90},{76,77,85}};int i,j,s=0,average,v[3];for(i=0;i<3;i++) //外循环递增慢{for(j=0;j<5;j++) //内循环递增快{s+=a[j][i];}v[i]=s/5;s=0;}average=(v[1]+v[2]+v[3])/3;printf("Math:%d\nC:%d\nEnglish:%d\n",v[1],v[2],v[3]);printf("average:%d\n",average);
}
二分法
利用数组进行数据查找——二分法(折半查找法)
在一批有序数据中查找某数
选定这批数据中居于中间位置的一个数与所查找数比较,看是否为所查找数,若不是,利用数据的有序性,可以决定所查找的数是在选定的数之前还是之后——缩小一半查找范围
#include<stdio.h>
#define M 10 //宏定义
viod main()
{static int a[M]={-12,0,6,16,23,56,80,100,110,115};int n,low,mid,high,found;low=0;high=M-1;found=0;printf("Input a number to be searched:");scanf("%d",&n);//以上程序为预处理while(low<high){mid=(low+high)/2;if(n==a[mid]){found=1;break;//找到该数,结束循环}else if(n<a[mid]){low=mid+1;}elsehigh=mid-1;}if(found=1){printf("The index of %d is %d",n,mid);}else{printf("There is not %d",n);}
}
08-函数
一个较大的程序可以分为若干个程序模块,每个模块用来实现一个特定的功能
函数的调用关系
由主函数调用其他函数,其他函数也可以互相调用,同一个函数可以被一个或多个函数调用多次
eg:
#include<stdio.h>
void main()
{void printstar(); //函数声明void print_message();printstar(); //调用函数print_message();printstar(); //同一个函数可以调用多次
}
void printstar() //定义函数
{printf("**************\n");
}
void print_message()
{printf("Hello world!\n");
}
从用户使用的角度看,函数有两种:①标准函数(库函数)由系统提供,不用自己定义(如printf()等)②用户自己定义的函数,用以解决用户的专门需要
从函数形式看,分为两类:①无参数函数(如printstar())主函数不向被调用函数传递数据,一般用来执行指定的一组操作。②有参数函数主调函数在调用被调用函数时,通过参数向被调用函数传递数据,一般情况下执行被调用函数时会得到一个函数值,供主函数使用
函数的一般定义形式
定义无参数函数的一般形式:
类型标识符 函数名()
{
声明部分
语句部分
}
定义有参数函数的一般形式:
类型标识符 函数名(形式参数表列)
{
声明部分
语句部分
}
int max(int x,int y);
{
int z; //函数体中的声明部分
z=x>y?x:y;
return z;
}
定义空函数的一般形式:
类型标识符 函数名()
{ }
int dummy()
{}
该函数没有任何实际作用,在主函数中写上一个空函数,表明“这里要调用一个函数”,等以后扩充函数功能时补充上
调用函数时的数据传递
#include<stdio.h>
void main()
{int max(int x,int y);//形参int a,b,c;scanf("%d,%d",&a,&b,);c=max(a,b);//实参printf("Max is %d",c);
}
int max(int x,int y);
{int z;z=X>y?x:y;return z;
}
函数的返回值
希望通过函数调用使主调函数能得到一个确定的值,这就是函数的返回值
- 函数的返回值是通过函数中return语句获得的:
return语句将被调用函数中的一个确定的值带回主调函数中去;一个函数可以有一个或以上的return语句,执行到哪个语句,哪个语句起作用;return后面还可以是表达式
- 函数的返回值应属于某个确定的类型
- 在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致,若不一致,则以函数类型为准
- 不带回值的函数,应当用void(在这种函数体内不能出现return语句)
函数的调用
函数名(实参表列)
多个实参,用逗号隔开,此时对实参求值的顺序并不确定,有自左向右也有自右向左
#include<stdio.h>
void main()//stdcall(自右向左)
{int f(int a,int b);int i=2,p;p=f(i,++i);printf("%d\n",p);
}
int f(int a,int b)
{int c;if(a>b){c=1;}else if(a==b){c=0;}else c=-1;return c;
}
根据输出结果来判断函数调用实参的顺序,输出结果为0,则为自右向左调用;输出结果为-1,则为自左向右
按函数在程序中出现的位置来分,有三种函数调用方式
- 函数语句把函数调用作为一个语句,如printstar()不需要函数带回值,只要函数完成 一个操作
- 函数表达式在函数内实现加减乘除的操作最后输出返回值,如比较大小,输出最大值的函数
- 函数参数函数调用作为一个函数的实参,如m=max(a,max(b,c));
函数的嵌套调用
定义一个函数时,其函数体内又包含另一个完整的函数定义但C语言不能嵌套定义函数,只可以嵌套调用函数
main()
{a();
}
a()
{b();
}
b()
{return;
}
#include<stdio.h> //计算s=1^2!+2^2!+3^2!
long square(int x) //计算平方的函数
{int a;a=x*x;return a;
}
long factorial(int y) //计算阶乘的函数
{int b=1,i;for(i=1;i<=y;i++){b=b*i;}return b;
}
void main()
{long square(int x);long factorial(int y);int m=1,s=0;for(m=1;m<=3;m++){s=s+factorial(square(m));}printf("s=1^2!+2^2!+3^2!=%d",s);
}
递归
在调用一个函数的过程中又出现直接或间接地调用该函数本身,成为函数的递归调用
int f(int x)
{int y,z;z=f(y);return (2*z);
}
递归必须要有一个退出的条件
例题:汉诺塔问题
有个老和尚想把64个盘子从A移到C,但每次只能移动一个盘子,且在移动过程中在ABC都保持大盘在下小盘在上,要求编程序打印出移动的步骤
移动3个盘子的步骤为:A->C,A->B,C->B,A->C,B->A,B->C,A->C
将n个盘子从A移到C可以分解为:
- 将A上n-1个盘子借助C先移到B上
- 把A剩下的一个盘子移到C
- 将n-1个盘子从B借助A移到C上
#include<stdio.h>
void hanoi(int n,char A,char B,char C)
{void move(char x,char y);if(n==1){move(A,C);}else{hanoi(n-1,A,C,B);move(A,C);hanoi(n-1,B,A,C);}
}
void move(char x,char y)
{printf("%c-->%c\n",x,y)
}
void main()
{void hanoi(int n,char A,char B,char C);int m;printf("input the number of the plates:");scanf("%d\n",&m);printf("the staps to move plates from A to B is")hanoi(m,'A','B','C');
}
数组作为函数参数
数组可以作为函数的参数使用,进行数据传送。
①把数组元素作为实参使用
②把数组名作为函数的形参和实参使用,不是进行值传递,而是址传递
#include<stdio.h>//数组元素作为实参的例子
void main()
{int a[10]={1,2,3,4,-1,-2,-3,-4,2,3};int i;for(i=0;i<10;i++){test(a[i]);}printf("\n");
}
void test(int x)
{if(x>0){printf("%d",x);}else{printf("%d",0);}
}
数据的传送
数组名就是数组的首地址,在数组名作为函数参数时所进行的传送只是地址的传送,也就是说把实参数组的首地址赋予形参数组名
形参数组名取得首地址之后,也就等于有了实在的数组,实际上是形参数组和实参数组为同一个数组,共同拥有同一段内存空间
#include<stdio.h> //数组名作函数参数的例子
void test(int b[10]);
void main()
{int a[10]={2,4,6,8,10,12,14,16,18,20};test(a);putchar('\n');
}
void test(int b[10])
{int i=0;for(;i<10;i++){printf("%d",b[i]);}
}
局部变量和全局变量
局部变量
在一个函数内部定义的变量是内部变量,它只在本函数范围内有效,在函数外不可使用该变量
- 主函数中定义的变量也只能在主函数中使用,主函数不可以使用其他函数定义的变量
- 不同的函数可以使用名称相同的变量,彼此互不干扰
- 形参也是局部变量
- 在一些函数内部,可以在复合语句中定义变量,这些变量只在复合语句中有效(for(int i;i<10;i++))也可称为分程序块和程序块
全局变量
在函数外定义的变量是全局变量(全程变量),可以被本文件中的函数所共用,它的有效范围是从定义变量的位置开始到本源文件结束
建议不必要时不要使用全局变量
- 全局变量在程序的全部执行过程中都占用存储单元,而不是在需要时开辟单元
- 使用全局变量过多,会降低程序清晰度
- 使函数的通用性,移植性降低了
变量的储存类别
从变量值存在的时间角度来分,可以分为静态存储方式和动态存储方式
静态存储方式在程序运行开始时由系统分配固定的存储空间的方式
动态存储方式在程序运行期间根据需要进行动态的分配存储空间的方式
每个变量和函数有两个属性:数据类型和存储类别
包括:自动的(auto),静态的(static),寄存器的(register),外部的(extern)
- register变量
一般情况下,变量(包括静态存储方式和动态存储方式)的值是存放在内存中的。
为提高执行效率,C语言允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。
- extern声明外部变量
外部变量即全局变量,有时需要用extern来声明外部变量,以扩展外部变量的作用域
09-指针
地址的概念
内存的每一个字节有一个编号,这就是“地址”。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元
在C语言中,对变量的访问有两种方式:一种是直接访问,一种间接访问
直接访问:a=5;
间接访问:scanf(“%d”,&a); 先把地址保存到一个单元中,在把从键盘接收到的数据储存的地址保存到a变量中
指针的概念
在C语言中,指针是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变量int i_pointer用来存放整型变量i的地址可以通过语句:i_pointer=&i;
*:叫做取值操作符
&:叫做取址操作符
知道了一个变量的地址,就可以通过这个地址来访问这个变量,因此,又把这个变量的地址称为该变量的指针
C语言中可以定义一类特殊的变量,这些变量专门用来存放变量的地址,指针变量
指针变量中存放的值是地址,这个地址就是指针
定义一个指针变量“*”
float *pointer_3;
char *pointer_4;
可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向一个变量
若pointer_1=pointer_2;则在pointer_1中存放的就不是&i而是&j
指针变量的引用“&”
指针变量中只能存放地址(指针)不要将一个整数(或其他非地址类型的数据)赋值给一个指针变量,否则编译器也会把该值当成一个地址来处理
进一步说明
如果已执行pointer_1=&a
- &*pointer_1的含义
&和*的优先级相同,但按照自右向左结合,先进行 *pointer_1的运算,就是变量a,再执行&运算,因此&*pointer_1与&a相同,即变量a的地址
- *&a的含义
先进行&a的运算,得到a的地址,再进行*运算,即&a所指向 的变量,也就是变量a, &a和pointer_1的作用是一样的
- (*pointer_1)++相当于a++,括号不可去掉
#include<stdio.h> //输入a和b两个整数,按先大后小的顺序输出a和b
void main()
{int *p1,*p2,*p,a,b;scanf("%d %d",&a,&b);p1=&a;p2=&b;if(a<b){p=p1;p1=p2;p2=p;}printf("a=%d,b=%d\n",a,b); //ab的值不变,p1,p2指向的值不同printf("max=%d,min=%d\n",*p1,*p2);
}
数组与指针
一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址
指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)
数组元素的指针就是数组元素的地址
p=&a[0]; 把a[0]元素的地址赋给指针变量p,也就是使p指向a数组的第0号元素
下标法,a[i]的形式
指针法,*a(a+i) 或 *(p+i)
数组名即为第一个元素的地址
eg.输出数组中的全部元素
假设有一个a数组,整型,有10个元素。要输出各元素的值有三种方法
(1)下标法
#include<stdio.h>
void main()
{int a[10];int i;for(i=0;i<10;i++){scanf("%d",&a[i]);}printf("\n");for(i=0;i<10;i++){printf("%d",&a[i]);}
}
(2)通过数组名计算数组元素的地址,找出元素的值
#include<stdio.h>
void main()
{int a[10];int i;for(i=0;i<10;i++){scanf("%d",&a[i]);}printf("\n");for(i=0;i<10;i++){printf("%d",*(a+i));}
}
(3)用指针变量指向数组元素
#include<stdio.h>
void main()
{int a[10];int i;int *p;for(i=0;i<10;i++){scanf("%d",&a[i]);}printf("\n");for(p=a;p<(a+10);p++){printf("%d",*p);}
}
多维数组与指针
多维数组的指针比一维数组的指针要复杂一些
内存中数据存放
表示形式 | 含义 | 地址 |
---|---|---|
a | 二维数组名,指向一维数组a[0],即0行首地址 | 2000 |
a[0],*(a+0), *a | 0行0列元素地址 | 2000 |
a+1,&a[1] | 1行首地址 | 2016 |
a[1],*(a+1) | 1行0列元素a[1] [0]的地址 | 2016 |
A[1]+2,*(a+1)+2,&a[1] [2] | 1行2列元素a[1] [2]的地址 | 2024 |
*(a[1]+2), *( *(a+1)+2),a[1] [2] | 1行2列元素a[1] [2]的值 | 元素值为13 |
指向多维数组元素的指针变量
把二维数组a分解为一维数组a[0],a[1],a[2]之后,设p为指向二维数组的指针变量。可定义为:*int( p)[4]
它表示p是一个指针变量,它指向包含4个元素的一维数组。若指向第一个一维数组a[0],其值等于a,a[0],或&a[0] [0]等。
而p+i则指向一维数组a[i]
*(p+i)+j是二维数组i行j列的元素地址,而==*( *(p+i)+j)==则是i行j列元素的值
类型说明符 (*指针变量名)[长度]
例题:
通过输入指定行数和列数打印出二维数组对应任一行任一列的值
#include<stdio.h>
void main()
{int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};int (*p)[4],i,j;p=a;printf("i=");scanf("%d",&i);while(i>2||i<0){printf("i=");scanf("%d",&i);}printf("j=");scanf("%d",&j);while(j>3||j<0){printf("j=");scanf("%d",&j);}printf("a[%d][%d]=%d",i,j,*(*(p+1)+j));
}
字符串与指针
字符串的储存方式
用字符指针指向一个字符串
char *string=‘I love u’
字符串中字符的存取方法
可以用下标法,也可以用指针的方法
下标法举例
将字符串a复制为字符串b
#include<stdio.h>
void main()
{char a[]="I love u!",b[40];int i;for(i=0;*(a+i)!='\0';i++){*(b+i)=*(a+1)}*(b+i)='\0';printf("String a is:%s\n",a);printf("String b is:");for(i=0;b[i]!='\0';i++){printf("%c",b[i]);}
}
指针法举例
将字符串a复制为字符串b
#include<stdio.h>
void main()
{char a[]="I love u!",b[40],*p1,*p2;int i;p1=a;p2=b;for(;*p1!='\0';p1++,p2++){*p2=*p1;}*p2='\0';printf("String a is :%s",a);printf("String b is:");for(i=0;b[i]!='\0';i++){printf("%c",b[i]);}
}
字符指针作函数参数
对使用字符指针变量和字符数组的讨论
字符数组和字符指针变量都能实现字符串的存储和运算,但二者之间是有区别的
- 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第一个字符的地址),绝不是将字符串放到字符指针变量中。
10-预处理
11-结构体与共用体
12-文件操作
13-位运算
对应任一行任一列的值
#include<stdio.h>
void main()
{int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};int (*p)[4],i,j;p=a;printf("i=");scanf("%d",&i);while(i>2||i<0){printf("i=");scanf("%d",&i);}printf("j=");scanf("%d",&j);while(j>3||j<0){printf("j=");scanf("%d",&j);}printf("a[%d][%d]=%d",i,j,*(*(p+1)+j));
}
字符串与指针
字符串的储存方式
用字符指针指向一个字符串
char *string=‘I love u’
字符串中字符的存取方法
可以用下标法,也可以用指针的方法
下标法举例
将字符串a复制为字符串b
#include<stdio.h>
void main()
{char a[]="I love u!",b[40];int i;for(i=0;*(a+i)!='\0';i++){*(b+i)=*(a+1)}*(b+i)='\0';printf("String a is:%s\n",a);printf("String b is:");for(i=0;b[i]!='\0';i++){printf("%c",b[i]);}
}
指针法举例
将字符串a复制为字符串b
#include<stdio.h>
void main()
{char a[]="I love u!",b[40],*p1,*p2;int i;p1=a;p2=b;for(;*p1!='\0';p1++,p2++){*p2=*p1;}*p2='\0';printf("String a is :%s",a);printf("String b is:");for(i=0;b[i]!='\0';i++){printf("%c",b[i]);}
}
字符指针作函数参数
对使用字符指针变量和字符数组的讨论
字符数组和字符指针变量都能实现字符串的存储和运算,但二者之间是有区别的
- 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第一个字符的地址),绝不是将字符串放到字符指针变量中。
10-预处理
11-结构体与共用体
12-文件操作
13-位运算
C语言学习(个人笔记)相关推荐
- 【c语言学习课堂笔记第三天】:int函数有符号整型和无符号整型
int 函数 有符号整型: 概念:截图 sizeof函数可查看字节数量 声明变量技巧: 随堂笔记: #include <stdio.h> #include <stdlib.h> ...
- Unity引擎中的C#语言学习的笔记(1)
一,认识.Net框架 .Net框架由微软开发,一个致力于敏捷软件开发.快速应用开发.平台无关性和透明化的软件平台.可以跨平台使用 .Net框架: IDE:集成开发环境,可以方便我们快速开发程序--编写 ...
- 入门Go语言神器!超全学习资源+笔记,新手从零学习全过程资源汇总
铜灵 发自 凹非寺 量子位 出品 | 公众号 QbitAI 如果你有意进大厂,学习Go语言一定要提到你的日程上了. 美国程序员招聘网站Hired发布<2019软件工程师状态>报告中显示,基 ...
- 梓益C语言学习笔记之链表&动态内存&文件
梓益C语言学习笔记之链表&动态内存&文件 一.定义: 链表是一种物理存储上非连续,通过指针链接次序,实现的一种线性存储结构. 二.特点: 链表由一系列节点(链表中每一个元素称为节点)组 ...
- 6.方法(go语言学习笔记)
6.方法(go语言学习笔记) 目录 定义 匿名字段 方法集 表达式 1. 定义 方法是与对象实例绑定的特殊函数. 方法是面向对象编程的基本概念,用于维护和展示对象的自身状态.对象是内敛的,每个实例对象 ...
- 梓益C语言学习笔记之指针
梓益C语言学习笔记之指针 一.32位平台下,地址是32位,所以指针变量占32位,共4个字节 二.内存单元的地址即为指针,存放指针的变量称为指针变量,故:"指针"是指地址,是常量,& ...
- c语言中void arrout,c语言学习笔记(数组、函数
<c语言学习笔记(数组.函数>由会员分享,可在线阅读,更多相关<c语言学习笔记(数组.函数(53页珍藏版)>请在人人文库网上搜索. 1.数组2010-3-29 22:40一维数 ...
- c语言float二进制输出代码_C语言学习笔记——学前知识概述
将我大一学习C语言时做的笔记拿来与大家分享,内容比较浅显,比较适合初学者,如有错误还请见谅,提出改正,谢谢! 前言:此C语言笔记是本人在自学时记录的一些重点或初学者常犯的错误,希望我的这本笔记能够对大 ...
- c语言学习笔记【结构体02】结构体指针变量与结构体变量的函数参数,C语言学习笔记结构体02结构体指针变量与结构体变量的函数参数.docx...
C 语言学习笔记[结构体02]结构体指针变量与结构体变量 的函数参数 C 语言学习笔记之结构体指针变量一提指针,那可 是 C 语言的核心了,有多少学子曾拜倒在指针的脚下.单纯的说指针,其实并不难,但是 ...
- C语言学习笔记---001C语言的介绍,编译过程原理,工具等
C语言学习笔记---001C语言的介绍,编译过程原理,工具等 创梦技术交流平台:资源下载,技术交流,网络赚钱: 交流qq群:1群:248318056 2群:251572072 技术交流平台:cre.i ...
最新文章
- Conversion error:Jekyll::Converters::Scss encountered an error while converting css/main.scss
- Redis实现参数的集中式管理
- XSLT - 利用template实现for循环
- Unity-3d Day03
- java n-ide 支持库,Android N是否要求IDE可以与Java 1.8或更高版本一起运行?
- JavaScript实现图像处理----resizeImageWidth调整图像宽度算法(附完整源码)
- 用GDB排查Python程序故障
- 2-2hadoop概述
- sql server 性能_SQL Server性能基础
- Revit 2021 族样板下载
- ftp服务器文件无法删除,ftp服务器文件删除
- linux版百度导航软件,百度导航2019新版
- 工程经济学99分速成复习——第一章 绪论
- 仿百度糯米TP5项目笔记
- php截图整个网页,selenium save_screenshot 如何进行全网页截图?
- VRRP协议个人理解(RFC5798)+典型配置+RFC2338/RFC3768文档翻译
- 无固定公网IP,群晖动态解析域名到动态公网IP
- 2022年3月盗取微软源代码的 APT组织 lapsus$完整资料汇总
- 参加数学建模比赛小结
- Linux网络管理以及端口聚合详解
热门文章
- 对bochs设置文档的初步翻译
- python精灵模块示例代码
- DerainCycleGAN: An Attention-guided Unsupervised Benchmark for Single Image Deraining and Rainmakin
- VMware虚拟机Linux 两边出现黑屏幕的妙计解决方法。
- 找工作时要注意的几点
- Winform MessageBox.Show时居中到主页面
- LED发光颜色及波长
- 注册ActiveX的命令是:regsvr32”+空格+控件名称
- MVS7200电流分流测量隔离放大器
- 无限火力是哪个服务器2019,LOL2019无限火力国服上线时间 LOL2019无限火力什么时候开?...