数组:
**作用:**存储数据
概念:
同一种类型元素连续存储在同一块空间中
姿势:
存储的类型
数组名字[元素个数]:
例子:
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 *
* 要求: 存入10个 char [5]
char arr[10][5] ;
//类型 char *
* 姿势:
存储的基本类型 变量名字[元素个数][每个元素基本类型个数];
short arr[3][4];
// 解析;
变量名字为 arr
变量类型: short [3][4]
含有 3 个 short [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 函数来注册信号处理函数。