C语言数组 指针

数组:

**作用:**存储数据

概念:
同一种类型元素连续存储在同一块空间中
姿势:
存储的类型
数组名字[元素个数]:
例子:

int arr[8] = {1,2,3,4,5,6,7,8};
arr : 一体两面:
    arr : 数组类型: int [8]
    arr :  指针类型:  int * 
    //在取arr里面值的时候,使用指针类型,其他是数组类型

​ 数组名字:数组的名字就是数组的首地址
​ 元素个数:空间里面可以存放的数据个数
​ 存储的类型:空间里面允许存放的数据的类型
定义变量: 类行 变量名字;

short arr[10];
/*在内存中占用20个字节空间,该空间取名为àrr
理解:变量名字为arr   类型为short[10]
short[10] :   数组类型
这样理解:short[10]  arr;但是没有这种写法*/

int arrs[20];
int a,brr[10],crr[20],p,x;
//int a;int brr[10];int crr[20];int p;int x;

赋值:

short arr[10];/
如果数组空间己经定义好了,那么有且只有一种方式可以存储数据(通过下标的方式存储)
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
...
arr[9] = 90;

初始化:

未完全初始化

short arr[8] = {1};//下标为0的位置给了1其他全部为0
short arr[8] = {0};//清0的效果
short arr[8] = 1,2,3);//下标为0:1下标为1:2下标为2:3其他全部为0
short arr[8]= {[3]=10,[5]=20};//下标为3:10下标为5:20其他全部为0

完全初始化

short arr[8] = {1,2,3,4,5,6,7,8};
short arr[] = {1,2,3,4,5,6,7,8};
//练习:
	short arr[8] = {2,3,2,6,4,1,8,7}; 
	显示出里面的最大值  
        int main(void)
    {
        int i;
        int arr[8], max;

        for(i = 0; i < 8; i++)
        {
            scanf("%d",&arr[i]);
        }

        max = arr[0];

        for(i = 1; i < 8; i++)
        {
            if(max < arr[i])
                max = arr[i];	
        }

        printf("%d\n", max);

        return 0;
    }

指针:

概念:

​ 就是地址

姿势:

​ 指向的空间的类型 * 变量名字;

例子:

			int *p;	//变量名字: p
                   	//指向 int 类型 的
					//p 占几个字节???	32位4个字节
					//					  64位8个字节

定义指针变量:

int *p;
char *pp;
short *ppp;
int *x,*y,z,k,*t;
//int *x;int *y;int z;int k;int *t;
//在内存中占用的内存空间一样

赋值:

	int *p;   //称:  p 为野指针
                     //野指针:不能做解引用操作、
    p = NULL;	//NULL:地址:0x00

大小端序:

​ 小端序:低位的数据保存在低位的地址上
​ 高位的数据保存在高位的地址上
​ 大端序:高位的数据保存在低位的地址上
​ 低位的数据保存在高位的地址上

指针的两个操作:
1 取地址 &
取某一块空间的地址
2 解引用 *
知道某个地址取到它指向的空间
对于地址:使用%p 打印 ,可以打印出地址值

​ 3 指针和整型的运算:
​ int * + n; // 跳过 n个 int

二级指针 :
	int a = 10;
	int *p = &a;    //一级指针 
	int **pp = &p;  //二级指针
	int ***ppp = &pp; //三级指针 
	.....// 多级指针 
	
	*ppp  // pp 
	**ppp  // p
 	***ppp // a

指针和数组之间:

short arr[8]=1,2,3,4,5,6,78
arr://变量名字arr,类型:short[8]
arr://数组的名字就是数组的首地址,将数组作为一维数组来看待,其所谓的第一个元素的地址,就是首地址,short*类型
//如果你要取arr里面保存的值的时候,当指针类型使用,其他当数组类型使用
sizeof(arr);		//16
sizeof(arr+1);		// 32位4   64位8

sizeof(arr)://通过该变量名字取得它的类型,然后通过类型知道其字节数		16
sizeof(arr+1);			arr short *
short * + 1 ===>  //还是short *类型 //8
&arr + 1 ----->   short(*)[8] + 1
//字符串赋值
char arr[20]"i love u";
char *p="i love u";//I常量区

指针数组

//定义数组  数据类型  数据名[元素个数] ={值1,值2}
	//定义指针数组
	int a = 10;
	int b = 20;
	int c = 30;
	int* arr[3] = { &a,&b,&c };

多级指针

    int a[] = { 1,2,3 };
	int b[] = { 4,5,6 };
	int c[] = { 7,8,9 };
	//指针数组是一个特殊的二维数组模型
	//指针数组对应于二级指针
	int* arr[] = { a,b,c };

	//指针数组和二级指针建立关系
	int** p = arr;

	//arr[0][0] a[0]
	//printf("%d\n", **p);
	//二级指针加偏移量 相当于跳过了一个一维数组大小
	//printf("%d\n", **(p + 1));
	//一级指针加偏移量 相当于跳过了一个元素
	//printf("%d\n", *(*p + 1));//arr[0][1]

	//printf("%d\n", *(*(p + 1) + 1));

指针和函数

//指针作为函数参数
void swap(int* a, int* b)
{
	int temp = *a;
	*a = *b; 
	*b = temp;
}
int main05()
{
	int a = 10;
	int b = 20;
	//值传递  形参不影响实参的值
	//swap(a, b);

	//地址传递  形参可以改变实参的值
	swap(&a, &b);
	printf("%d\n", a);
	printf("%d\n", b);
	return EXIT_SUCCESS;
}

数组名作为函数参数

void remove_space(char* ch)
{
	//用来遍历字符串
	char* ftemp = ch;
	//记录非空格字符串
	char* rtemp = ch;
	while (*ftemp)
	{
		if (*ftemp != ' ')
		{
			*rtemp = *ftemp;
			rtemp++;
		}
		ftemp++;
	}
	*rtemp = 0;
}
int main0602(void)
{
	char ch[] = "   h   e    ll  o  w o   r lld    ";

	remove_space(ch);
	printf("%s\n", ch);
	return 0;
}

指针作为函数返回值

char* my_strchr(char* str, char ch)
{
	while (*str)
	{
		if (*str == ch)
			return str;
		str++;
	}
	return NULL;
}
int main07()
{
	char str[] = "hell worldll";
	char* p = my_strchr(str, 'o');
	if (p == NULL)
	{
		printf("未找到\n");
	}
	else
	{
		printf("%s\n", p);
	}
	return EXIT_SUCCESS;
}

例子:

//字符串查找字符串
char* my_strstr(char* src, char* dest)
{
	char* fsrc = src;//用作于循环遍历的指针
	char* rsrc = src;//记录每次相同的首地址

	char* tdest = dest;
	while (*fsrc)
	{
		rsrc = fsrc;
		while (*fsrc == *tdest && *fsrc != '\0')
		{
			fsrc++;
			tdest++;
		}
		if (*tdest == '\0')
		{
			return rsrc;
		}
		//回滚
		fsrc = rsrc;
		tdest = dest;
		fsrc++;
	}
	return NULL;
}
int main()
{
	char src[] = "llllhelle worldllo";
	char dest[] = "llm";

	char* p = my_strstr(src, dest);
	printf("%s\n", p);
	return EXIT_SUCCESS;
}

字符数组:

字符串常量:
	"abcd"  // 'a'  'b'  'c'  'd'  '\0'
	
字符数组:
	*姿势:
		char 变量名字[元素个数];
	
	*定义
		char arr[5];
		
	*赋值:
		arr[0] = 'a';
		arr[1] = 'b';
		
	*初始化:
		非完全初始化:
			char arr[10] = {'a','b'};
			char arr[10] = {0};
			char arr[10] = "abc"; // 'a' 'b' 'c' '\0'
			char arr[10] = {[3]='a'};
			
		完全初始化:	
			char arr[5] = {'a','b','c','d','e'};
			char arr[5] = "abcd";
			char arr[] = "abcd";
 		
字符串变量:
	没有特定的字符串变量:
		字符数组和字符指针来表示
		字符数组 : char arr[20] = "i love u";
		字符指针 : char *p = "i love u"; //常量区
作为形参: char *char []  是一个意思 
	* 字符串的打印:
		printf("%s","abc"); //碰到'\0'就结束
		
		char arr[10] = "abc";
		printf("%s\n",arr);  
		
======
字符串的几个函数:
	strlen  strcpy  strcmp  strcat  bzero  memset 
//取别名:
	姿势:
		typedef  类型 别名;
		*将 类型 取一个别名 
	例子:
		typedef int a;   // 多了个类型 a 类型 
		a b;  // int b;

typedef  和  宏定义的区别:
* #define a int   // 
* typedef int b;  //
a x,y,z; // int x,y,z;  int x; int y; int z;
b z,y,z; // b x; b y; b z;  // int x; int y; int z;

* #define a int *    
* typedef int * b; 
a x,y,z;  //  int * x,y,z;   // int *x; int y; int z;
b z,y,z;  // b x; b y; b z;  // int *x; int *y; int *z; 

const和volatile:

const : 不能以某个方式改变的意思 
    const int a = 10;  // const 修饰a 那么就不能使用 a = 这种方式对 a空间 进行修改
	a = 20;  // 有问题 
int b = 20;
const int *p = &b;  // const 修饰 *p 那么我们就不能使用 *p = 这种方式 对数据修改 
*p = 30;  // 有问题 

int x = 20;
int y = 30;
int * const p = &x; // const 修饰 p ,那么我们不能使用 p = 这样方式修改数据
p = &y; // 有问题

int z = 10;
int k = 20;
const int * const pppp = &z;
pppp = &k;  //有问题
*pppp = 50; //有问题 
======================================================
volatile : 波动的(容易改变的)

int a = 10; // 在内存中占用4个字节空间,该空间命名为a,并且将10存入该空间 
int b = a; // 将内存中a的值取出来 放入CPU寄存器中,将其赋值给b
int c = a; //将cpu寄存器中的a直接存入c中    
int d = a; //将cpu寄存器中的a直接存入d中 
int x = a; //将cpu寄存器中的a直接存入x中 

===========
    有可能在执行的过程中,通过手段将内存中的a发生改变
    为了避免数据不一致,需要每一次取数据都去内存中读取,避免CPU优化,在变量前面加 volatile 
========volatile int a = 10;
int b = a; //将内存中a的值取出来 放入CPU寄存器中,将其赋值给b
int c = a; //将内存中a的值取出来 放入CPU寄存器中,将其赋值给c
int d = a; //将内存中a的值取出来 放入CPU寄存器中,将其赋值给d	
int x = a; //将内存中a的值取出来 放入CPU寄存器中,将其赋值给x 

CPU优化 

二维数组:

二维数组:
    * 一维数组:
    存储的类型 变量名字[元素个数];
	int arr[10];  // 
    //类型 int *
* 要求: 存入10char [5]
    char arr[10][5] ;
   //类型 char *
* 姿势:
    存储的基本类型  变量名字[元素个数][每个元素基本类型个数];
	short arr[3][4];

// 解析; 
变量名字为 arr 
    变量类型: short [3][4]
    含有 3short [4]
=====================================================
变长数组:
* attention: 
 数组一旦定义好,就不允许改变大小 
    char arr[20]; // 
	arr =  // arr的值不允许改变 。顾arr= 是错误的	
===============
unsigned int n;
scanf("%u",&n); 200  0
char arr[n]; // 变长数组不允许 初始化 
int a = 10;
char arr[a];

#define N 100
char arr[N]; //这不是变长数组
======================================================
二维数组:
    * 存储类型 变量名字[元素个数];
	int arr[5];  // 可以存放 5个 int  
	* 假设数组中存入  char [3]
    char [3]  arr[5];// 可以存放 5个 char [3],但是我们没有这样的写法
		-==写成: char arr[5][3];  // 二维数组
//类型 是 char (*)[3],是数组指针
//char (*p2)[3] = arr;   p2指向arr中的第0个一维数组 arr[0]
//p2++;   数组指针自增则指向下一个数组,指向 arr 中的第 1 个一维数组 arr[1]
//二维数组中一维数组的存储是连续的

//通过数组折针访问数组的方法
//arr[2] 等价于 *(arr+2)
//*p2 的类型是 int *
(*p2)[0] = 1;// (*p2)[0]等价于 *((*p2)+0),也等价于p2[0][0]
printf("*((*p2)+0)=%d\n",*(p2)+0));
printf("p2[0][0]=%d\n",p2[o][01]):

* 二维数组姿势:
    基本存储类型 变量名字[一维数组个数][每个一维数组中基本存储类型的个数];
short arr[4][5];  // 有4个 short [5]

* 定义变量;
    short arr[4][5];
	short a,arr[3][4],brr[5][6];

* 赋值:
    short arr[4][5];
	arr[0][0] = 10;
	arr[0][1] = 20;

* 初始化:
    不完全初始化:
    short arr[4][5] = {1,2}; //arr[0][0]  arr[0][1] 分别赋值为 1  2  其他全部为0
	short arr[4][5] = {{1},{2},{3},{4,5}};
	short arr[4][5] = {[0][3]=10,[2][4]=20};
完全初始化:
    short arr[2][3] = {1,2,3,4,5,6};
	short arr[][3] = {1,2,3,4,5,6,7,8,9,10,11,12};
=========================================================
多维数组 :
	* 本质: 一切的数组的本质都是一维数组,都可以将其看成是一维数组 
	* 二维数组:	
		int arr[3][4];  // 由 3个 int [4] 组成 , 每一个int [4] 由 4个 int 组成   
		
	* 三维数组:
		int arr[2][3][4];
			// 由 2 个 int [3][4]组成 
			// 每一个 int [3][4] 由 3 个 int [4]组成 
			// 每一个 int [4] 由 4 个int 组成 
		
	* 多维数组:
		short arr[2][3][4][5][6][7][8][9];
=========================================================
深入理解例子:
    int arr2[2][3]
类型:
    arr2          int (*)[3]
    &arr2         int (*)[2][3]
    * arr2        int *
    arr2[0]       int *
    arr2[0][0]    int
地址计算
    arr2 + 1          地址增加   12字节
    &arr2 + 1         地址增加   24字节
    * arr2 + 1        地址增加   4字节
    arr2[0] + 1       地址增加   4字节

函数指针:

函数指针:指向函数的指针
函数名是一个指针
=========================================================
例子:
 void (* signal(int signum, void (*handler)(int)) )(int);
    这是一个函数原型的声明,用于在 C 语言中注册信号处理函数。
解释该函数原型:
函数名:signal
参数:
signum:整数类型,表示要注册信号处理函数的信号编号。
handler:指向函数的指针,该函数接受一个整数参数并返回 void(即无返回值);它是用于处理信号的自定义处理函数。
返回值:
void (*)(int):表示该函数返回一个指向接受整数参数并返回 void 的函数指针,即信号处理函数的函数指针。
换句话说,signal 函数用于为特定信号编号注册自定义的信号处理函数。函数原型表明 signal 函数接受两个参数:一个表示信号编号的整数和一个指向信号处理函数的函数指针。它返回一个函数指针,该函数指针指向一个接受整数参数并返回 void 的函数,这个函数将作为信号的处理函数来执行。
使用这个函数原型,可以注册自定义的信号处理函数,以便在程序接收到相应的信号时执行特定的操作。

void (* signal(int signum, void (*handler)(int)) )(int);解释并用typedfy简化

这是一个函数原型的声明,用于在 C 语言中注册信号处理函数。
使用 typedef 可以简化该函数原型的复杂性,使其更易读和理解。下面是对该函数原型进行简化的示例:
        
typedef void (*SignalHandler)(int);

SignalHandler signal(int signum, SignalHandler handler);

在上述示例中,我们使用 typedef 创建了一个名为 SignalHandler 的函数指针类型,该函数指针接受一个整数参数并返回 void。然后,我们将 signal 函数的参数和返回类型重命名为 SignalHandler,以使用这个简化后的类型。

这样修改后的函数原型更加清晰易读。现在,signal 函数接受两个参数:一个表示信号编号的整数和一个 SignalHandler 类型的函数指针,它用于指定信号的处理函数。该函数返回一个 SignalHandler 类型的函数指针,即所注册的信号处理函数。

使用简化后的版本,我们可以更容易地理解和使用 signal 函数来注册信号处理函数。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值