1
七、返回指针值的函数与生存期八、枚举
2
七、返回指针值的函数与生存期如果一个函数返回指针则得到返回指针值的函数。有不同类型的指针就有返回不同指针的函数。内部连接数属性的名称在本模块访问或索引,但指针用于在不同模块之间传递数据。可以通过指针或返回指针的函数将全局或局部静态变量的数据引渡到其它模块。
指针分全局指针、静态指针和局部指针。用一个长寿的指针访问短命的数组空间可能导致死机或黑屏的结果。
可以返回全局变量,静态局部变量或堆空间的地址以及入口形参指针所指向的主控函数中局部变量的地址。不要返回被调函数中定义的非静态的局部变量或局部数组的地址。
3
[例 ]返回 long*型指针值的函数与 static关键字内部屏蔽的编程技术。
a.cpp
#include<stdio.h>
void Show (const char* s,long p[ ])
{printf ("%s p[0]=%d,p[1]=%d \n",s,p[0],p[1]); }
extern long* fg (); long* fx();
extern long* fy (); long* fz();
void main (void) /*程序运行输出结果如下 */
{ Show ("globle",fg ()); //globle p[0]=1,p[1]=2
Show ("static",fx ()); // static p[0]=3,p[1]=4
Show ("heap ",fy ()); // heap p[0]=5,p[1]=6
4
Show ("stack ",fz ());
}
xyz.cpp
long* fz(){ long a[ ]={7,8,9}; return a+1; }
//返回一个局部内存地址危险!
long* fx(){ static long a[]={2,3,4}; return a+1; }
//返回静态局部内存的地址
long* fy() { long* y=new long[2]; y[0]=5;y[1]=6;
return y;} //返回堆空间的地址
g.cpp
static long g[ ]={1,2,3};
//定义 long型静态全局数组 g[3]
long* fg() { return g; }
// fg返回静态全局数组的首地址
5
说明:
函数 fz返回局部变量 a[1]的地址,此种用法违背了变量生存期的有效性,这种违规作业易导致程序的崩溃。
fx函数返回局部静态变量的地址,则是稳固的操作运算。静态局部变量封装在函数体中,函数返回该静态局部变量的地址,这是 C语言封装和数据共享的手段。
fy函数在对堆中数组实施初始化后返回 Heap空间的首地址,是高频采用的编程技巧。
fg函数的手法是常用的,该函数返回封装在模块内的静态全局数组的首地址。
6
[例 ] 求二维数组中的元素最后一次小于 60所在的行的函数
int* low (int(*)[3],int)
# include<stdio.h>
const int M=3;
int* low (int (*)[ M ],int n);
void show (int a[ ],const int m)
{for (int j=0; j<m; j++) printf ("%d,",a[ j] ); }
void main() //程序运行输出,61,58,78,70,90,56,
{ int a[ ][M]=
{60,70,80,85,95,75,
{70,90,56},88,90,78,
{61,58,78},80,90,70};
const int n=sizeof (a)/sizeof ( a [0]);
7
int * p=low (a,n);
for( int i=0; i<n; i+=2,p = low ( a,n-I ))
if (p!=0) show (p,M);
}
int* low (int a[ ][M],int n)
{ int* low = 0;
for (int i=0; i<n; i++)
for (int j=0; j<M; j++) if (a [ I ][ j ]<60) low =a [ i ];
return low;
}
//函数返回局部指针名 low,而不是全局函数名 low,返回的 a[i]是实参的地址。
8
[例 ] 返回指向二维数组的指针的函数与顺序法求数组中的极大值所在的行顺序法依照既定的次序从头到尾查找内存空间的元素,查找的判断准不同,但编程的思路简单明了,
程序不易出错。
顺序查找是高频采用的查找方法。上题也是顺序查找。
下面 FindFirstMax函数仅得到第一次求得的极大值所在的行。 FindFirstMax返回入口形参的指针的偏移,这个偏移地址指向实参数组的生存期有效的内存区。
#include<stdio.h>
const int M=3;
typedef double (*PT)[M];???????????
9
PT FindFirstMax (double (*q)[M],int n)
{ double max= q[0][0];
int keep=0;
for (int i=0; i<n; i++)
{ double* pi=q[i];
for (int j=0;j<M;j++)
if (pi[j]>max) {keep=i; max=pi[j]; }
} //函数返回 double(*)[3]型的地址 q+keep
return q+keep;
//返回的这个地址指向实参对应的数组空间
}
//局部指针 q的生存期到此结束
10
double
b[ ][3]={{60,70,58},{70,80,78},{90,95,85},{80,45,95}};
const int m=sizeof(b)/sizeof(b[0]);
PT q= FindFirstMax(b,m);
void main (void)
{ double* const p=*q;
int keep=q-b;
for (int k=0;k<3;k++)
printf ("b[%d][%d]=%4.1f\t",keep,k,p[k]);
}
//输出结果,b[2][0]=90.0 b[2][1]=95.0 b[2][2]=85.0
11
[例 ]返回二级指针的函数返回堆空间指针数组的首地址
int** ppHeap (int n,int m)
{ int** pp=new int*[n]; int *p=new int[n*m];
for (int j=0;j<n;j++) pp [j]=p+j*m;
return pp;
} /*2次调用 new运算符函数 */
int a[ ]={1,2,3,4,5,6};
int**s=ppHeap(2,3); /*定义全局指针 s */
#include<stdio.h>
void main()
{ int* p=*s;
for (int i=0; i<6; i++)
p[i]=a[i];
printf ("%d,%d",s[0][2],s[1][2]);
} /*输出,3,6*/
12
[例 ] 返回二级指针的函数返回堆空间指针数组的首地址
int** ppnHeap (int n,int m)
{ int** pp=new int*[n];
for (int j=0;j<n;j++)
pp[j] =new int[m];return pp;
} /*n+1次调用 new运算符函数 */
int a[ ][3]={1,2,3,4,5,6}; int**s=ppnHeap(2,3);
#include<stdio.h>
void main()
{ for (int i=0;i<2;i++)
{ int* p=s[i];
for (int j=0; j<3; j++) p [j]=a[i][j];
}
printf ("%d,%d",s[0][2],s[1][2]);
} //输出,3,6
13
new int*[n]分配指针数组空间,二级指针 pp操作指针数组。指针数组的元素 pp[j]如何指向是灵活的。
左边的 pp[j]分别指向堆空间单一的一维数组的相邻 n个元素的位置。
而右边的 pp[j]指向堆空间 n个不同的一维数组,它们在内存未必是相邻的。
堆空间的生存期由用户控制,应编写相关的函数释放 pp以及 pp[i]占有的堆空间。
14
八、枚举枚举旨在用有限个数名称方便地表示整型常数。
枚举类型是可用于重载的类型但不属于集合类型,不参入继承机制既不作基类也不作派生类,没有成员函数。
枚举一经声明,枚举常数就可以作为 int型常数直接使用,而无须定义枚举变量。
15
1,枚举的声明枚举是有限个整型符号常数构成的独特序列,用关键字 enum 来声明一个枚举类型,枚举类型名和枚举成员名遵循标识符的命名规定,其声明格式为:
enum [ EEnum ] enum [枚举类型名 ]
{ item1[=1+1],{ 枚举常数 1 [=整型常数表达式 1],
item2[=3],枚举成员 2[=整型常数表达式 2],
...,...,
itemn[=0xffff] 枚举元素 n[=整型常数表达式 n]
}; };
16
花括号包含的名称 item1,item2,itemn等称为枚举常数或枚举元素或枚举成员,枚举元素之间用逗号作为分隔而不以分号相隔。
枚举元素前无类型名界定枚举元素的类型属性,在
ANSI C/C++中枚举常数隐含地具有 int类型的属性,枚举变量占有的内存是一个 int型数所盘踞的内存即,
sizeof (EEnum) = sizeof (item1) = sizeof (int)
上面方括号包括的部分如枚举类型名可以省略,此时称为无名枚举。
整型常数表达式的操作数可以是整型常数和字符常数。
枚举元素仅可以在声明时显式赋值一次,方括号中的整型常数表达式可以全部缺省,此时系统默认第一个成员项具有值 0,第二个枚举常数具有值 1,依次类推。
17
方括号中的整型常数部分缺省时,后一个成员总是前一个成员的值加 1除非明显另行指定一个常数。
编译器从有值的成员依次增 1,如 item2指定为 3,则
item4为 5。
花括号中的枚举常数名不能彼此相同亦不能与其它相同范围的变量名相同,但整型常数表达式的值可以重复。
枚举常数名的作用范围始于其声明处超出花括号之外。
如果枚举声明位于全局范围则枚举常数名具有全局作用域,如果枚举声明位于类范围则枚举常数名隶属于外层的直接包含类,枚举在程序块中声明枚举常数名则限于其局部范围。
18
下面 #define序列指令:
#define e1 1
#define e2 2
...
#define e8 8
可以简练且几乎等价地写为:
enum E {e1=1,e2,e3,e4,e5,e6,e7,e8};
或 enum E { e5=5,e6,e7,e8,e1=1,e2,e3,e4 };
19
对于枚举常数的操作是对于其具体值的操作。
同样,
const int Sunday=0;
const int Monday=1;
const int Tuesday =2;
const int Wedneday=3;
const int Thursday=4;
const int Friday=5;
const int Saturday=6;
可以用枚举声明来等价地代替为:
enum EDay
{ Thursday=4,Friday,Saturday,Sunday=0,
Monday,Tuesday,Wedneday };
2,枚举变量枚举声明中 enum关键字之后的枚举名可以用来定义枚举变量。
枚举变量是一种特殊的变量,其取值范围只能是枚举声明中所允许的枚举元素名。
枚举变量的定义格式( 其中枚举名之前的 enum在
C++中可以省略)为:
enum EEnum ev1,ev2,..,evn;
枚举名 枚举变量 1,枚举变量 2,..,枚举变量 n;
21
例如,enum EDay ed1,ed2,ed3;
在 C++中可写为,EDay ed1,ed2,ed3;定义了三个枚举变量 ed1,ed2,ed3,具有类型属性为 enum EDay,它们的值只能是枚举元素,
[ Thursday,Friday,Saturday,Sunday,
Monday,Tuesday,Wedneday]
中名称之一。 例如:
ed1= Thursday; ed2= Saturday; ed3 = uesday;
是合法的赋值操作,但将一个整数赋给枚举变量是不合规则的运算,ed3=6;
尽管整数 6在枚举列表的具体值域内。对于整数可进行强制类型转换完成赋值。 如:
ed2= ( enum EDay)3; //等价于 ed2= Wedneday;
22
[例 ]整型表达式强制转换为枚举变量,引起枚举变量的值超出枚举常数之外。
# include<stdio.h>
void main (void)
{ enum Enum
{z,charDat=sizeof ('c'),fltDat =1+3,intDat =
sizeof (1)} e,x= intDat;
e=(Enum ) (charDat+fltDat+x+intDat);
printf ("E=%d,%d\n",e,(z+intDat) /fltDat);
}
23
表达式 fltDat+x中枚举常数 fltDat (值为 4)和枚举变量
x(值为 4)处理为 int型的右值参入运算。
用 typedef声明一个枚举的别名 EnumName,格式如下:
typedef enum EEnum { item1,item2,itemn }
EnumName;
可用枚举类的别名 EnumName定义枚举变量:
EnumName ev1,ev2,...evn; // 枚举别名 枚举变量 1,枚举变量 2,..,枚举变量 n;
24
[例 ]无名枚举直接指定 case分支的多个符号常数
#include<stdio.h>
enum { LINE=1,RECTANGLE,TRIANGLE };
const char * GetString (int nType)
{ switch ( nType )
{ case LINE,return ("Line");
case RECTANGLE,return "Rectangle";
case TRIANGLE,return "Triangle";
default,return "Unknown";
}
}
25
void main ( void )
{ enum { five=5 };
printf ("%d,%s\t",LINE,"Line");
printf ("%d,%s\t",TRIANGLE,GetString (TRIANGLE));
printf ("%d,%s\n",five,GetString (five));
}
//输出,1,Line 3,Triangle 5,Unknown
GetString是返回指针值的函数。函数调用
GetString ( LINE )和 return,Line”的,Line”具有直接映射关系。
枚举常数 LINE相当于整型常数 1,而不是字符串
"LINE"。
26