C语言学习——4
一、指针
1.指针、地址、基地址
指针定义:用于存放地址的一个数据类型
地址定义:地址就是内存中每一个字节的编号,假设一个内存的大小为4G,他的地址会从0x 0000 0000 ~ 0x FFFF FFFF
基地址:变量所占字节中,地址编号最小的,作为变量的地址,也称为基地址
指针的作用
- C程序设计中使用指针可以:
- 使程序简洁、紧凑、高效
- 有效地表示复杂的数据结构
- 动态分配内存
- 得到多于一个的函数返回值
指针变量
用来存放变量地址的变量(指针变量也有地址,但是该变量里面存的值是所指目标的地址)
定义格式
<存储类型> <数据类型> <*><变量名>;
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++ ;
}
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;
}
运行截图:
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;
}
运行截图:
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;
}
运行截图:
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;
}
运行截图:
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;
}
运行截图:
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;
}
运行截图: