首页
动态
归档
Github
留言
工具箱
更多
邻居
壁纸
音乐
Search
1
欢迎访问我的日志空间
8,244 阅读
2
C语言读写程序文件-学习三十二
742 阅读
3
WEB音乐播放器源码
712 阅读
4
Typecho - Joe主题魔改版(持续更新)
660 阅读
5
Typecho-1.1/17.10.30-定制版源码
639 阅读
学习笔记
源码库
BUG
第九艺术
登录
Search
标签搜索
学习笔记
C语言
typecho
Java
扫雷
源码
PHP
插件
网站源码
音乐播放器
模板
git
github
bug
XG.孤梦
累计撰写
50
篇文章
累计收到
44
条评论
首页
栏目
学习笔记
源码库
BUG
第九艺术
页面
动态
归档
Github
留言
工具箱
邻居
壁纸
音乐
搜索到
39
篇与
的结果
2022-02-18
C语言多维数组与指针-学习二十四
数组名作函数参数用数组名作函数参数时,因为实参数组名代表该数组首元素的地址,形参应该是一个指针变量。C编译都是将形参数组名作为变量来处理的。实参数组名是指针常量,但形参数组名是按指针变量处理。在函数调用进行虚实结合后,它的值就是实参数组首元素的地址。在函数执行期间,形参数组可以再被赋值。例如:void fun (arr[],int n){ printf("%d\n", *arr); // 输出 a[0]的值 arr=arr+2; printf("%d\n", *arr); // 输出 a[2]的值 }例子将数组a中n个整数按相反顺序存放#include <stdio.h> void main() { void inv(int x[], int n); int i,a[10] = {1,2,3,4,5,6,7,8,9,10}; int *p; for (i = 0; i < 10; i++) { printf("%d ", a[i]); } printf("\n"); inv(a, 10); for (i = 0; i < 10; i++) { printf("%d ", a[i]); } printf("\n"); } void inv(int x[], int n) { int temp, i, j, m = (n - 1) / 2; for (i = 0; i <= m; i++) { j = n - 1 - i; temp = x[i]; x[i] = x[j]; x[j] = temp; } }用指针变量作实参#include <stdio.h> void main() { void inv(int *x, int n); int i,arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, *p; p = arr; // 如果用指针变量作实参,必须先使指针变量有确定的值,指向一个已定义的单元。 for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } printf("\n"); inv(p, 10); for (p = arr; p <arr + 10; p++) { printf("%d ",*p); } printf("\n"); } void inv(int *x, int n) { int temp, *p,*i, *j, m = (n - 1) / 2; p = x + m; i = x; j = x + n - 1; for (; i <= p; i++,j--) { temp = *i; *i = *j; *j = temp; } }用指针方法对10个整数按由大到小顺序排序。#include <stdio.h> void main(){ void sort(int *x, int n); int i, *p, a[10] = {9,8,6,2,3,4,5,1,2,10}; p = a; for (i = 0; i < 10; i++) { printf("%d ", a[i]); } printf("\n"); sort(p, 10); for (i = 0; i < 10; i++) { printf("%d ",*p); p++; } printf("\n"); } void sort(int *x, int n) { int i, j, k, t; for (i = 0; i < n - 1; i++) { k = i; for (j = i + 1; j < n; j++) { if (*(x+j)>*(x+k)) { k = j; } if (k != i) { t = *(x + i); *(x + i) = *(x + k); *(x + k) = t; } } } }2、多维数组与指针二维数组在内存中存储时,将二维转换为一维形式。C语言中定义的二维数组可以看作是一个一维数组,而这个一维数组的每个元素又是一个一维数组。从二维数组的角度来看,a是二维数组名,a代表整个二维数组的首地址,也是二维数组0行的首地址,等于1000。a+1 代表第一行的首地址,等于1008。a+i 第i行首地址为: a+i×4×2。数组名可以代表数组首地址,所以 a[0] 代表0行0列地址,即 &a0 。a[1] 的值是 &a1 ,a[2] 的值是 &a2 。a, a[0], a[1],a[2]本身不占内存,不存放数据,仅表示一个地址。行元素地址表示:a+0、a+1、a+2 等等;列元素地址表示:a[0]+0、a[0]+1、a[0]+2、a[0]+3 等等元素值: *(a[i]+j) <=> *(*(a+i)+j) <=> a[]i[j]a[i] <=> *(a+i) 等价:第i行第0列的元素地址 &a[i][0]a[i]+j <=> *(a+i)+j 等价:第i行第j列的元素地址 &a[i][j]a[1]+3 <=> *(a+1)+3 等价:第1行第3列的元素地址 &a[1][3]行指针与列指针a+i = &a[i] = a[i] = *(a+i) = &a[i][0],值相等,含义不同。a+i <=> &a[i],表示第i行首地址,指向行a[i] <=> *(a+i) <=> &a[0][0],表示第i行第0列元素地址,指向列例子使用指针的方式输出二维数组元素的值。#include <stdio.h> void main() { float f[3][4] = { {0.0, 0.1, 0.2, 0.3},{1.0, 1.1, 1.2, 1.3},{2.0, 2.1 ,2.2, 2.3} }; float *pf; int i; pf = f[0]; for (i = 0; i < 12; i++) { if (i != 0 && i % 4 == 0){ printf("\n"); } printf("%6.2f", *pf++); } printf("\n"); }表达式 *pf++ 等价于 *(pf++) ,其含义是取 *pf 的值作为表达式的值,再使 pf加1。通过pf值的变化,逐一访问 f数组 中每个元素。顺序输出数组元素方法简单,而指定输出数组元素则要进行地址的计算。例如二维数组为n X m (n为行,m为列)首元素地:址为a[0]a[i][j]在数组中相对位置的计算公式: i * m + j (m为每行元素个数)a0a0a0a0a1a1a1a1a2a2a2a2位移量的计算:a[1][1] = 1*4+1 = 5a[2][3] = 2*4+3 = 11若初值:p=a[0]则:*(p+1*4+1) = *(p+5) = a[1][1]*(p+2*4+3) = *(p+11) = a[2][3]数组下标从0开始便于计算相对位置多维数组的指针变量二维数组指针变量说明一般形式为:类型说明符 (*指针变量名)[长度]例如:int(*p)[4]把二维数组a分解为一维数组a[0],a[1],a[2]之后,设p为指向二维数组的指针变量。例子使用指向一维数组的指针变量输出二维数组元素的值。#include <stdio.h> void main() { float f[3][4] = { {0.0, 0.1, 0.2, 0.3},{1.0, 1.1, 1.2, 1.3},{2.0, 2.1 ,2.2, 2.3} }; float (*pf)[4]; int i,j; pf = f; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%6.2f", *(*pf+i)+j); } printf("\n"); } }多维数组指针作为函数参数一维数组名可以作为函数参数传递,多维数组名也可以作为函数参数传递。例子3个学生各学4门课,计算总平均分,输出第n个学生成绩#include <stdio.h> void main() { void average(float *p, int n); void search(float(*p)[4], int n); float score[3][4] = { {65, 67, 79, 60}, { 80,87,90,81 }, { 90,99,100,98 } }; average(*score, 12); search(score, 1); } void average(float *p, int n) { float *p_end, sum = 0, aver; p_end = p + n - 1; for (; p <= p_end; p ++) { sum = sum + (*p); } aver = sum / n; printf("平均成绩:%6.2f\n", aver); } void search(float(*p)[4], int n) { int i; printf("第%d学生的成绩:\n", n); for (i = 0; i < 4; i++) { printf("%6.2f ", *(*(p + n) + i)); } printf("\n"); }
2022年02月18日
273 阅读
0 评论
0 点赞
2022-02-17
C语言指针与数组-学习二十三
指针变量作为函数参数函数的参数不仅可以是整型、浮点型、字符型等数据,还可以是指针类型。它的作用是将一个变量的地址传送到另一个函数中。例如:void swap(int *a,int *b)#include <stdio.h> void main() { void swap(int *x, int *y); int a,b; int *a1 = &a; int *b1 = &b; printf("请输入a,b的值:\n"); scanf_s("%d %d", &a, &b); if (a < b) { swap(a1, b1); } printf("max = %d, min=%d\n", a, b); } void swap(int *x, int *y) { int p; p = *x; *x = *y; *y = p; }注意:函数的调用可以(而且只可以)得到一个返回值(即函数值),而使用指针变量作参数,可以得到多个变化了的值。如果不用指针变量是难以做到这一点的。如果想通过函数调用得到n个要改变的值1.在主调函数中设n个变量,用n个指针变量指向它们;2.设计一个函数,有n个指针形参。在这个函数中改变这n个形参的值;3.在主调函数中调用这个函数,在调用时将这n个指针变量作实参,将它们的地址传给该函数的形参;4.在执行该函数的过程中,通过形参指针变量,改变它们所指向的n个变量的值;5.主调函数中就可以使用这些改变了值的变量。例子输入3个整数a,b,c,要求按由大到小的顺序将它们输出。用函数实现。#include <stdio.h> void main() { void exchange(int *x, int *y,int *z); int a,b,c; int *a1 = &a; int *b1 = &b; int *c1 = &c; printf("请输入a,b,c的值:\n"); scanf_s("%d %d %d", &a, &b, &c); exchange(a1,b1,c1); printf("%d, %d, %d\n", a,b,c); } void swap(int *x, int *y) { int p; p = *x; *x = *y; *y = p; } void exchange(int *x, int *y, int *z) { void swap(int *x, int *y); if (*x < *y) { swap(x, y); } if (*x < *z) { swap(x, z); } if (*y < *z) { swap(y, z); } }数组元素的指针一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址。指针变量可以指向数组元素(把某元素的地址放到一个指针变量中)。数组元素的指针就是数组元素的地址。引用数组元素时指针的运算在指针指向数组元素时,允许以下运算:一加一个整数(用 + 或 +=),例如:p + 1一减一个整数(用 - 或 -=),例如:p - 1一两个指针相减,如 p1-p2 (只有p1和p2都指向同一数组中的元素时才有意义)如果指针变量p已指向数组中的一个元素p+1指向同一数组中的下一个元素;p-1指向同一数组中的上一个元素。如果p的初值为&a[0],则p+i 和 a+i 就是数组元素a[i]的地址,或者说,它们指向a数组序号为i的元素。例如#include <stdio.h> void main() { int a[5] = {1,2,3,4,5}; int *a1 = &a[0]; // 或者 int *a1 = a printf("%d, %d\n", *a1, a1); a1 = a1 + 2; printf("%d, %d\n",*a1, a1); }当p指向a数组首元素时,数组元素 a[i] 的地址表达式有以下四种: &a[i] a + i p + i &p[i] 当p指向a数组首元素时,数组元素 a[i] 的值表达式有以下四种: a[i] *(a + i) *(p + i) p[i] a 和 p 是有本质的区别:a 是指针常量,其值不可变;p 是指针变量,其值可变。所以,a++、a = p 等都是非法表达式;而 p++、p = a都是合法的表达式。如果 指针p1 和 p2 都指向同一数组p2 - p1 用来计算 p2所指的元素 与 p1所指的元素 之间有多少个元素。两个指针变量相减 的含义是 两个地址之差 除以 每个数组元素所占的字节数。例子#include <stdio.h> void main() { int a[10] = {1,2,3,4,5,6,7,8,9,10}; int *p1 = &a[0]; int *p2 = &a[6]; printf("p1的地址:%d\n",p1); printf("p2的地址:%d\n",p2); printf("%d\n", p2 - p1); printf("%d\n",*p2 - *p1); // int 为4个字节 }*++p,等价于 *(++p),先使 p+1 即指向下一个数组元素,再取 *p 作为此表达式的值。*p++,等价于 *(p++),取 p 作为此 表达式的值,再使 p+1。(*p)++,先取 p 所指向的 元素值 作为 此表达式的值,再将 该元素值加1。++(*p),将 p 所指向的 元素值加1 作为 此表达式的值。#include <stdio.h> void main() { int a[10] = {1,2,3,4,5,6,7,8,9,10}; int *p = &a[0]; printf("%d\n",*p); printf("%d\n",*++p); printf("%d\n",*p++); printf("%d\n",(*p)++); printf("%d\n",++(*p)); }通过指针引用数组元素引用一个数组元素,可用下面两种方法:1.下标法,例如:a[i]2.指针法,例如:*(a+i) 或 *(p+i) 其中a是数组名,p 是指向数组元素的指针变量,其初值 p = a例子有一个整型数组a,有10个元素,要求输出数组中的全部元素。#include <stdio.h> void main() { int a[10] = {1,2,3,4,5,6,7,8,9,10}; int *p; // 下标法 for (int i = 0; i < 10; i++) { printf("%d ", a[i]); } printf("\n"); // 通过数组名计算数组元素地址 for (int i = 0; i < 10; i++) { printf("%d ", *(a+i)); } printf("\n"); // 指针法 for (p=a; p <a+10; p++) { printf("%d ", *p); } printf("\n"); }3种方法的比较:第(1)和第(2)种方法执行效率相同C编译系统是将 a[i] 转换为 *(a+i)处理的,即先计算元素地址。因此用第(1)和第(2)种方法找数组元素费时较多。第(3)种方法比第(1)、第(2)种方法快用指针变量直接指向元素,不必每次都重新计算地址。这种有规律地改变地址值 例如 p++ 能大大提高执行效率。用下标法比较直观,能直接知道是第几个元素。用地址法或指针变量的方法不直观,难以很快地判断出当前处理的是哪一个元素。
2022年02月17日
297 阅读
0 评论
0 点赞
2022-02-16
C语言指针-学习二十二
地址和指针的概念如果在程序中定义了一个变量,在对程序进行编译时,系统就会给该变量分配内存单元内存区的每一个字节有一个编号,这就是“地址”,它相当于旅馆中的房间号。在地址所标识的内存单元中存放数据,这相当于馆房间中居住的旅客-样。由于通过地址能找到所需的变量单元,我们可以用地址指向该变量单元,将地址形象化地称为“指针”。为了表示将数值3送到变量 i 中,可以有两种表达方法:1.直接存取例如: i=3;2.间接存取例如:*i_ad=3; 其中 *i_ad 表示 i_ad 指向的对象假设 i_ad 中的值是变量 i 的地址(2000),这样就在 i_ad 和变量 i 之间建立起一种联系,即通过 i_ad 能知道 i 的地址,从而找到变量 i 的内存单元。由于通过地址能找到所需的变量单元,因此说,地址指向该变量单元将地址形象化地称为“指针”,即通过它能找到以它为地址的内存单元。一个变量的地址称为该变量的“指针”例如:地址2000 是 变量 i 的指针如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”,指针变量就是地址变量,用来存放地址的变量,指针变量的值是地址(即指针)。例如:i_ad 就是一个指针变量指针变量的定义一般形式为:类型 *指针变量名;例如:int *addr_a, *addr_b; int 是为指针变量指定的 “基类型”基类型指定指针变量可指向的变量类型如 addr_a 可以指向整型变量,但不能指向浮点型变量注意: *addr_a = &a; 错误的addr_a = &a; 正确的float *addr_f; addr_f = &a; 错误的,a 为int类型,addr_f 为float类型例子通过指针变量访问整型变量。#include <stdio.h> void main() { int a = 10, b = 100; int *addr_a, *addr_b; // 定义指针变量 addr_a = &a; // 使addr_a指同a addr_b = &b; // 使addr_b指同b printf("a = %d, b = %d\n", a, b); // 直接输出 a,b的值 printf("addr_a = %d, addr_b = %d\n", *addr_a, *addr_b); // 间接输出 a,b的值 }指针变量的引用在引用指针变量时,可能有三种情况:1.给指针变量赋值。例如: p = &a;2.引用指针变量指向的变量。例如: p = &a; *p = 1;引用指针变量的值例如:printf("%o", p); 以八进制输出a的地址& 取地址运算符&a 是变量 a 的地址* 指针运算符 (“间接访问”运算符)例如: p 指向变量 a, 则 *p 就代表 a例子#include <stdio.h> void main() { int a,b,c,d,e,*p; a = 100; p = &a; b = *&a; printf("a=%d\n", a); printf("p=%d\n", p); printf("*p=%d\n", *p); printf("*&a=%d\n", b); c = (*p)++; printf("(*p)++ = %d\n", c); printf("a = %d\n",a); }#include <stdio.h> void main() { int m, n, *p1 = &m, *p2 = &n, *phint = NULL; m = n = 6; printf("m=%d,n=%d\n", m, n); printf("*p1=%d,*p2=%d\n", *p1, *p2); printf("p1=%d,p2=%d\n", p1, p2); printf("phint=%d\n", phint); }#include <stdio.h> void main() { int m, n, *p1 = &m, *p2 = &n, *phint = NULL; m = n = 6; *p1 += 3; p2 = p1; *p2*=4; phint = p2; printf("*p1=%d,*p2=%d\n", *p1, *p2); printf("p1=%d,p2=%d\n", p1, p2); printf("m=%d,n=%d\n", m, n); printf("phint=%d\n", phint); printf("*phint=%d\n", *phint); }
2022年02月16日
258 阅读
0 评论
0 点赞
2022-02-15
C语言预处理指令-学习二十一
预处理命令概述所谓编译预处理就是在编译程序对C源程序进行编译前,由编译预处理程序对这些编译预处理指令行进行处理的过程。C语言中,以 “#” 开头的行,都称为编译预处理指令行,每行的末尾 没有“;” 。C提供的预处理功能主要有以下3种:宏定义文件包含条件编译宏定义无参宏无参宏的定义格式:#define 标识符 字符串define 为宏定义命令。标识符 为所定文的宏名,通常用大写字母表示,以便于与变量区别。字符串 可以是常数、表达式等。例如:#define PAI 3.1415926“宏”:用一个标识符来表示一个字符串。“宏名”:被定义为“宏”的标识符。“宏替换”:在编译预处理时,预处理程序将程序中所有出现的“宏名”,都用宏定义中的字符串去替换。完成后,才将程序交给编译程序去处理。使用宏定义的优点:可提高源程序的可维护性;可提高源程序的可移植性;减少源程序中重复书写字符串的工作量。关于宏定义几点说明:宏名一般用大写字母表示,以示与变量区别。但这并非是语法规定。宏定义不是C语句,所以不能在行尾加分号。在宏展开时,预处理程序仅按宏定义简单替换宏名,而不作任何检查。宏定义命令#define出现在函数的外部,宏名的有效范围是:从定义命令之后,到本文件结束。在进行宏定义时,可以引用已定义的宏名。例如:# defineR 3# define PI 3.14159# define S PI*R*R对双引号括起来的字符串内的字符,即使与宏名同名,也不进行宏展开。例如: printf("R=%f,S=%f",R,S)符号常量在定义无参宏时,如果宏定义中的“字符串”是一个常量,则相应的““宏名” 称为“符号常量”。例子:#include<stdio.h> #define A 1+2 // 没有括号 void main() { int a; a = A * 2; // 替换后 a = 1+ 2 * 2 所以a = 5 printf("a = %d,A = %d\n", a, A); }有参宏有参宏的定义格式:#define 宏名(参数表) 字符串例如:#define ADD(X,Y) (X+Y)有参宏的调用和宏展开调用格式:宏名(实参表)宏展开:用宏调用提供的实参字符串,直接替换宏定义命令行中相应形参字符串,非形参字符保持不变。例子:#include<stdio.h> #define PAI 3.1415926 #define S(r) PAI*(r)*(r) void main() { double a; printf("请输入半径:\n"); scanf_s("%lf", &a); printf("半径为%.2f的圆的面积:%.2f\n", a, S(a)); }关于有参宏的几点说明:1.定义有参宏时,宏名与左圆括号之间不能留有空格。#define S(r) PAI*(r)*(r)上例中在 S 和 (r) 之间,不能有空格。 如果写成了 #define S (r) PAI*(r)*(r)表示 宏名S 所替换的字符串为 (r) PAI*(r)*(r) 。2.有参宏定义中,形参不分配内存单元,因此形参不必做类型定义; 而宏替换中的实参有具体的值要用它们去代换形参,因此实参必须做类型说明。在有参宏中,只是符号替换。3.调用有参宏名时,一对圆括号必不可少,圆括号中实参的个数应该与形参个数相同,如果有多个参数,参数之间用逗号隔开。4.在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。宏替换时对实参表达式不做计算直接照原样替换。5.在宏定义中,字符串内的形参和整个表达式通常要用括号括起来以避免出错。例子:# include< stdio.h > # define A(X,Y) X*Y void main() { int a, s; float w; printf("请输入a的值:\n"); scanf_s("%d", &a); s = A(a, a + 1); w = 6 / A(a, a); printf("s = a*a+1 = %d,w = 6/a*a = %.2f\n", s, w); }写成如下形式:#define A(X,Y) (X)*(Y)第1次调用宏计算 s 值时,宏替换后的语句:s = ((a)*(a+1));第2次调用么计算 w 值时,宏替换后的语句:w = 6/((a)*(a));结果就是 s = ((a)*(a+1)) = 42, w = 6/((a)*(a))= 6.00 宏定义时应在参数两侧加括号,也应在整个字符串外加括号。文件包含文件包含是指在一个文件中,去包含另一个文件的全部内容。C语言用#include指令实现文件包含的功能。文件包含的语法格式:首先在源码当前目录下面寻找该头文件,此方法通常用于包含自己定义的头文件。#include "文件名"首先在编译器默认的include目录下寻找该头文件,此方法通常用于包含标准库头文件。#include <文件名>例如:#include <stdio.h>#include <math.h>#include "diy.h"在编译预处理阶段,预处理程序将用指定文件中的内容来替换此行。从而把指定的文件和当前的源程序文件连成一个源文件。如果程序很大,最好分为几个不同的文件,每个文件含有一组函数。这些文件用#include将它们包含在主程序的开始处。有一些函数和宏几乎所有的程序中都会用到。可以将这些常用函数和宏定义存放在一个文件中,将这个文件包含在所写的程序中,该文件的内容就会插到程序中。被包含的文件扩展名可以为 .h ,此扩展名为头文件。一般包含在程序的头部。所有库函数被分成不同的类别,存放于不同的文件中。使用文件包含命令时要注意以下几点:1.当被包含文件修改后,包含该文件的源程序必须重新进行编译连接。2.一个文件包含命令只能指定一个被包含文件,如果要包含多个文件,则应使用多个文件包含命令。#include <stdio.h>#include <string.h>#include <math.h>3.文件包含允许嵌套,即在一个被包含的文件中又可包含另一个文件。在编译预处理时,要对 #include 命令进行”文件包含”处理,将 f2.c 的全部内容插入到 #include"f2.c" 命令处,得到所示的结果.在编译时,对 f1.c 作为一个源文件单位进行编译。条件编译如果希望程序中的一部分只在满足一定条件时才进行编译,也就是对这部分内容指定编译的条件,可以使用条件编译实现。条件编译有以下几种形式:形式一#ifdef 标识符 程序段1 #else 程序段2 #endif // 或者 #ifdef 标识符 程序段1 #endif功能:若标识符是已被宏定义指令定义过的宏名,则只对程序段1进行编译,程序段2不参加编译;否则只对程序段2进行编译,程序段1不参加编译。形式二#ifndef 标识符 // if n def 程序段1 #else 程序段2 #endif功能:若标识符是未被宏定义指令定义过的宏名,则只对程序段1进行编译,程序段2不参加编译;否则只对程序段2进行编译,程序段1不参加编译。与形式一刚好相反。形式三#if 常量表达式 程序段1 #else 程序段2 #endif功能:如常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。因此可以使程序在不同条件下,完成不同的功能。例子#include <stdio.h> #define DEBUG 0 void main() { #if DEBUG printf("Debugging...\n"); #else printf("Running...\n"); #endif }#define DEBUG 1上面介绍的条件编译当然也可以用条件语句来实现。但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长;而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。如果条件选择的程序段很长,采用条件编译的方法是十分必要的。有利于程序的可移植性,增加程序的灵活性。
2022年02月15日
345 阅读
0 评论
0 点赞
2022-02-14
C语言变量的作用域和类别-学习二十
变量的作用域在程序中能对变量进行存取操作的范围称为变量的作用域。根据变量的作用域不同,变量分为局部变量和全局变量。局部变量在一个函数体内或复合语句内定义变量称为局部变量。局部变量只在定义它的函数体或复合语句内有效,即只能在定义它的函数体或复合语句内部使用它, 而在定义它的函数体或复合语句之外不能使用它。例子#include <stdio.h> void main(){ int i,a,b; // 定义局部变量 i,a,b i = i + 1; // 可以使用 i { int c; c = a + b; // c 在此范围内有效; a, b在此范围内有效 } c = c + 1; // c 在此范围内无效 } int add(int x,int y){ int j; // 定义局部变量 j j = x + y; // 可以使用 j i = x + y; // 不能使用main函数里局部变量i,系统会提示 i 未声明 } 全局变量在函数定义之外定义的变量称为全局变量。全局变量可以在定义它的文件中使用,其作用域是从它的定义处开始到变量所在文件的末尾。例子#include <stdio.h> int i,j,a,b, n = 5; // 定义全局变量 void main(){ a = 5; b = 6; i = a + b; { j = a - b; // c在此范围内有效;a,b在此范围内有效 } int add(int x); // 函数声明 printf("i = %d, j = %d, m = %d\n",i,j,add(5)); } int add(int x){ int m; m = n + 1; // 可以使用n return m; }建议:不必要时不要使用全局变量,原因如下:1.全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。2.使用全局变量过多,会降低程序的清晰性。 在各个函数执行时都可能改变外部变量的值,程序容易出错,所以要限制使用全局变量。3.降低函数的通用性。 因为函数在执行时要依赖于其所在的外部变量。如果将-一个函数移到另一个文件中,还要将有关的外部变量及其值一起移过去。但若该外部变量与其他文件的变量同名时,就会出现问题,降低了程序的可靠性和通用性。一般要求把C程序中的函数做成一个封闭体, 只可以通过“实参-形参”的渠道与外界发生联系外。如果外部变量与局部变量同名,即全局变量与局部变量同名时,局部变量优先!#include <stdio.h> int a = 3, b = 5; // a,b为外部变量 void main() { int a = 8; // a为局部变量 printf("%d\n", max(a, b));//这里a为局部变量,b为全局变量 } max(int a,int b) { int c; c = a > b ? a : b; // 形参a、b作用范围只在max函数里 return (c); } 变量的存储类别1.动态存储方式与静态存储方式从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。从变量值存在的时间角度来分,又可以分为静态存储方式和动态存储方式。静态存储方式:指在程序运行期间由系统分配固定的存储空间的方式。动态存储方式:则是在程序运行期间根据需要进行动态的分配存储空间的方式。这个存储空间可以分为三部分:程序区静态存储区:全局变量动态存储区:函数的形参、函数中定义的变量、函数调用时的现场保护和返回地址等变量和函数有两个属性: 数据类型和数据的存储类别。存储类别指的是数据在内存中存储的方式。存储方式分为两大类:静态存储类和动态存储类。 包含:自动的( auto ) ;静态的( static ) ;寄存器的( register ) ;外部的( extern )。根据变量的存储类别,可以知道变量的作用域和生存期。auto变量自动的auto,不专门声明为static存储类别的局部变量都是动态分配存储空间,在调用该函数时系统会给 它们分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类。用关键字auto作存储类别的声明。例子 int f(int a) // 定义f函数,a为形参 { auto int b,C=3; // 定义b、C为自动变量 }关键字auto可以省略。auto int a,b,c=3; 与 int a,b,c=3; 二者等价static静态局部变量静态的static,当函数中的局部变量的值在函数调用结束后不消失而保留原值,该变量称为静态局部变量。用关键字static进行声明。例子#include <stdio.h> void main() { int f(int a); int a = 0, i; for (i = 0; i < 3; i++) { printf("%d\n", f(a)); } } int f(int a) { // 每调用一次,开辟新 a 和 b,但是 c 没有 auto b = 0; static c = 0; b++; c++; return (a + b + c); }静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。动态局部变量属于动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后即释放。静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。如果在定义局部变量时不赋初值,则对静态局部变量来说,编译时自动赋初值 0 (对数值型变量)或 空字符 (对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。虽然静态局部变量在函数调用结束后仍然存在,但其他函数不能引用它。例题求1-5的阶乘#include <stdio.h> void main() { int fac(int a); int i; for (i = 1; i <= 5; i++) { printf("%d的阶乘=%d\n",i, fac(i)); } } int fac(int a) { static int f = 1; f = f * a; return (f); }register变量寄存器的register,变量的值是存放在内存中的,当程序中用到哪个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。经过运算器进行运算,如果需要存数,再从运算器将数据送到内存存放。如果有一些变量使用频繁,则为存取变量的值要花费不少时间。为提高执行效率,C语言允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。由于对寄存器的存取速度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做寄存器变量。用关键字register作声明。例子#include <stdio.h> void main() { long fac(long); long i, n; printf("请输入一个整数:"); scanf_s("%ld", &n); for (i = 1; i <= n; i++){ printf("%ld的阶乘=%ld\n", i, fac(i)); } } long fac(long n){ register long i, f = 1; // 定义寄存器变量 for (i = 1; i <= n; i++) { f = f * i; } return(f); }extern外部变量外部的extern,变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存储区。用extern来声明外部变量,以扩展外部变量的作用域。例子在一个文件内声明外部变量#include <stdio.h> void main() { int max(int, int); extern A, B; // 声明外部变量 printf("%d\n", max(A, B)); } int A = 15, B = 8; // 定义外部变量 int max(int x, int y){ int z; z = x > y ? x : y; return(z); }在多文件的程序中声明外部变量,用extern将外部变量的作用域扩展到其他文件。文件一#include<stdio.h> int A; //定义外部变量 void main(){ printf("%d\n",A++); } 文件二extern A = 100; //声明A为一个已定义的外部变量static声明外部变量在程序设计中,某些外部变量只限于被本文件引用而不能被其他文件引用。这时可以在定义外部变量时加一个static声明。例子文件一#include<stdio.h> static int A; // 定义外部变量 void main() { printf("%d\n", A++); }文件二extern int A; // 无法使用 全局变量A void fun(int n) { A = A * n; }关于变量的声明和定义定义性声明:需要建立存储空间的(如: int a; )声明。引用性声明:不需建立存储空间的声明( extern a; )。注意:声明包括定义,但并非所有的声明都是定义。对 int a; 而言,它既具声明,又是定义。而对 extern a; 而言,它是声明而不是定义。总结1.从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下:局部变量包括:自动变量、静态局部变量、寄存器变量。全局变量包括:静态外部变量、外部变量。形式参数可以定义为 自动变量 或 寄存器变量。2.从变量存在的时间来区分,有动态存储和静态存储两种类型。动态存储:自动变量、寄存器变量、形式参数。静态存储:静态局部变量、静态外部变量、外部变量。静态存储是程序整个运行时间都存在,而动态存储则是在调用函数时临时分配单元。3.从变量值存放的位置来区分,可分为:内存中静态存储区:静态局部变量、静态外部变量、外部变量。内存中动态存储区:自动变量和形式参数。CPU中的寄存器:寄存器变量。4.关于作用域和生存期的概念作用域:如果一个变量在某个文件或函数范围内是有效的,就称该范围为该变量的作用域。生存期:如果一个变量值在某一时刻是存在的,则认为这一时刻属于该变量的生存期。作用域是从空间的角度,生存期是从时间的角度。内部函数和外部函数根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数。内部函数内部函数又被称为静态函数,它只能被定义它的文件中的其他函数调用,而不能被其他文件中的函数调用,即内部函数的作用范围仅仅局限于本文件。在定义内部函数时,在函数名和函数类型的前面加static。即 static 类型标识符 函数名(形参表)例如:static int fun( inta, intb)外部函数定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数是外部函数,可供其他文件调用。例如:函数首部可以写为 extern int fun (int a,int b) 这样,函数fun就可以为其他文件调用。如果在定义函数时省略 extern ,则隐含为外部函数。在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外部函数。例子有一个字符串,内有若干个字符,输入一个字符,要求程序将字符串中该字符删去。用外部函数实现。文件一 main主函数#include<stdio.h> void main() { extern void estring(char str[]); extern void dstring(char str[], char ch); extern void pstring(char str[]); char c, str[80]; printf("请输入一串字符:\n"); estring(str); printf("请输入一个要删除的字符:\n"); scanf_s("%c", &c); dstring(str, c); pstring(str); }文件二 dstring 删除字符函数void dstring(char str[], char ch) { int i, j; for (i = j = 0; str[i] != '\0'; i++) { if (str[i] != ch) { str[j] = str[i]; j++; } } str[j] = '\0'; }文件三 estring 接收输入函数#include <stdio.h> extern void estring(char str[80]) { gets(str); }文件四 pstring 打印输出函数#include <stdio.h> void pstring(char str[]) { printf("%s\n", str); }
2022年02月14日
247 阅读
0 评论
0 点赞
1
...
3
4
5
...
8