郭炜-C语言程序设计-程序设计与算法(一)-第九周

第九周

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函数将数组内容全部设置成0int 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数组排序

在这里插入图片描述

对数组排序,需要知道:

  1. 数组起始地址
  2. 数组元素的个数
  3. 每个元素的大小(由此可以算出每个元素的地址)
  4. 元素谁在前谁在后的规则

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);

在这里插入图片描述

比较函数编写规则:

  1. 如果 * elem1应该排在 * elem2前面,则函数返回值是负整数
  2. 如果 * elem1和* elem2哪个排在前面都行,那么函数返回0
  3. 如果 * 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;
}    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值