C和指针与相关应用

C和指针与相关应用

指针的基础知识

  • 指针是C语言的一个重要的概念,也是C语言的一个重要特色
  • 从地址可以找到内存中存储的相关数据,这个地址对应的就是C语言中的指针
  • 指针指向的是数据在内存中的地址,而不是指向内存中内容

&是取内容运算符
就是可以通过变量名,找到这个变量名对应的地址
比如:int a=0,printf("%p",&a) 此时&a就对应于变量a在内存中储存的地址

*是指针运算符
可以通过地址,找到这个地址里面的变量
比如:int a=0,*p;p=&a; 此时p就等同于变量a*,也是是指针p指向的对象,而p就等同于变量a的地址

对于int a=0,*p;p=&a;来说,*p(指针p指向的对象a)等价于a(原本的对象a)
对于int a=0;int *p;//此时需要对*p进行初始化 *p=a;来说,*p等于a的内容,此时a的地址和p指向的地址不是一样的 而且必须对*p进行初始化

  • 1、*p=a的意思是:将a的值赋给p指针指向的地址的值;
  • 2、p=&a的意思是:将a的地址赋给指针p;
#include <stdio.h>
//指针变量 和 指针的区别
int main()
{
    
    
    int anum = 10;
    int bnum = 20;
    int *a,*b;
    //a,b 为 指针变量 此时指向空地址
    //&取地址符号 将后者地址赋值给前者指针变量
    a = &anum;
    b = &bnum;
    printf("anum = %d,bnum = %d\n",anum,bnum);
    //%p 输出地址 将指针输出 指针本质是十六进制的地址
    printf("a's addr = %p,b's addr = %p\n",a,b);
    //*取内容符号 输出地址对应存储的内容
    printf("a's num = %d,b's num = %d\n",*a,*b);
	return 0;
}

此时指针a,b的地址是不一样的 是两个不同的指针变量
但是a和b指向的地址和内容与anum/bnum是一致的

指针变量的基础应用

  • 1.使用指针变量 交换两个数的值
#include <stdio.h>
//用指针交换两个数
//输入两个数 按大小输出
int main()
{
    
    
    int a,b,temp;
    scanf("%d%d",&a,&b);
	//a1,b1 为 int型指针变量 此时指向空地址
    int *a1,*b1;
	//把a的地址赋值给a1 把b的地址赋值给b1
    a1 = &a;
    b1 = &b;
    if(a<b){
    
    
     //用temp交换a1和b1指向的内容
	 //实际上是使用temp中间变量交换了a1和b1指向的a和b的内容
     temp = *a1;
     *a1 = *b1;
     *b1 = temp;
    }
    printf("max = %d,min = %d\n",a,b);
	return 0;
}
  • 2.也可以使用指针变量作为函数的参数进行传递
#include <stdio.h>
//用指针交换两个数 函数版
//输入两个数 按大小输出

void swaps(int *a1,int *b1){
    
    
    // 此时a1,b1为指针变量
    //*a1就是a,*b1就是b
    //交换a,b两变量的值
    int temp;
    temp = *a1;
    *a1 = *b1;
    *b1 =temp;
}
int main()
{
    
    
    int a,b;
    scanf("%d%d",&a,&b);
	//a1,b1 为 指针变量 此时指向空地址
    int *a1,*b1;
	//把a的地址赋值给a1 把b的地址赋值给b1
    a1 = &a;
    b1 = &b;
    if(a<b){
    
    
     //向swaps方法传入指针变量参数 并交换指针变量a1和指针变量b1指向的地址存储的值
     swaps(a1,b1);
    }
    printf("max = %d,min = %d\n",a,b);
	return 0;
}
  • 3.还可以在嵌套调用中使用指针的办法
#include <stdio.h>
//用指针排序三个数 嵌套调用的方法
//输入三个数 按由大到小输出
void exchange(int *a1,int *b1,int *c1){
    
    
    //*a1<*b1 交换a,b两数的值
    if(*a1<*b1){
    
    
        swaps(a1,b1);
    }
    //*a1<*c1 交换a,c两数的值
    if(*a1<*c1){
    
    
        swaps(a1,c1);
    }
    //*b1<*c1 交换a,b两数的值
    if(*b1<*c1){
    
    
        swaps(b1,c1);
    }
}

void swaps(int *a1,int *b1){
    
    
    // 此时a1,b1为指针变量
    //*a1就是第一个参数,*b1就是第二个参数
    //交换a,b存储的值
    int temp;
    temp = *a1;
    *a1 = *b1;
    *b1 =temp;
}

int main()
{
    
    
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
	//a,b,c 为 指针变量 此时指向空地址
    int *a1,*b1,*c1;
	//把a的地址赋值给a1 把b的地址赋值给b1 把c的地址赋值给c1
    a1 = &a;
    b1 = &b;
    c1 = &c;
	//向swaps方法传入指针变量参数 并按情况交换指针变量a1,指针变量b1和指针变量c1 指向的地址存储的值
    exchange(a1,b1,c1);
    printf("%d %d %d\n",a,b,c);
	return 0;
}

通过指针引用数组

  • 1.通过指针引用数组的方法
	int arr[7] = {
    
    1,9,19,8,1,0,0};
	//arr是数组的首地址 arr可以作为指针变量
	int *p = arr;
	//&arr[0]是该数组的首个元素的地址
	int *p1 = &arr[0];
	//*arr是取数组的首地址存储的值
	int arr1 = *arr;
	//arr[0]是取数组的首个元素的值
	int arr2 = arr[0];

注:
数组名就是一个指向数组第一个元素的指针变量
p指向的是arr的第一个元素的地址 也是数组的首地址
&arr[0]是该数组的首个元素的地址 p=p1=&arr[0]=arr=某个十六进制的地址
*arr是取数组的首地址存储的值
arr[0]是取数组的首个元素的值 arr2=arr1=arr[0]=*arr=1

  • 2.通过指针引用数组的应用
#include <stdio.h>
//指针和数组的相关应用
void outputarr(int arr[]){
    
    
    //接受传入的数组首地址
	//顺序打印数组
    for(int *a=arr;a<arr+7;a++){
    
    
        printf("%d ",*a);
    }
}

int main()
{
    
    
    int arr[7] = {
    
    1,9,19,8,1,0,0};
    //arr是数组的首地址 arr可以作为指针变量
    int *p = arr;
    //&arr[0]是该数组的首个元素的地址
    int *p1 = &arr[0];
    printf("CASE 1\n%p %p\n",p,p1);
    //*arr是取数组的首地址存储的值
    int arr1 = *arr;
    //arr[0]是取数组的首个元素的值
    int arr2 = arr[0];
    printf("%d %d\n",arr1,arr2);
    //p+4 是取数组的首地址的p+16 即(4*sizeof(arr)) 指向arr第五个元素的首地址
    p = p+4;
    //&arr[4] 是取数组的第五个元素地址
    p1 = &arr[4];
    printf("CASE 2\n%p %p\n",p,p1);
    //*(arr+4)是取数组的首地址的p+16的地址值
    arr1 = *(arr+4);
    //arr[4]取数组的第五个元素的值
    arr2 = arr[4];
    printf("%d %d\nCASE 3\n",arr1,arr2);
    //用数组指针遍历arr 方法1
    for(int *a=arr;a<arr+7;a++){
    
    
        printf("%d ",*a);
    }
    printf("\nCASE 4\n");
    //用数组指针遍历arr 方法2
    for(int *a=&arr[0];a<arr+7;a++){
    
    
        printf("%d ",*a);
    }
    printf("\nCASE 5\n");
    //用数组指针遍历arr 用数组名(一个指向数组第一个元素的指针变量)做参数 方法3
    outputarr(arr);
	return 0;
}
  • 3.通过指针引用多维数组
    多维数组的指针使用方法基本和一维数组类似
#include <stdio.h>
//指针和二维数组
void outputarr(int arr[][2]){
    
    
    printf("CASE 5\n");
    //接受传入的二维数组首地址 并循环输出
    for(int *a=&arr[0][0];a<arr+2;a+=1){
    
    
        printf("%d ",*a);
        if((a-&arr[0][0])%2!=0){
    
    
            printf("\n");
        }
    }
}

int main()
{
    
    
    int arr[2][2] = {
    
    {
    
    1,9},{
    
    8,1}};
    //arr是二维数组的第一行首地址
    int *p = arr;
    //&arr[0]是该二维数组的第一行的首个元素的地址
    int *p1 = &arr[0];
    printf("CASE 1\n %p %p\n",p,p1);
    //*arr[0]是取二维数组的第1行第1列存储的值
    int arr1 = *arr[0];
    //arr[0][0]是取二维数组的第1行第1列存储的值
    int arr2 = arr[0][0];
    printf("%d %d\n",arr1,arr2);
    //p+1 是取二维数组的第一行首地址的p+16 即(2*sizeof(arr)) 指向arr第2行的首地址
    p = p+1;
    //&arr[1] 是取二维数组的第二行第一个元素的地址
    p1 = &arr[1];
    printf("CASE 2\n%p %p\n",p,p1);
    //*(arr+1)是取二维数组的第二行的(第一个元素)首地址 *(arr+1)+1 为第二行第二个元素的地址 *(*(arr+1)+1)为取数组第二行第二个元素的值
    arr1 = *(*(arr+1)+1);
    //arr[1][1]取二维数组第二行第二个元素的值
    arr2 = arr[1][1];
    printf("%d %d\n",arr1,arr2);
    printf("CASE 3\n");
    //用数组指针遍历arr 方法1
    for(int *a=&arr[0][0];a<arr+2;a+=1){
    
    
        printf("%d ",*a);
        if((a-&arr[0][0])%2!=0){
    
    
            printf("\n");
        }
    }
    printf("CASE 4\n");
    //用数组指针遍历arr 方法2
    for(int *a=arr;a<arr+2;a+=1){
    
    
        printf("%d ",*a);
        if((a-arr[0])%2!=0){
    
    
            printf("\n");
        }
    }
    //用数组指针遍历arr 用二维数组首地址做参数 方法3
    outputarr(arr);
	return 0;
}

通过指针引用字符串

通过指针引用字符串的原理和通过指针引用数组是类似的

#include <stdio.h>
//引入memset函数
#include <string.h>
//指针和字符串和相关操作 定义strcopy方法
void strcopy(char str[],char str1 []){
    
    
    for(char *c = str,i=0 ; *c!='\0';c++,i++){
    
    
        str1[i] = *c;
    }
}

int main()
{
    
    
    char str[]="HELLO WORLD!!!";
    char str1[15];
    //str是字符数组的起始地址
    //%s输出str串
    printf("%s\n",str);
    //*str输出str第一个元素
    printf("%c\n",*str);
    //a串赋值到b串 方法1 老办法
    for(int i = 0 ; i < sizeof(str);i++){
    
    
        str1[i] = str [i];
    }
    printf("CASE 1 \n%s\n%s\n",str,str1);
	//清空字符串str1
    memset(str1,'\0',sizeof(str1));
    //a串赋值到b串 方法2 将str首地址赋值到字符指针上
    for(char *c = str,i=0 ; c < str + sizeof(str)&&*c!='\0';c++,i++){
    
    
		//把c指向的字符内容赋值到相对应的str1的位置上
        str1[i] = *c;
    }
    printf("CASE 2 \n%s\n%s\n",str,str1);
    //清空字符串str1
	memset(str1,'\0',sizeof(str1));
    //a串赋值到b串 方法3 数组作参数
    strcopy(str,str1);
    printf("CASE 3 \n%s\n%s\n",str,str1);
	return 0;
}

字符指针变量和字符数组的区别

  1. 两者的声明不一样
    字符数组 char str[20];
    字符指针变量 char str[20];char *p=str;
  2. 字符数组由若干个元素组成,每个元素都是一个字符 而 字符指针变量是存放的指向一个字符类型的变量的地址
  3. 赋值方式不同
    可以对字符指针赋值 不能对数组名赋值
	char *a;  //声明一个字符指针变量
	a = "I love China!"; //把字符串的首地址赋值给字符指针变量 赋值给a的是首地址 并不是内容
	char str[14];  //声明一个字符数组
	str[0] = 'I'; //合法 对数组元素赋值
	str = "I love China!"; //不合法 不能给指针对应的地址赋值
  1. 编译时 为字符数组分配足够多的存储单元 而字符指针变量只分配一个存储单元
  2. 指针变量的值可以改变 但是数据名对应的值不能改变
#include <stdio.h>
    int main(){
    
    
		char *a = {
    
    "I love China!"};
		a = a+7; //正确 a是指针变量 可以改变值
		printf("%s\n",a);
		return 0;
	}
#include <stdio.h>
    int main(){
    
    
		char str[] = "I love China!";
		str= str+7; //错误 数组名不能改变
		printf("%s\n",str);
		return 0;
	}
  1. 字符数组的各个元素可以改变 , 字符指针变量指向的字符串是个常量不能改变
		char str[] = "I love China!";
		str[2] = 'r'; //合法 可以对字符数组进行重新赋值
		char *a = "I love China!";
		a[2] = 'r'; //不合法 不可以对字符串常量进行重新赋值
		return 0;

指向函数的指针

定义一个指向函数的函数指针变量
比如

int max(int a,int b){
    
     return a>b?a:b;}
int (*p)(int,int) = max;  

使用(*p)(a,b) 来代替 max(a,b)的调用

#include <stdio.h>
//函数指针
//输入三个数 输出最大和最小的数
//定义函数max2num
int max2num(int a,int b)
{
    
    
    //返回两数最大值
    return a>b?a:b;
}
//定义函数max3num
int max3num(int a,int b,int c)
{
    
    
    int maxnum;
    //定义三个两个参数的函数指针p,p1,p2 指向max2num
    int (*p)(int,int) = max2num;
    int (*p1)(int,int) = max2num;
    int (*p2)(int,int) = max2num;
    //调用三次max2num函数 获得最大值
    return (*p2)((*p)(a,b),(*p1)(b,c));
}
//定义函数min2num
int min2num(int a,int b)
{
    
    
    //返回两数最小值
    return a<b?a:b;
}
//定义函数min3num
int min3num(int a,int b,int c)
{
    
    
    int minnum;
    //定义三个两个参数的函数指针p,p1,p2 都指向min2num函数
    int (*p)(int,int) = min2num;
    int (*p1)(int,int) = min2num;
    int (*p2)(int,int) = min2num;
    //调用三次min2num函数 获得最小值
    return (*p2)((*p)(a,b),(*p1)(b,c));
}
//定义指针型函数output
void *output(int a,int b,int c)
{
    
    
    //定义一个三个参数的函数指针p 指向max3num函数
    int (*p)(int,int,int) = max3num;
    //定义一个三个参数的函数指针p1 指向min3num函数
    int (*p1)(int,int,int) = min3num;
    //通过指针调用函数 (*p)(a,b,c)返回最大值 (*p1)(a,b,c)返回最小值
    printf("maxnum is %d minnum is %d",(*p)(a,b,c),(*p1)(a,b,c));
}
int main()
{
    
    
    int a,b,c;
    scanf("%d %d %d",&a,&b,&c);
    //声明一个函数指针
    int (*p)(int,int,int);
    //指向指针型函数
    p = &output;
    //调用指针型函数
    p(a,b,c);
	return 0;
}

返回指针值的函数

返回指针值的函数的声明
表示声明一个叫maxn的函数 该函数的返回值类型是指向int型变量的一个指针值

int *maxn(int a,int b){
    
    
}
#include <stdio.h>
//返回指针值的函数maxn 返回的是指向一个int型变量指针值
int *maxn(int a,int b){
    
    
    int *temp ;
    temp = a>b?&a:&b;
    return temp;
}

int main()
{
    
    
    int a,b;
    scanf("%d%d",&a,&b);
	//接收该指针值
    int *temp =maxn(a,b);
	//输出该指针指向的数据
    printf("the max number is %d",*temp);
	return 0;
}

指针数组

指针数组的声明
数据类型名 * 数组名[数组长];
注意不能写成
数据类型名 (* 数组名)[数组长];
前者是一个指针数组 数组的每一个元素都是一个指针 而后者定义一个指向一维数组的指针

扫描二维码关注公众号,回复: 17341552 查看本文章
#include <stdio.h>
#include <string.h>
//指针数组
//使用指针数组排序多个字符数组 使用指针数组作为参数传入函数内部
void sortstr(char *str[])
{
    
    
    char *temp;
    int i,j,k;
    //冒泡排序的相关代码
    for( i = 0 ; i < 4; i ++)
    {
    
    
        k = i;
        for(j=i+1; j<5; j++)
        {
    
    
            if(strcmp(str[k],str[j])>0)
            {
    
    
                k = j;
            }
        }
        if(k != i)
        {
    
    
            //交换不同指针指向的字符串内容
            temp = str[i];
            str[i] = str[k];
            str[k] = temp;
        }
    }
}
//输出指针数组
void printstr(char *str[])
{
    
    
    for(int i=0; i<5; i++)
    {
    
    
        printf("%s\n",str[i]);
    }
}
int main()
{
    
    
	//定义一个指向五个字符数组的指针数组str
    char *str[]= {
    
    "banana","cheer","cheems","apple","microsoft"};
	//使用指针数组的首地址作为参数 传入sortstr方法进行排序
    sortstr(str);
	//使用指针数组的首地址作为参数 传入printstr方法进行顺序输出
    printstr(str);
	return 0;
}

内存的动态分配

常用的内存分配函数有malloc,free,realloc,calloc
这些源文件都包含在stdlib.h头文件内
malloc
原型定义 void *malloc(size_t size)
参数是需要划分的内存的大小 单位是字节数
返回的是无类型的指针 需要手动转换为需要的数据类型
比如

	char *p;
    //用malloc动态分配内存区域给p
    p = (char *) malloc(10*sizeof(char));//强制转换为需要的数据类型

free
原型定义 void free(void *ptr)
释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。
比如

	char *p;
    //用malloc动态分配内存区域给p
    p = (char *) malloc(10*sizeof(char));//强制转换为需要的数据类型
	//释放分配的内存空间
	free(p);

realloc
原型定义 void *realloc(void *ptr, size_t size)
尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
参数是之前分配好内存的指针变量和需要重新划分的内存的大小 单位是字节数
返回的是无类型的指针 需要手动转换为需要的数据类型 如果请求失败,则返回为 NULL。
比如

	char *p;
    //用malloc动态分配内存区域给p
    p = (char *) malloc(10*sizeof(char));//强制转换为需要的数据类型
	//调整之前分配的内存空间
	realloc(p,20*sizeof(char));

calloc
原型定义 void *calloc(size_t nitems, size_t size)
分配所需的内存空间,并返回一个指向它的指针。
主要是为了开辟动态的内存区
参数是需要划分的区域的个数nitems 和每个区域的字节数size
返回的是无类型的指针 需要手动转换为需要的数据类型

	char *p;
    //用calloc动态分配内存区域给p
    p = (char *) calloc(50,sizeof(char));//强制转换为需要的数据类型
#include <stdio.h>
#include <stdlib.h>
int main()
{
    
    
//定义指针数组
    char *p;
    //用malloc动态分配内存
    p = (char *) malloc(10*sizeof(char));
	//p = (char *) calloc(50,sizeof(char));
    scanf("%s",p);
    //三种输出方式
    puts(p);
    printf("%s\n",p);
    char *temp = p;
    for(int i=0;i<10&&*(temp)!='\0';i++){
    
    
        printf("%c",*(temp++));
    }
    printf("\n");
    //realloc不太容易成功 有可能返回NULL 导致程序闪退
    realloc(p,20*sizeof(char));
    free(p);
    //free之后再printf会报错或闪退
    //printf("%d\n",p[0]);
	//用calloc重新动态分配内存
    p = (char *) calloc(50,sizeof(char));
	//使p[0]为'c'
    p[0]='1';
    printf("%c\n",p[0]);
    return 0;
}

c和指针的相关小结

  1. 使用指针时要注意不要越过数组的上界与下界 如果数组越界可能会导致程序闪退或报错
  2. 使用指针变量之前一定要初始化,否则可能出现野指针的问题 使用这个指针有可能导致程序闪退或报错
  3. 两个指针变量可以相减。两个指针相减则表示两个指针相差的元素个数 至少需要保证这两个指针都是指向一个数组的不同元素的合法指针
  4. 常见的指针的应用如下
定义 含义
int *p; 定义指向一个整形数据的指针p
int **p; 定义指向一个整形数据的指针变量的指针变量p
int *p[n]; 定义指针数组p,该指针数组的每个元素都是一个单独的指针变量
int (*p)[n]; 定义指向拥有n个元素的一维数组的指针变量p
int *p(); 定义返回值类型为指向一个整形变量的函数p()
int (*p)(); 定义一个函数指针p,指向返回值为整形变量的一个方法比如int func(),可以用 (*p)()来代替func()的调用

猜你喜欢

转载自blog.csdn.net/a695415974/article/details/122512348
今日推荐