c语言进阶

debug和releas版本会优化运行(优化栈空间都使用)

数据的存储

数据类型介绍

内置类型  char  1

                short 2

                int 4

                long 4/8

                longlong 8

                float 4

                double 8

类型基本归类

整型

默认是有符号;无符号数用于无负数值没有符号位;

char存的ascll值

浮点型

自定义类型(构造类型)

指针类型

空类型

1没用;

整型在内存中的存储

以二进制的补码存储0b                //cpu只能加补码运算可以统一归为加法;补码和源码的转化步骤是相同的(不需要逆运算);

正数原反补相同

负数通过计算得到反补;

                原码符号位不变其他位取反;

                补码反码加一;

注:编译器内存查看时以16进制展示

 大端字节序存储       高位数放在低位地址;低位字节放在高位;

小端字节序存储        低位数放低位地址;高位放高位地址;

练习

运行结果

----------------------------------------------------------

运行结果

    %d-128

-------------------------------------------------------------

运行结果

-------------------------------------------------------

结果

补码相加;

-10

--------------------------------------------------------

-------------------------------------------------------------------

255

------------------------------------------------------------------

死循环

------------------------------------------------------------------

>

<

------------------------------------------------------------------------------

 浮点数的存储;

//everything中可以看  。h

//说明整型和浮点型存储方式不一样;

//第一个*pfloat为什么是0;

//第二个n为什么输出1091…………;

存储方式

eg

2^2类似10进制小数点往前移一个就乘10;

eg2

0.1    2进制就是0.5了;

eg3

小数和浮点数可能无法精确保存;因为后面是1/(2^n);会出现位数不够用情况;

比如0.6的二进制是无限的;

m部分1.……的1省略读的时候加上;

E是一个无符号整数;因为E实际有负数;所以存储时  加127(float中间值)或(double中间值)1024得到值再存

5.5存40 b0 00 00;

e不为全1或全0取的时候逆运算;

e为全0;

e为全1;

指针

字符指针

不是把abcdef存在p里;

abcdef是一个常量字符串;所以两个p一样的

指针数组和数组指针

模拟二维

因为在C语言中,数组名p代表数组的首地址,而p+i代表数组首地址偏移i个元素的地址,所以*(p+i)表示数组中第i个元素的值,而p[i]也表示数组中第i个元素的值,因此*(p+i)和p[i]是等价的

 数组指针存放数组的地址;//去掉名字就是类型;

指针数组存储的是一组指针,每个元素都是一个指针。

整型指针存放整型的地址;

字符指针存放字符的地址;

 在C语言中,指针数组和数组指针有以下主要区别:

1. 定义方式不同:

指针数组:

类型 *数组名[大小];

如:

int *ptr[5]; 

数组指针:

类型 (*数组名)[大小];

如: 

int (*parr)[5];

2. 存储不同:

指针数组存储的是一组指针,每个元素都是一个指针。

数组指针指向一个整个数组,它指向的是一个整块连续的内存。

3. 使用不同:

指针数组可以通过[]访问每个元素(指针),然后通过*访问指针指向的内容。

数组指针通过[]访问整个数组中的每个元素。

4. 应用不同:

指针数组常用于将多个不同对象的地址存放在一起,便于以数组形式操作。

数组指针常用于需要传递整个数组的场合,如函数参数。

总之,指针数组每个元素是指针,数组指针指向的是一个整个数组。指针数组可以看作是一个指针的集合,数组指针看作是一个指向整个数组的指针。

数组名的理解;

数组名通常表示首元素地址;

两个例外

1.sizeof(数组名)这里是计算整个数组

2.&数组名;表示的是整个数组;//在值上和首位一样但是运算时比如加一的时候结果不一样;

数组指针格式      赋值变量数组类型(*自定义变量)[个数]=&赋值变量数组

 对,在数组指针的定义中,*就是解引用运算符。

具体来说:

类型 (*数组名)[大小];

这里*的作用是指明数组名是一个指针,它指向的是数组类型。

举个例子:

int (*parr)[5];

这里:

int 是数组元素的类型
(*parr) 表示parr是一个指针
[5] 表示它指向的是一个包含5个int类型元素的数组

所以*的作用就是将数组名声明为指针,让它可以指向整个数组。

当使用数组指针访问数组元素时,是通过数组下标来访问的,比如:

parr[0][1] 

这里parr作为指针,通过下标0访问到了第一个数组,然后再通过下标1访问到这个数组的第二个元素。

所以总结来说,*在数组指针定义中表示数组名是个指针,它指向整个数组,通过*可以对整个数组进行操作,如通过下标访问元素。

//arr类 型是int    p2的类型是 int(*)[10];

//arr类型是char*

数组指针的使用

1。

//*(p+i)=p[i]

用于二维数组,

常规方法//但是arr传递的应该是一个首行地址(二维)

正确用法

//p+i跳过一行因为p是一个数组地址;*(*(p+i)+j)=*(p[i]+j)=p[i][j];

p是数组*p是首地址;p+1是跳过一行,*p+1是下一个;

int(*parr2)[10]的意思是:

定义了一个指向大小为10的int数组的指针。

具体来说:

- parr2是一个指针,指向一个大小为10的int数组。

所以整体来说,int(*parr2)[10]声明了:

一个指向大小为10的int数组的指针。

简单来说,它声明了一个指向大小为10的一维int数组的指针。

 int(*parr3[10])[5]的意思是:

定义一个名为parr3的指针数组,这个指针数组有10个元素。

每个元素都是一个指向int数组的指针。

所指向的int数组大小为5个元素。

即:

- parr3是一个指针数组,有10个元素
- 每个元素parr3[i]都是一个指向int数组的指针
- 所指向的int数组大小为5个int类型元素

所以整体来说,int(*parr3[10])[5]定义了:

一个大小为10的指针数组parr3
每个parr3元素都是一个指向大小为5的int数组的指针

等同于定义了一个10x5的二维数组,但是使用指针的方式实现,而不是直接定义二维数组。

每个parr3元素都可以指向一个独立的1维int数组,1维int数组大小都是5。

所以这个声明定义了一个包含10个大小为5的int数组的指针数组

数组参数和指针参数

一维数组

int test2(int *arr[])     int test2(int *arr[20])     int **arr;//都可以

因为 arr2是指针数组存放int*     而**arr是指向一级指针的二级指针

二维数组
 void test(int arr[3][5]) //正确。这是传递一个3行5列的二维数组。
 void test(int arr[][]) // 不正确。在C/C++中,函数参数中不能省略数组的第一个维度,因此这种方式是不正确的。
 void test(int arr[][5]) // 正确。这是传递一个包含5列的二维数组,但没有指定行数。
 void test(int *arr)// 不正确。这是传递一个一维数组的指针,而不是二维数组。
 void test(int* arr[5]) // 不正确。这是传递一个指向包含5个指针的数组的指针,而不是二维数组。
 void test(int (*arr)[5]) // 正确。这是传递一个指向包含5列的二维数组的指针。
 void test(int **arr) // 不正确。这是传递一个指向指针的指针,而不是二维数组。


int main()
{
 int arr[3][5]={0};
 test(arr);
}

在这段代码中,`void test(int(*arr)[5])` 中的 `int(*arr)[5]` 表示一个指向包含 5 个整数的一维数组的指针。

当你调用 `test(arr)` 时,你将一个二维数组 `arr[3][5]` 传递给了 `test` 函数。在 C 语言中,二维数组可以被看作是一系列的一维数组。因此,`arr` 在这里会被隐式转换为指向包含 5 个整数的一维数组的指针,这和 `int(*arr)[5]` 的声明是匹配的。

现在来回答你的问题:为什么 `int(*arr)[5]` 表示包含二维数组的指针,而 `int(*parr2)[10]` 表示包含一维数组的指针呢?

其实,无论是 `int(*arr)[5]` 还是 `int(*parr2)[10]`,它们都表示指向一维数组的指针。在 C 语言中,二维数组可以被看作是一系列的一维数组,因此指向二维数组的指针实际上是指向一维数组的指针。这可能是导致你感到困惑的原因。

总之,`int(*arr)[5]` 表示指向包含 5 个整数的一维数组的指针,而 `int(*parr2)[10]` 也表示指向包含 10 个整数的一维数组的指针。这两者都是指向一维数组的指针。

小结

一级指针传参

二级指针传参

函数指针

int (*pf)(int,int)=&add;存储;*可以省略可以多写(没用)

int ret=(*pf)(2,3);使用;

//(*PF)("abc")里的*可以不写;

是一个函数指针,它接受一个没有参数的函数指针作为参数,并返回一个指向函数的指针。

typedef void (*VoidFunctionPointer)();

VoidFunctionPointer func(VoidFunctionPointer ptr);
ai//回答

typedef void (*SignalHandler)(int);//重命名void(*)(int)为singnal

SignalHandler signal(int signum, SignalHandler handler);
//ai回答

是一个函数指针signal,它接受两个参数:一个int类型的参数和一个函数指针,该函数指针接受一个int类型的参数并返回void。该函数指针signal返回一个指向函数的指针,该函数接受一个int类型的参数并返回void。

函数指针数组(转移表)

参数相同;函数返回类型相同;

指向函数指针数组的指针;

解释一下这段代码:

1. `pfarr` 是一个函数指针数组,表示一个指向函数的指针的数组。
2. `int(*(*ppfarr)[5])(int,int)` 定义了一个指针 `ppfarr`,指向一个包含5个元素的函数指针数组,每个函数指针都接受两个int类型的参数并返回一个int类型的值。
3. `&pfarr` 将函数指针数组 `pfarr` 的地址赋值给指针 `ppfarr`。

现在我们来详细分析 `int(*(*ppfarr)[5])(int,int)` 这个类型的定义:

- `int` 表示返回值的类型是int。
- `*(*ppfarr)[5]` 表示一个指向函数指针数组的指针,该指针指向一个包含5个元素的函数指针数组。
- `(int,int)` 表示函数指针的参数类型是int和int。

因此,整个定义表示 `ppfarr` 是一个指针,指向一个包含5个元素的函数指针数组,每个函数指针都接受两个int类型的参数并返回一个int类型的值。

最后,`&pfarr` 将函数指针数组 `pfarr` 的地址赋值给指针 `ppfarr`,使得 `ppfarr` 指向 `pfarr`。

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

eg:

//qsort -这个函数可以排序任意类型的数据
//

//void qsort(void* base,//你要排序的数据的起始位置
//	       size_t num,//待排序的数据元素的个数
//	       size_t width,//待排序的数据元素的大小(单位是字节)
//	       int(* cmp)(const void* e1, const void* e2)//函数指针-比较函数
//          );

//__cdecl - 函数调用约定    程序里搜关键字qsort

#include <stdlib.h>

//比较2个整型元素
//e1指向一个整数
//e2指向另外一个整数

int cmp_int(const void* e1, const void* e2)//void类型可以给任意一种但是不能解引用±操作;
{
	return (*(int*)e1 - *(int*)e2);
}

int main()
{
	//int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	//0 1 2 3 4 5 6 7 8 9
	//把数组排成升序
	int sz = sizeof(arr) / sizeof(arr[0]);
	//bubble_sort(arr, sz);

	qsort(arr, sz, sizeof(arr[0]), cmp_int);

	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
用qsort比较非int类型

struct Stu
{
	char name[20];
	int age;
};

//abbdef
//abbqwerttt
//从头开始往后比直到分出大小;
int cmp_stu_by_name(const void* e1, const void* e2)
{
	//strcmp --> >0 ==0 <0
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
//strcmp用于比较字符串大小;e1是void型所以要强制转化;
}

int cmp_stu_by_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

int main()
{
	//测试使用qsort来排序结构数据
	struct Stu s[] = { {"zhangsan", 15}, {"lisi", 30}, {"wangwu", 25} };
	int sz = sizeof(s) / sizeof(s[0]);
	//qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);//名字排序
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);//年龄排序
    for (int i = 0;i < 3;i++)
	{
		printf("name:%s\nage:%d\n\n", s[i].name, s[i].age);
	}
}

自定义一个qsort

struct Stu
{
	char name[20];
	int age;
};
void Swap(char*buf1, char* buf2, int width)//因为不知道实际比较的类型是什么所以就按字节一个字节一个字节的交换以需要交换的宽度为上线
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void*base, int sz, int width, int(*cmp)(const void*e1, const void*e2))//里面传递的值和库函数qsort一样
{
	int i = 0;
	//趟数
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;//假设数组是排好序
		//一趟冒泡排序的过程
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)base+j*width, (char*)base+(j+1)*width)>0)//强制转化为最小的单位char  1  乘实际需要交换类型的宽度得到每次往后需要跳过多少字节能到下一位;因为base是void类型所以只能这样变相的运算;
			{
				//交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);//因为不知道实际运用是什么类型所以就以最小单位变相得到实际的大小;这里加的宽度是为了知道进行到哪里了;以防止越过了内容;
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

//abbdef
//abbqwerttt
//
int cmp_stu_by_name(const void* e1, const void* e2)
{
	//strcmp --> >0 ==0 <0
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

int cmp_stu_by_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}


//
void test3()//测试使用bubble_sort
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	//0 1 2 3 4 5 6 7 8 9
	//把数组排成升序
	int sz = sizeof(arr) / sizeof(arr[0]);
	//bubble_sort(arr, sz);

	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);

	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
void test4()
{
	//测试使用bubble_sort来排序结构数据
	struct Stu s[] = { {"zhangsan", 15}, {"lisi", 30}, {"wangwu", 25} };
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	//bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	for (int i = 0;i < 3;i++)
	{
		printf("name:%s\nage:%d\n\n", s[i].name, s[i].age);
	}
}

int main()
{
	//test1();
	//test2();
	//test3();
	test4();
	return 0;
}

 野指针;目标被销毁了;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值