一、作用域
栈帧:每个函数只能看到自己的;(作用域)
例1:
void f(int a,int b)
{
/*作用域1:j 在作用域2中创建 只能在作用域2中访问 作用域1看无法访问*/
int i;
for(i = 0 ; i< a+b ; i++)
{
/*作用域2:可访问外层作用域值 i a b*/
int j;
}
}
例2:
void f(int a,int b)
{
/*作用域1:j 在作用域2中创建 只能在作用域2中访问 作用域1看无法访问*/
int i;
for(i = 0 ; i< a+b ; i++)
{
/*作用域2:可访问外层作用域值 i a b*/
int j;
}
}
void f2(int a,int b)
{
/*作用域3:与f1中的实参毫无关系,他们是互不重叠的作用域*/
f1(a+b,a-b);
}
例3:
void f(int a,int b)
{
/*作用域1:j 在作用域2中创建 只能在作用域2中访问 作用域1看无法访问*/
int i;
for(i = 0 ; i< a+b ; i++)
{
/*作用域2:可访问外层作用域值 i a b*/
int j;
int u;/*内层作用域中创建另一个相同名字变量合法;影子变量*/
u = 5;
}
/*修改作用域2中的u,不会改变作用域1中的u,影子变量是不好的编程类型 gcc编译器会提醒-Wshadow警告*/
}
二、swap函数
一个函数只能返回一个值,返回值可以修改调用函数中一项内容;
例:
int f1(int a,int b)
{
return (a+b);
}
void f2(void)
{
int a;
a = f1(2,3);
/*RL A*/
}
想要改变调用函数多个变量值(指针)
例:
void swap(int x,int y)
{
/*将s与y进行交换*/
}
void f2(void)
{
int a = 2;
int b = 37;
swap(a,b);
}
三、指针
引入指针概念:通过指针概念来解决上述问题;指针是一个变量(或是一个实参),其值为一个内存地址。为创建指针,在类型后面加上*;
type * ptr
int * iptr; //名称 iptr 值 一个地址 地址中内容 一个整型
char * cptr; //名称 cptr 值 一个地址 地址中内容 一个字符
double *dptr;//名称 dptr 值 一个地址 地址中内容 一个双精度浮点数
程序员不能控制具体地址分配,为获取有效地址,提供了特殊语法:在变量前加&;
int a = 16; //a是一个整型数
int * iptr; //iptr是一个指针
iptr = &a; //iptr的值是a的地址
//标识符 iptr 地址 101 值100
//标识符 a 地址 100 值16
//注:每次运行地址都发生改变 我们可以改变变量值,但是不能改变变量地址
**指针作用:**指针像其他变量类型一样,可以为同一个值;
检索存于指针所指地址处的值:
int a = 120;
int * iptr;
int c;
iptr = &a;
c = *iptr;/*把iptr的值看作一个地址来读取;到那个地址处读取并将其值赋给C*/
printf("%d",*iptr);
//如果*iptr在赋值符号(=)右边(RHS):解引指针;赋值符号不是必须的 读取地址所指向的值
//如果*iptr在赋值符号(=)左边(LHS):会以一种相似却相反的方式工作 修改地址所指向的值
讨论swap函数
void swap(int *a ,int *b/*a和c的地址*/) //函数实参必须是储存这些地址的指针
{
/*将s与y进行交换*/
int c = *a;
*a = *b;
*b = c;
}
void f2(void)
{
int a = 2;
int b = 37;
swap(a,b);
}
//通过指针,一个函数可以访问(读写)在另一个栈帧的变量值;
int * f1(void)
{
int m = 0;
return &m;
}
void f2(void)
{
int * iptr = f1();
/*RL*/
*iptr = 7;
}
//不能把m值从0改为7,因为m值存在f1栈帧中;
//指针进行读写只能在一个方向起作用;通过指针,一个函数可以读写储存在函数帧或它晓媚的栈帧中的值,读写栈帧上方帧中的值是不可能的
**指针类型与变量类型不一致:**不要这样去做;gcc编译器会出现-Wall;
四、数组和指针
- 数组下标总是从0开始的;
- 数组与指针之间是有着一种特殊关系的,在许多情况下数组和指针是没有区别的;
int c;
int arr[5];//arr的值看做一个指针,其值作为数组第一个元素的地址;
arr[2] = 9;//2是[]里面的索引,叫地址偏移量;
c = arr[2];
//1、arr的值看作一个指针,其值为数组第一个元素的地址
//2、找到那个地址之后两个元素的地址得到一个新的地址。2为地址偏移
//3、读取那个地址的值9
//4、把9写道c的值中
//arr总是可以看作一个指针,其值为数组第一个元素的地址。
//arr == &arr[0]
例:把一个数组中的元素添加在一起
int sumarr(int * intarr,int len)
{
int ind;
int sum2 = 0;
for(ind = 0; ind < len ;ind ++)
{
sum2 += intarr[ind];
}
return sum2;
}
void f(void)
{
int sum = 0;
int arr[5];
arr[0] = 4;
arr[1] = -7;
arr[2] = 2;
arr[3] = 3;
arr[4] = 9;
sum = sumarr(arr , 5);
/*RL*/
printf("sum = *d \n",sum);
}
类型规则:
- 如果var类型是t,那么&var的类型是t*;
- 如果ptr的类型是t*,那么*ptr的类型是t;
- 如果arr是一个类型t的数组,那么每个元素储存着一个类型t的值;
- 如果arr是一个类型t的数组,那么arr(没有任何下标)的类型数t*,因为arr等效&arr[0];\
- 数组的名字总是一个指针;
- 指针不一定必须是数组;禁止野指针操作;
五、指针运算
指针可以通过(访问)数组的元素从而实现迭代:
#include <stdio.h>
#include <stdlib.h>
int main(int argc ,char * *argv)
{
int arr1[] ={7, 2, 5, 3, 1, 6, -8, 16, 4};//【】之间不添加任何内容,就可以创建一个常量数组,编译器会自动计算数组长度
char arr2[] ={'m', 'q' , 'k', 'z', '%', '>'};
double arr3[] ={3.14, -2.718, 6.626, 0.529};
int len1 = sizeof(arr1) / sizeof(int);//这种长度计算只对常量数组有效,malloc创建的不起作用
int len2 = sizeof(arr2) / sizeof(char);
int len3 = sizeof(arr3) / sizeof(double);
printf("lengths = %d , %d , %d \n", len1,len2,len3);
int * iptr = arr1;
char * cptr = arr2;
double * dptr = arr3;
printf("values = %d , %c , %f \n",*iptr,*cptr,*dptr);
iptr++;//指针增1,实际值增4
cptr++;//指针增1,实际值增1
dptr++;//指针增1,实际值增8
printf("values = %d , %c , %f \n",*iptr,*cptr,*dptr);
}