10:变量作用域、指针
来源:互联网 发布:奇葩秀视频直播间源码 编辑:程序博客网 时间:2024/05/16 09:22
【变量的作用域】
程序中一个变量只能被某些语句使用,这些语句叫做这个变量的作用域
声明在函数里面的变量叫做/* 局部变量 */,局部变量的作用域包含函数里面的所有语句
声明在所有函数外边的变量叫做/* 全局变量 */,全局变量的作用域包含程序里的所有语句
没有初始化的全局变量自动被初始化为 0
全局变量和局部变量可以重名,这个变量名称优先代表局部变量
如果全局变量和局部变量都可以满足程序,要求应该/* 优先使用局部变量 */
存储区的使用不受作用域限制(可以跨函数使用存储区)
存储区的使用受到生命周期的限制
生命周期是一段时间,存储区只能在这段时间里才能使用
全局变量的生命周期是整个程序的执行时间
局部变量的生命周期是函数执行的某一次时间范围
如果一个函数多次执行则局部变量每次代表存储区都不同
使用 static 关键字可以声明 /* 静态变量 */
静态变量的声明周期一定是整个程序的执行时间
只要程序没有结束,则静态变量的存储区随时可以使用
静态局部变量在函数每次执行的时候对应的存储区都是同一个
没有初始化的静态变量,自动被初始化为 0
静态变量的初始化只在程序开始的时候执行 1 次
静态全局变量的作用域只包含了声明它的文件里面的所有语句(不可以跨文件使用静态全局变量)
【指针】
指针变量用来记录存储区的地址
只有记录有效地址的指针变量才可以使用
int num = 0;
int *p_num;
p_num = #
*p_num = 10; //num结果为10
在有效指针变量名称前使用 * 操作符可以表示地址对应的存储区
指针也分类型,不同类型的指针适合与不同的存储区捆绑
可以在一条语句中声明多个同类型指针变量,这个时候要在每个指针变量名称前单独加 *
没有记录有效地址的指针分成两类:
1. /* 空指针 */里面记录空地址( NULL 表示,这个地址的数值就是数字 0)
2. /* 野指针 */其他所有无效指针的统称
程序员必须保证:/* 程序里不出现野指针 */
所有/* 指针变量必须初始化 */
<思想>
三个分支分别判断 12 13 23 进行三次对比交换
指针和存储区的捆绑关系可以随着程序的执行不断变化
这个时候可以把指针看作是存储区的某种身份,利用指针可以实现针对身份编程
如果指针和数组中第一个存储区捆绑,就可以用这个指针表示数组里的每个存储区
这个时候就认为指针间接捆绑了数组里的每个存储区
在这个指针后边使用下标就可以表示数组里的存储区了
int arr[] = {1, 2, 3, 4, 5};
int *p_num = arr;
for(...)
arr[num]
p_num[num] //指针地址名 [下标]
地址数据可以参与如下计算过程:
1. 地址 + 整数
2. 地址 - 整数
3. 地址 - 地址
地址数据+-整数n:实际上加减的并不是n,而是n个捆绑存储区大小
地址-地址:得到一个整数,这个整数表示两个地址之间捆绑的存储区个数
数组里第一个存储区的地址+下标,可以得到下标对应存储区的地址
可以采用如下方法表示数组里的存储区
*(arr + num) //数组名+num:下标为num的存储区
*(p_num + num) //指针名+num:下标为num的存储区
所有跨函数使用存储区都是通过指针实现的:
1. 被调用函数把一个存储区的地址作为返回值使用,可以让调用函数使用它的存储区
被调用函数需要提供一个指针类型存储区,记录作为返回值的地址
不可以把局部变量的地址作为返回值使用"静态局部变量生命周期为整个程序"
2. 数组做形式参数的时候,真正的形式参数就是一个指针,这个时候可以让被调用函数使用调用函数分配的存储区
【练习】
编写函数把主函数里两个变量的内容进行交换
<思想>
指针形参,地址做实参,三角交换法
【练习】
编写函数从一个数组里找到最大数字,所在存储区并把它传递给调用函数
<思想>
指针变量,循环时每次使用指针绑定最大值的地址
声明指针变量时可以使用 "const" 关键字
0. 什么时候需要 const 关键字:
如果函数不会修改指针形式参数捆绑存储区的内容,就应该在声明指针形式参数的时候,在类型名称前加 const 关键字
1. 如果 const 关键字写在类型名称前:
表示:不可以通过指针对捆绑存储区做赋值,但可以对指针本身做赋值
2. 如果 const 关键字写在指针变量名称前:
表示:不可以对指针本身做赋值,但可以对指针捆绑的存储区做赋值
声明指针的时候,可以使用 void 作为类型名称,这种指针叫"无类型指针"
void *p_num = # //无法判断num的数据类型
这种指针可以和任意类型的存储区捆绑
不能通过这种指针本身知道捆绑存储区的类型
在程序中不应该在无类型指针前使用 * 操作符,也不应该用这种指针+-整数
这种指针使用前必须强制类型转化成有类型指针
无类型指针应用:
通常"作为形式参数使用",可以利用它们把任意类型的存储区从调用函数传递给被调用函数
程序中一个变量只能被某些语句使用,这些语句叫做这个变量的作用域
声明在函数里面的变量叫做/* 局部变量 */,局部变量的作用域包含函数里面的所有语句
声明在所有函数外边的变量叫做/* 全局变量 */,全局变量的作用域包含程序里的所有语句
没有初始化的全局变量自动被初始化为 0
全局变量和局部变量可以重名,这个变量名称优先代表局部变量
如果全局变量和局部变量都可以满足程序,要求应该/* 优先使用局部变量 */
存储区的使用不受作用域限制(可以跨函数使用存储区)
存储区的使用受到生命周期的限制
生命周期是一段时间,存储区只能在这段时间里才能使用
全局变量的生命周期是整个程序的执行时间
局部变量的生命周期是函数执行的某一次时间范围
如果一个函数多次执行则局部变量每次代表存储区都不同
使用 static 关键字可以声明 /* 静态变量 */
静态变量的声明周期一定是整个程序的执行时间
只要程序没有结束,则静态变量的存储区随时可以使用
静态局部变量在函数每次执行的时候对应的存储区都是同一个
没有初始化的静态变量,自动被初始化为 0
静态变量的初始化只在程序开始的时候执行 1 次
静态全局变量的作用域只包含了声明它的文件里面的所有语句(不可以跨文件使用静态全局变量)
【指针】
指针变量用来记录存储区的地址
只有记录有效地址的指针变量才可以使用
int num = 0;
int *p_num;
p_num = #
*p_num = 10; //num结果为10
在有效指针变量名称前使用 * 操作符可以表示地址对应的存储区
指针也分类型,不同类型的指针适合与不同的存储区捆绑
可以在一条语句中声明多个同类型指针变量,这个时候要在每个指针变量名称前单独加 *
没有记录有效地址的指针分成两类:
1. /* 空指针 */里面记录空地址( NULL 表示,这个地址的数值就是数字 0)
2. /* 野指针 */其他所有无效指针的统称
程序员必须保证:/* 程序里不出现野指针 */
所有/* 指针变量必须初始化 */
初始化的时候 * 没有参与赋值过程,被修改的是指针变量本身的存储区
【练习】
指针捆绑三个变量,从键盘获得3个整数,按照从小到大的顺序打印,使用指针<思想>
三个分支分别判断 12 13 23 进行三次对比交换
/*代码*///方法1:用捆绑指针改变变量存储区的值,3次对比和交换实现#include <stdio.h>int main(){ int num = 0, num1 = 0, num2 = 0, tmp = 0; int *p_num = &num, *p_num1 = &num1, *p_num2 = &num2; printf("请输入三个整数:"); scanf("%d%d%d", p_num, p_num1, p_num2); if(*p_num > *p_num1){ tmp = *p_num; *p_num = *p_num1; *p_num1 = tmp; } if(*p_num > *p_num2){ tmp = *p_num; *p_num = *p_num2; *p_num2 = tmp; } if(*p_num1 > *p_num2){ tmp = *p_num1; *p_num1 = *p_num2; *p_num2 = tmp; } printf("从小到大排列:%d %d %d\n", num, num1, num2); return 0;}//方法2:3次对比值的大小,交换捆绑的指针进行按大小排列#include <stdio.h>int main(){ int num = 0, num1 = 0, num2 = 0, tmp = 0; int *p_num = &num, *p_num1 = &num1, *p_num2 = &num2, *p_tmp = &tmp; printf("请输入三个整数:"); scanf("%d%d%d", p_num, p_num1, p_num2); if(*p_num > *p_num1){ p_tmp = p_num; p_num = p_num1; p_num1 = p_tmp; } if(*p_num > *p_num2){ p_tmp = p_num; p_num = p_num2; p_num2 = p_tmp; } if(*p_num1 > *p_num2){ p_tmp = p_num1; p_num1 = p_num2; p_num2 = p_tmp; } printf("从小到大排列:%d %d %d\n", *p_num, *p_num1, *p_num2); return 0;}
指针和存储区的捆绑关系可以随着程序的执行不断变化
这个时候可以把指针看作是存储区的某种身份,利用指针可以实现针对身份编程
如果指针和数组中第一个存储区捆绑,就可以用这个指针表示数组里的每个存储区
这个时候就认为指针间接捆绑了数组里的每个存储区
在这个指针后边使用下标就可以表示数组里的存储区了
int arr[] = {1, 2, 3, 4, 5};
int *p_num = arr;
for(...)
arr[num]
p_num[num] //指针地址名 [下标]
地址数据可以参与如下计算过程:
1. 地址 + 整数
2. 地址 - 整数
3. 地址 - 地址
地址数据+-整数n:实际上加减的并不是n,而是n个捆绑存储区大小
地址-地址:得到一个整数,这个整数表示两个地址之间捆绑的存储区个数
数组里第一个存储区的地址+下标,可以得到下标对应存储区的地址
可以采用如下方法表示数组里的存储区
*(arr + num) //数组名+num:下标为num的存储区
*(p_num + num) //指针名+num:下标为num的存储区
所有跨函数使用存储区都是通过指针实现的:
1. 被调用函数把一个存储区的地址作为返回值使用,可以让调用函数使用它的存储区
被调用函数需要提供一个指针类型存储区,记录作为返回值的地址
不可以把局部变量的地址作为返回值使用"静态局部变量生命周期为整个程序"
int * read(void){ //被调用函数,返回指针类型(地址) static int num = 0; //static可以让局部变量在函数中返回其地址 printf("输入一个数:"); scanf("%d", &num); return # //对应函数返回值类型 }
2. 数组做形式参数的时候,真正的形式参数就是一个指针,这个时候可以让被调用函数使用调用函数分配的存储区
//第一种写法:指针做形式参数 #include <stdio.h> void print(int *p_num, int size){ int num = 0; for(num = 0; num < size; num++){ printf("%d ", *(p_num + num)); } printf("\n"); } //第二种写法:指针做循环变量 【牢记】 void print(int *p_num, int size){ int *p_tmp = NULL; for(p_tmp = p_num; p_tmp < p_num + size; p_tmp++){ printf("%d ", *p_tmp); } printf("\n"); } int main() { int arr[] = {1, 2, 3, 4, 5}; print(arr, 5); return 0; } //两种方法打印结果均为:1 2 3 4 5
【练习】
编写函数把主函数里两个变量的内容进行交换
<思想>
指针形参,地址做实参,三角交换法
/*代码*/#include <stdio.h>//方法1:整数变量做临时变量void swap1(int *p_a, int *p_b){ int tmp = 0; tmp = *p_a; *p_a = *p_b; *p_b = tmp;}//方法2:指针变量做临时变量,被调用函数才能打印void swap2(int *p_a, int *p_b){ int *tmp = NULL; tmp = p_a; p_a = p_b; p_b = tmp; printf("swap2:a=%d,b=%d\n", *p_a, *p_b);}int main(){ int a = 3, b = 8; printf("a=3,b=8交换后:\n"); swap2(&a, &b); swap1(&a, &b); printf("swap1:a=%d,b=%d\n", a, b); return 0;}
【练习】
编写函数从一个数组里找到最大数字,所在存储区并把它传递给调用函数
<思想>
指针变量,循环时每次使用指针绑定最大值的地址
/*代码*/#include <stdio.h>int * Max(int *p_num, int size){ int *p_tmp = NULL; int *p_max = NULL; for(p_tmp = p_num; p_tmp < p_num + size; p_tmp++){ if(!p_max || *p_tmp > *p_max) //!p_max处理第一个数字 p_max = p_tmp; //存储区捆绑地址,p_max每次放最大值地址 } return p_max;}int main(){ int arr[5] = {0}; int i = 0; printf("请输入5个数字,自动找到最大值:\n"); for(i = 0; i < 5; i++){ scanf("%d", &arr[i]); } int *p_num = Max(arr, 5); printf("最大值是:%d\n", *p_num); return 0;}
声明指针变量时可以使用 "const" 关键字
0. 什么时候需要 const 关键字:
如果函数不会修改指针形式参数捆绑存储区的内容,就应该在声明指针形式参数的时候,在类型名称前加 const 关键字
1. 如果 const 关键字写在类型名称前:
表示:不可以通过指针对捆绑存储区做赋值,但可以对指针本身做赋值
int num = 0; const int *p_num = # //*p_num = 10; 错误! 因为不可以对常量做赋值 p_num = NULL; //正确
2. 如果 const 关键字写在指针变量名称前:
表示:不可以对指针本身做赋值,但可以对指针捆绑的存储区做赋值
int num = 0; int * const p_num = # *p_num = 10; //正确 //p_num = NULL; 错误! 因为不可以对指针本身赋值
声明指针的时候,可以使用 void 作为类型名称,这种指针叫"无类型指针"
void *p_num = # //无法判断num的数据类型
这种指针可以和任意类型的存储区捆绑
不能通过这种指针本身知道捆绑存储区的类型
在程序中不应该在无类型指针前使用 * 操作符,也不应该用这种指针+-整数
这种指针使用前必须强制类型转化成有类型指针
char ch = 'q'; int num = 10; float fnum = 3.4f; void *p_v = NULL; //无类型指针 p_v = &ch; printf("%c\n", *(char *)p_v); //强制类型转换 p_v = # printf("%d\n", *(int *)p_v); //强制类型转换 p_v = &fnum; printf("%g\n", *(float *)p_v); //强制类型转换
无类型指针应用:
通常"作为形式参数使用",可以利用它们把任意类型的存储区从调用函数传递给被调用函数
0 0
- 10:变量作用域、指针
- 变量作用域与this指针
- 08:变量的作用域、初识指针
- Go语言:变量声明,作用域,指针
- 指针变量的作用
- 黑马程序员IOS-C语言-指针、变量作用域
- static变量作用域
- 变量作用域2
- 变量作用域3
- 变量作用域1
- 变量作用域
- 变量作用域
- C++变量作用域
- 变量作用域
- JavaScript 变量作用域
- php变量作用域
- 变量的作用域
- javascript 变量作用域
- 一道积分不等式的证明
- 编程规范2
- [github高级控件] 带你走近 -> CircleIndicator指示器原点动画切换
- 《重构--改善既有代码的设计》总结三之重新组织函数
- java学习(properties)
- 10:变量作用域、指针
- Redis入门 Hello World
- 数据结构之查找总结
- Android view 工作流程《上》
- shuf处理文本
- 1121. Damn Single (25)-PAT甲级真题
- Stanford coreNLP源码学习(1)
- Ubuntu安装codeblocks
- 1123. Is It a Complete AVL Tree (30)-PAT甲级真题