C语言学习——4

一、指针

1.指针、地址、基地址

指针定义:用于存放地址的一个数据类型

地址定义:地址就是内存中每一个字节的编号,假设一个内存的大小为4G,他的地址会从0x 0000 0000 ~ 0x FFFF FFFF

基地址:变量所占字节中,地址编号最小的,作为变量的地址,也称为基地址

alt

指针的作用

  1. C程序设计中使用指针可以:
  2. 使程序简洁、紧凑、高效
  3. 有效地表示复杂的数据结构
  4. 动态分配内存
  5. 得到多于一个的函数返回值

指针变量

​ 用来存放变量地址的变量(指针变量也有地址,但是该变量里面存的值是所指目标的地址)

定义格式

​ <存储类型> <数据类型> <*><变量名>;

int *p;				//可以指向整型空间的指针,这是一个野指针
					//野指针:没有赋值,所以指向未知地址的指针,称为野指针
int *p = NULL;		//指向空地址的指针(空指针)
					//空地址是不能被访问的,会出现段错误

int a = 100;
p = &a;					//取地址符
printf("p = %p\n",p);	//输出 a 的地址
*p = 10;				//访问所指向地址的空间,等同于 a = 10
printf("*p = %d\n",*p); //输出值为 10

2.大端序、小端序

大端:低位数据存放在高地址

小端:低位数据存放在低地址

int a = 0x12345678;
char *p = (char *)&a;		//转换数据类型为char *,每次只取一一个字节
for(inti=0;i<4;i++)
{
	printf("%#x\n",*p);
	p++ ;
}

alt

3.指针相关运算符

p+n; 			//以指针指向的位置为基准,往高地址方向,偏移n个元素(n*元素大小个字节数)
p-n;			//以指针指向的位置作为基准,往低地址方向,偏移n个元素(n*元素大小个字节数)
    
p++;			//跟 p+1 区别,会更新p的地址,也就是 p = p + 1;
p--;			//同p++

p - q			//p 、 q同为指针,结果为两指针之间所间隔的地址大小
p + q			//无意义操作

/*
	运算逻辑:
		零地址	--- 假		NULL --- 零地址的宏定义
		非零地址 --- 真
	支持关系运算:
		>、<、>=、<=、==、!=
	
	[]: 变地址运算符
	数组名:
		1.数组本身
		2.数组首地址
		3.数组第一个元素的地址
	数组名[]访问元素:
*/
    int a[3] = {1,2,3};		//数组和指针可以互相赋值(本意上都是对地址进行操作)
	a[0] = 4;				//a[0] ---> *(a+0) 地址偏移量
指针和地址
int arr[3] = {1,2,3};

arr[0] = 4;
*(arr+1) = 5;								//1.数组当指针用

int *p = arr;
*(p+2) = 6;									//2.指针正常偏移

for(inti=0;i<3;i++)
{
	printf("arr[%d] = %d",i,arr[i] ) ;
}
printf("\n");

p[0] = 10;									//3.指针当数组用
printf("arr[0] = %d\n",arr[0]) ;

练习

使用函数strstr

char *strstr(const char *str1,const char *str2);
//参数:
const	char *str1	//被查找字符串的首地址
const	char *str2	//子字符串的首地址
/*
返回值:
	找到,返回第一个字符串的首地址
	没找到,返回NULL
*/

使用示例:

#include <stdio. h>
#include <string.h> 
int main(int argc, char *argv[]){
    char *str1 = "abcdefg";
	char *str2 = "cd";
	//原地址
    printf("%p\n" , str1);
    
	char *result = strstr(str1,str2) ;
    
    //返回后的首地址(子串被找到的位置)
	printf ("%p\n" , result);
	printf ("%s\n" , result);
	return 0;
}

手动实现:

运行结果

0x55644013b004
0x55644013b006 
cdefg

4.指针与一位数组的关系

维数组的名字代表了数组的首地址和数组首元素的地址,但是不能被更改,因为是一个地址常量

int a[5] = {1,2,3,4,5};

//让指针指向数组的首元素
	int *p = a;
	int *p = &a[0];

//让指针指向数组中的某一元素
	int *p = &a[3];
	int *p = a + 3;

//a[2] == *(a+2) == p[2] == *(p+2)
int *p;
p = a;
练习

利用指针遍历数组,找到最大值

/*===============================================
*   文件名称:eg2.c
*   创 建 者:青木莲华     
*   创建日期:2025年07月17日
*   描    述:
================================================*/
#include <stdio.h>

int main(int argc, char *argv[])
{ 
    int arr[10] = {0};
    int *p = arr,max;
    //输入
    printf(">:");
    for(int i = 0 ; i < 10 ; i++)
    {
        scanf("%d",&arr[i]);
    }
    max = *p;
    printf("%d ",max);
    for(int i = 1 ; i < 10 ; i++)
    {
        if(max < *(p+1)) 
            max = *(p+1);
        printf("%d ",*(p+1));
        p++;
    }
    printf("\n");
    printf("max_num is : %d\n",max);
    return 0;
} 

运行结果:

>:1 88 29 10 8 123 54 65 11 93
1 88 29 10 8 123 54 65 11 93 
max_num is : 123

5.指针与一维字符数组的关系

char str[32] = "he11o wor1d";
char *p = str;

//字符串存储在字符串常量区,在此区域中有且仅有一个相同字符串
char *p = "hello world";
char *q = "hello world";
//p 和 q 指针因为字符串常量相同,所以在常量区里面 p 和 q指向同一个地址

6.一级指针与二维数组的关系

//1.二维数组在内存中连续
//2.二维数组的名字代表了行指针

int a[3][3] = {1,2,3,4,5,6,7,8,9};

//让指针指向数组[0][0]的元素
int *p = a[0];
int *p = a[0][0];

//让指针指向数组中的某一个元素
int *p = &a[0][2]
int *p = a[0] + 2;

7.指针所占空间大小

//指针所占空间大小
	int  *p;
	char *q;
/*
在同一个系统中,
地址所占空间大小是一致的,与指针的数据类型无关
所以 p 和 q 所占空间大小一致
例如: 在32位系统中,指针大小(地址)为8字节
*/

8.行指针(数组指针)

/*
行指针是一个指针,指向的是一个装着指针的数组
二维数组的名字 --- 代表了首行的地址
&一维数组的名字 --- 代表了行地址
*/

/*
一般形式:
	存储类型 数据类型 (*变量名)[元素个数];
*/

int (*p)[4];

/*
升维: & --- 一维数组名
降维(解引用): * --- 行指针
*/

int arr[3] = {1,2,3};
int (*ar4)[3] = &arr;
printf("ar+1 = %p\n",ar+1);

//降维/解引用
int arr[3][3] = {1,2,3,4,5,6,7,8,9};
int (*p)[4] = arr;
printf("%p\n", p);				//指向第一行
printf("%p\n", p+1);			//指向第二行

解引用示例:

#include <stdio.h>

int main() {
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    int (*p)[4] = arr;  // 行指针初始化

    // 访问第1行(row_ptr+1)的第2列(偏移2)
    printf("arr[1][2] = %d\n", *(*(p + 1) + 2));  // 输出:7
    // 等价写法:
    printf("arr[1][2] = %d\n", (*(p + 1))[2]);     // 输出:7
    printf("arr[1][2] = %d\n", p[1][2]);           // 输出:7

    return 0;
}

作业

1.对二维数组进行整体冒泡排序

/*===============================================
*   文件名称:work1.c
*   创 建 者:青木莲华     
*   创建日期:2025年07月17日
*   描    述:对二维数组进行整体冒泡排序
================================================*/
#include <stdio.h>

int main(int argc, char *argv[])
{ 
    int arr[3][4] = {0};
    int (*p)[4] = arr;
    int temp = 0;
    //输入数据
    printf(">:");
    for(int i = 0 ; i < 3 ; i++)
    {
        for(int j = 0 ; j < 4 ; j++)
        {
            scanf("%d",&arr[i][j]);
            printf("%-3d",arr[i][j]);
        }
        printf("\n");
    }
    //冒泡排序
    for(int i = 0 ; i < 11 ; i++)
    {
        //printf("%d  ",*(*p + i));
        for(int j = 0 ; j < 11 - i ; j++)
        {
            if(*(*p + j) > *(*p + j + 1))
            {
                temp = *(*p + j);
                *(*p + j) = *(*p + j + 1);
                *(*p + j + 1) = temp;
            }
        }
    }
    printf("------sorted------\n");
    for(int i = 0 ; i < 12 ; i++)
    {
        printf("%-4d",*(*p + i));
    }
    printf("\n");
    return 0;
} 

运行截图:

alt

2.字符串逆序输出

/*===============================================
*   文件名称:work2.c
*   创 建 者:青木莲华     
*   创建日期:2025年07月17日
*   描    述:字符串逆序输出
================================================*/
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{ 
    char *str = malloc(32);
    printf(">:");
    gets(str);
    //获取字符串长度
    long len = strlen(str);
    printf("exchange:\n");
    //循环逆序
    while(len > 0)
    {
        printf("%c",*(str + len - 1));
        len--;
    }
    printf("\n");
    free(str);
    return 0;
} 

运行截图:

alt

3.利用行指针,找出二维数组的最大元素以及下标

/*===============================================
*   文件名称:work3.c
*   创 建 者:青木莲华     
*   创建日期:2025年07月17日
*   描    述:利用行指针找出二维数组中
*             最大的元素以及其下标
================================================*/
#include <stdio.h>

int main(int argc, char *argv[])
{ 
    int arr[3][4] = {0};
    int (*p)[4] = arr;
    printf(">:");
    for(int i = 0 ; i < 12 ; i++)
    {
        scanf("%d",*p + i);
    }
    int max = *(*p + 1),index = 0;

    for(int i = 1 ; i < 12 ; i++)
    {
        if(max < *(*p + i))
        {
            max = *(*p + i);
            index = i;
        }
    }
    printf("The max_num is : %d\n",max);
    printf("It's index is :  %d\n",index);
    return 0;
} 

运行截图:

alt

4.判断一个字符串是否是一个回文字符串

/*===============================================
*   文件名称:work4.c
*   创 建 者:青木莲华
*   创建日期:2025年07月17日
*   描    述:判断字符串是否是回文字符串
================================================*/
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{ 
    

    char str1[32] = {0};
    char str2[32] = {0};
    printf(">:");
    gets(str1);  
    char *p = str1;
    char *p2 = str2;
    //判断长度
    long len = strlen(str1);
    //判断字符串长度
    if(len == 0) 
        return -1;
    //逆序输出构造回文字符串
    while(len > 0)
    {
        *p2 = *(p + len - 1);
        p2++;
        len--;
    }
    if(strcmp(str1,str2) == 0) 
        printf("<%s> 是回文字符串。\n",str1);
    else
        printf("<%s> 不是回文字符串。\n",str1);
    return 0;
}

运行截图:

alt

5.实现矩阵旋转(顺时针)

1	2	3	4
5	6	7	8
9	10	11	12
13	14	15	16
   
13	9	5	1
14	10	6	2
15	11	7	3
16	12	8	4
/*===============================================
*   文件名称:work5.c
*   创 建 者:青木莲华     
*   创建日期:2025年07月17日
*   描    述:矩阵旋转(顺时针90度)
================================================*/
#include <stdio.h>

int main(int argc, char *argv[])
{ 
    int arr[4][4] = {{1,2,3,4},
                     {5,6,7,8},
                     {9,10,11,12},
                     {13,14,15,16}};
    int arr2[4][4] = {0};
    printf("before:\n");
    for(int i = 0 ; i < 4 ; i++)
    {
        for(int j = 0 ; j < 4 ; j++)
            printf("%-4d",arr[i][j]);
        printf("\n");
    }
    printf("after:\n");
    //转置
    for(int i = 0 ; i < 4 ; i++)
    {
        for(int j = 0 ; j < 4 ; j++)
        {
            arr2[i][j] = arr[3-j][i]; 
            printf("%-4d",arr2[i][j]);
        }
        printf("\n");
    }
    return 0;
} 

运行截图:

alt

6.删除字符串中的重复字符

/*===============================================
*   文件名称:work6.c
*   创 建 者:青木莲华     
*   创建日期:2025年07月17日
*   描    述:删除字符串中的重复字符
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
    //利用一个数组来存储
    //字符出现第一次的位置
    //字符的ASCII码值就是下标
    int arr[128] = {0};
    char *str = malloc(32);
    int index = 1,asc = 0;
    printf(">: ");
    gets(str);
    //记录字符串的所有字符的ASCII码状态
    do
    {
        asc = *str;
        //判断是否出现过
        if(arr[asc] == 0)
            arr[asc] = index;
        index++;
        str++;
    }while(*str != '\0');
    /*
    for(int i = 1 ; i < 128 ; i++)
        printf("ascii: %c | %d\n", i ,arr[i]);
    */
    printf("去重后: ");
    //j 用来表示ascii码 , i 表示出现的位置(从1开始)
    for(int i = 1 ; i < 32 + 1 ; i++)
    {
        for(int j = 0 ; j < 128 ; j++)
        {
            if(i == arr[j])
                printf("%c",j);
        }
    }
    printf("\n");
    return 0;
} 

运行截图:

alt