第九周
1.指针和二维数组
如果定义二维数组:
T a[M][N];
a[i](i是整数)
是一个一维数组- a[i]的类型是 T *
- sizeof(a[i]) = sizeof(T) * N
- a[i]指向的地址: 数组a的起始地址 + i×N×sizeof(T)
void Reverse(int * p,int size) { //颠倒一个数组
for(int i = 0;i < size/2; ++i) {
int tmp = p[i];
p[i] = p[size-1-i];
p[size-1-i] = tmp;
}
}
int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12}};
Reverse(a[1],4);
=> { {1,2,3,4},{8,7,6,5},{9,10,11,12}};
Reverse(a[1],6);
=> { {1,2,3,4},{10,9,5,6},{7,8,11,12}};
2.指向指针的指针
定义:
T ** p;
p是指向指针的指针,p指向的地方应该存放着一个类型为 T * 的指针
*p 的类型是 T *
#include <iostream>
using namespace std;
int main()
{
int **pp; //指向int*类型指针的指针
int * p;
int n = 1234;
p = &n; // p指向n
pp = & p; //pp指向p
cout << *(*pp) << endl; // *pp是p, 所以*(*pp)就是n
return 0;
}
=> 1234
3.指针和字符串
- 字符串常量的类型就是 char *
- 字符数组名的类型也是 char *
#include <iostream>
using namespace std;
int main()
{
char * p = "Please input your name:\n";
cout << p ; // 若不用cout, printf(p) 亦可
char name[20];
char * pName = name;
cin >> pName;
cout << "Your name is " << pName;
return 0;
}
____________________
Please input your name:
Jack↙
Your name is Jack
- 字符数组名的类型也是 char *,就是一个地址.
char name[20];
int n;
scanf("%d%s",&n, name);
cin >> n >> name;
4.字符串操作库函数
-
char * strchr(const char * str,int c);【string char】
寻找字符c在字符串str中第一次出现的位置。如果找到,就返回指向该位置的char*指针;如果str中不 包含字符c,则返回NULL。
-
char * strstr(const char * str, const char * subStr);【string substr】
寻找子串subStr在str中第一次出现的位置。如果找到,就返回指向该位置的指针;如果str不包含字符串subStr,则返回NULL。
-
int stricmp(const char * s1,const char * s2);【string ignore compare】
大小写无关的字符串比较。如果s1小于s2则返回负数;如果s1等于s2,返回0;s1大于s2,返回正数。不同编译器编译出来的程序,执行stricmp的结果就可能不同。
-
int strncmp(const char * s1,const char * s2,int n);【string number compare】
比较s1前n个字符组成的子串和s2前n个字符组成的子串的大小。若长度不足n,则取整个串作为子串。返回值和strcmp类似。
-
char * strncpy(char * dest, const char * src,int n);【string number copy】
拷贝src的前n个字符到dest。如果src长度大于或等于n,该函数不会自动往dest中写入‘\0’;若src长度不足n,则拷贝src的全部内容以及结尾的‘\0’到dest。
-
char * strtok(char * str, const char * delim);【string token 标记 delimiter:定界符,分隔符】
连续调用该函数若干次,可以做到:从str中逐个抽取出被字符串delim中的字符分隔开的若干个子串。
-
int atoi(char *s);【ascii to integer】
将字符串s里的内容转换成一个整型数返回。比如,如果字符串s的内容是“1234”,那么函数返回值就是1234。如果s格式不是一个整数,比如是"a12",那么返回0。
-
double atof(char *s);【ascii to floating point numbers】
将字符串s中的内容转换成实数返回。比如,"12.34"就会转换成12.34。如果s的格式不是一个实数 ,则返回0。
-
char *itoa(int value, char *string, int radix);【integer to ascii】
将整型值value以radix进制表示法写入 string:
char szValue[20]; itoa( 27,szValue,10); //使得szValue的内容变为 "27" itoa( 27,szValue,16); //使得szValue的内容变为"1b"
例案:
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char s1[100] = "12345";
char s2[100] = "abcdefg";
char s3[100] = "ABCDE";
strncat(s1,s2,3); // s1 = "12345abc"
cout << "1) " << s1 << endl; //输出 1) 12345abc
strncpy(s1,s3,3); // s3的前三个字符拷贝到s1,s1="ABC45abc"
cout << "2) " << s1 << endl; //输出 2) ABC45abc
strncpy(s2,s3,6); // s2 = "ABCDE"
cout << "3) " << s2 << endl; //输出 3) ABCDE
cout << "4) " << strncmp(s1,s3,3) << endl;
//比较s1和s3的前三个字符,比较结果是相等,输出 4) 0
char * p = strchr(s1,'B'); //在s1中查找 'B'第一次出现的位置
17
if( p ) // 等价于 if( p!= NULL)
cout << "5) " << p - s1 <<"," << *p << endl; //输出 5) 1,B
else
cout << "5) Not Found" << endl;
p = strstr( s1,"45a"); //在s1中查找字串 "45a"。s1="ABC45abc"
if( p )
cout << "6) " << p - s1 << "," << p << endl; //输出 6) 3,45abc
else
cout << "6) Not Found" << endl;
//以下演示strtok用法:
cout << "strtok usage demo:" << endl;
char str[] ="- This, a sample string, OK.";
//下面要从str逐个抽取出被" ,.-"这几个字符分隔的字串
p = strtok (str," ,.-"); //请注意," ,.-"中的第一个字符是空格
while ( p != NULL) { //只要p不为NULL,就说明找到了一个子串
cout << p << endl;
p = strtok(NULL, " ,.-"); //后续调用,第一个参数必须是NULL
}
return 0;
}
5.void指针
void * p;
-
可以用任何类型的指针对 void 指针进行赋值或初始化:
-
因 sizeof(void) 没有定义,所以对于 void * 类型的指针p,
*p
无定义+p, --p, p += n, p+n,p-n
等均无定义
double d = 1.54;
void * p = & d;
int a=3;
void * f = & a;
6.内存操作库函数
6.1内存操作库函数memset
头文件cstring中声明:
void * memset(void * dest,int ch,int n);
- 将从dest开始的n个字节,都设置成ch。返回值是dest。ch只有最低的字节起
作用。
例1:将szName的前10个字符,都设置成'a':
char szName[200] = "";
memset( szName,'a',10);
cout << szName << endl;
=>aaaaaaaaaa
例2:用memset函数将数组内容全部设置成0:
int a[100];
memset(a,0,sizeof(a));
则数组a的每个元素都变成0
6.2内存操作库函数memcpy
头文件cstring中声明:
void * memcpy(void * dest, void * src, int n);
- 将地址src开始的n个字节,拷贝到地址dest。返回值是dest。
例1:将数组a1的内容拷贝到数组a2中去,结果是a2[0] = a1[0], a2[1] =
a1[1]……a2[9] = a1[9] :
int a1[10];
int a2[10];
memcpy( a2, a1, 10*sizeof(int));
- 编写内存操作库函数memcpy
void * MyMemcpy( void * dest , const void * src, int n)
{
char * pDest = (char * )dest;
char * pSrc = ( char * ) src;
for( int i = 0; i < n; ++i ) {
//逐个字节拷贝源块的内容到目的块
* (pDest + i) = * ( pSrc + i );
}
return dest;
}
有缺陷,在dest区间和src区间有重叠时可能出问题!!!
7.函数指针
程序运行期间,每个函数都会占用一段连续的内存空间。而函数名就是该函数所占内存区域的起始地址(也称“入口地址”)。我们可以将函数的入口地址赋给一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以调用这个函数。这种指向函数的指针变量称为“函数指针”。
7.1定义形式
类型名(*指针变量名)(参数类型1,参数类型2,…);
int(*pf)(int,char);
//表示pf是一个函数指针,它所指向的函数,返回值类型应是int
,该函数应有两个参数,第一个是int 类型,第二个是char类型。
7.2使用方法
-
可以用一个原型匹配的函数的名字给一个函数指针赋值:
指针变量名=函数名;
-
要通过函数指针调用它所指向的函数,写法为:
函数指针名(实参表);
#include<stdio.h>
void PrintMin(int a,int b){
if(a<b) printf("%d",a);
else printf("%d",b);
}
int main(){
void(*pf)(int,int);//类型名(*指针变量名)(参数类型1,参数类型2,...);
int x=4, y=5;
pf=PrintMin;//指针变量名=函数名;
pf(x,y);//函数指针名(实参表);
return 0;
}
8.函数指针和qsort库函数
7.1数组排序
对数组排序,需要知道:
- 数组起始地址
- 数组元素的个数
- 每个元素的大小(由此可以算出每个元素的地址)
- 元素谁在前谁在后的规则
7.2快排库函数的书写格式
void qsort(void* base,int nelem,unsigned int width,
int(*pfCompare)(const void*,const void*));
- base:待排序数组的初始地址
- nelem:待排序数组的元素个数(number of elements)
- width:待排序数组的每个元素的大小(以字节为单位)
- pfCompare:比较函数的地址
可以对任意类型的数组进行排序
7.3编写比较函数
pfCompare: 函数指针,它指向一个“比较函数”。该比较函数应为以下形式:
int 函数名(const void * elem1, const void * elem2);
比较函数编写规则:
- 如果 * elem1应该排在 * elem2前面,则函数返回值是负整数
- 如果 * elem1和* elem2哪个排在前面都行,那么函数返回0
- 如果 * elem1应该排在 * elem2后面,则函数返回值是正整数
例案:
下面的程序,功能是调用qsort库函数,将一个unsigned int数组按照个位数从小到大进行排序。
#include<cstdio>
#include<cstdlib>
using namespace std;
int MyCompare(const void* element1,const void* element2){
unsigned int *p1,*p2;
p1=(unsigned int*)element1;
p2=(unsigned int*)element2;
return (*p1%10)-(*p2%10);
}
#define NUM 5
int main(){
int(*pf)(const void*elem1,const void* elem2);
pf=MyCompare;
unsigned int an[NUM]={8,123,11,10,4};
qsort(an,NUM,sizeof(unsigned int),pf);//*pf
for(int i=0;i<NUM;i++)
printf("%d ",an[i]);
return 0;
}
____________________________________________________________________________________
#include<cstdio>
#include<cstdlib>
using namespace std;
int MyCompare(const void* element1,consyt void* element2){
unsigned int *p1,*p2;
p1=(unsigned int*)element1;
p2=(unsigned int*)element2;
return (*p1%10)-(*p2%10);
}
#define NUM 5
int main(){
unsigned int an[NUM]={8,123,11,10,4};
qsort(an,NUM,sizeof(unsigned int),MyCompare);
for(int i=0;i<NUM;i++)
printf("%d",an[i]);
return 0;
}