C语言指针进阶

【C语言】——指针进阶 两万字长文,总结C语言指针的知识点 阅读详情


在指针的初阶,我们已经理解了指针的概念:

  1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
  2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
  3. 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
  4. 指针的运算。

一、字符指针

在指针类型中我们知道有一种类型是:char* ,一般用法为

int main()
{
	char temp = 'a';//创建字符变量 temp
	char* pt = &temp;//将 temp 的地址存在指针变量 pt 中
	*pt = 'b';//通过对指针变量 pt 的解引用来实现访问(修改)
	
	return 0;
}

还有一种用法

int main()
{
    const char* pstr = "hello bit.";
    printf("%s\n",pstr);
    
    return 0;
}

这段代码输出什么呢?
在这里插入图片描述
很显然,输出 hello bit.
那么这里是把一个字符串放到pstr指针变量里了吗? 并不是,pstr 中存放的是一个地址,是字符串首字母的地址!
在这里插入图片描述

注: const char* pstr 意思是 pstr 只读(不能被修改),而指针变量 pstr 可以修改,也就是指向的地址可以修改,但是内容不能修改。
char
* const pstr 意思是 pstr 指向的地址不能修改,但是 *pstr 可以修改,也就是指向的地址不可修改,内容可修改。

我们来看一道面试题

#include <stdio.h>
int main()
{
    char str1[] = "hello bit.";		// (1)
    char str2[] = "hello bit.";		// (2)
    const char *str3 = "hello bit.";// (3)
    const char *str4 = "hello bit.";// (4)
    if(str1 ==str2)					// (5)
 printf("str1 and str2 are same\n");
    else
 printf("str1 and str2 are not same\n");
       
    if(str3 ==str4)					// (6)
 printf("str3 and str4 are same\n");
    else
 printf("str3 and str4 are not same\n");
       
    return 0;
}

该代码输出什么?

在这里插入图片描述
上述代码中(1)位置与(2)位置各创建了一个数组,数组的操作方式是将右边常量字符串的内容拷贝进来,所以实际上是在内存中开辟了两个空间,分别存放内容相同的字符串,在(5)处,str1 与 str2 是数组名,表示首元素地址,既然开辟了两个地址,那么首元素地址肯定是不相同的,结果是不相同。

上述代码中(3)位置与(4)位置是两个char* 类型指针变量,编译器在处理的时候,会将相同的常量字符串做成同一个地址,str3 和 str4都指向同一个位置,那么(6)位置的 str3 与 str4 是一样的,所以结果是相同。

二、指针数组

指针数组是存放指针的数组,例如

int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组

(存放的指针类型)数组名[数组大小];

三、数组指针

1.数组指针的定义

数组指针是指针?还是数组?
答案是:指针。

我们已经熟悉:
整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。

(1)一维数组指针的定义

下面代码哪个是(一维)数组指针?

int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?

我们先来看第一行代码,[ ] 的优先级比 * 高,所以 p1 先与 [ ] 结合,这里本质是一个数组,前面的int* 是存放的元素类型,所以这是一个指针数组,存放指针的数组。

第二行代码 (*p2) ,p2 先与 * 结合,说明 p2 是一个指针变量,指向的是一个大小为 10 个元素的数组,类型是 int (*) [10]。

注意:[ ]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

(2)二维数组指针的定义

我们明白了一维数组指针的定义,二维数组指针的定义其实也类似,例如

int (*p3) [3][5];
int* (*p4) [3][5];

第一行代码,p3 先与 * 结合,说明 p3 是一个指针变量,指向的是一个大小为 3 行 5 列的数组,数组中存放的元素是 int 类型,指针的类型为: int (*) [3][5]
第二行代码,p4 先与 * 结合,说明 p4 是一个指针变量,指向的是一个大小为 3 行 5 列的数组,数组中存放的元素是 int* 类型,指针的类型为:int* (*) [3][5]

2.&数组名 VS 数组名

数组名有时是首元素地址,有时是整个数组,那数组名到底是什么?

一般情况下数组名表示的是数组首元素的地址,有两种例外情况:
(1)sizeof(数组名),这里的数组名表示的是整个数组。
(2)&数组名 时取出的是整个数组的地址。

我们来看一段代码:

#include <stdio.h>
int main()
{
    int arr[10] = {0};
    printf("%p\n", arr);
    printf("%p\n", &arr);
    
    return 0;
}

在这里插入图片描述
我们发现,当以 %p 的形式打印 arr 和 &arr 时打印的地址是一样的,那么 arr 和 &arr 是不是表达的一个意思呢?
我们接下来对以上代码进行一点小小的修改

#include <stdio.h>
int main()
{
    int arr[10] = {0};
    printf("%p\n", arr + 1);
    printf("%p\n", &arr + 1);
    
    return 0;
}

在这里插入图片描述
这时我们发现打印出来的地址不同了,十六进制的 00B5F720 与 00B5F744 相差了 36 ,arr 中存放的元素是 int 类型,也就是相差了 36 个字节。由于 arr 是首元素地址,arr + 1 是第二个元素的地址;而 &arr 是取出了整个数组的地址,&arr + 1则是跳过了整个数组。我们可以画一张图来更直观的看一下
在这里插入图片描述

以上是一维数组,接下来我们再来看一下二维数组

int main()
{
	int arr[3][5] = {0};
	
	printf("%p\n", arr);
	printf("%p\n", &arr);

	printf("%p\n", arr + 1);
	printf("%p\n", &arr + 1);

	return 0;
}

在这里插入图片描述
由此可见,二维数组也是一样的, arr 表示的是首元素的地址,&arr表示的是整个数组的地址,在数值上,这两个值是相同的,但是意义上是不一样的。

那么 arr + 1 与 arr 为什么相差了 20 个字节?

其实,二维数组 &arr + 1还是跳过整个数组的地址;arr + 1 是跳过首元素的地址,只不过对于二维数组而言,首元素的地址是第一行的地址,所以 arr + 1 是第二行的首元素地址,也就是第二行的地址,所以 arr + 1 与 arr 相差了 20 个字节。

3.数组指针的使用

我们已经知道了数组指针的定义,那么数组指针是怎么使用的呢?

既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
    //但是我们一般很少这样写代码
    
    return 0;
}

我们再来看一段完整的实例代码

代码1

#include <stdio.h>

void my_print(int (*p)[5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ",*( * (p + i) + j));
		}
	}
}

int main()
{
	int arr[2][5] = { {1,2,3,4,5},{6,7,8,9,10} };
	my_print(arr,2,5);

	return 0;
}

在这里插入图片描述
这是一个二维数组传参问题,也是一个降维的例子。将二维数组数组名传给 my_print() 函数,其实是将二维数组的首元素传给了该函数,也就是将第一行的地址传了过去,也就是说可以理解成一个一维数组,此操作实现了降维。函数定义时形参要用数组指针接收,打印时先进行 p + i ,这是用来控制行数,控制打印第几行的数据, * (p + i)是拿到了二维数组第一行数组的数组名,数组名又是首元素地址,解引用就得到了第一行第一个元素的地址,* (p + i) + j 是得到打印同一行中第几个元素的地址,再对这个整体进行解引用操作就拿到了想打印的元素数值。

代码2

#include <stdio.h>

void my_print(int arr[][5], int row, int col)//
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ",arr[i][j] );
		}
	}
}

int main()
{
	int arr[2][5] = { {1,2,3,4,5},{6,7,8,9,10} };
	my_print(arr,2,5);

	return 0;
}

此段代码和之前的代码结果是相同的,都是打印二维数组中各个元素的值,只不过是语法的不同,为了让程序员能够更好的理解、更方便的使用。其实这样写,本质上还是代码 1 的意思,编译器会按照代码 1 来进行操作,还是用一个数组指针进行接收。

学完了数组指针,我们来回顾一下,下面代码是什么意思

int arr[5];	        // 5 个元素的 arr 数组,每个元素类型为 int
int *parr1[10];     // 10 个元素的 parr1 数组,每个元素类型为 int* ,为指针数组
int (*parr2)[10];   // 数组指针,parr2 指向大小为 10 个元素的数组,数组每个元素类型为 int
int* (*parr3[10])[5];// 指向指针数组的数组指针,本质上是指针

四、数组参数、指针参数

1.一维数组传参

以下传参是否ok?

#include <stdio.h>
void test(int arr[]) //ok,数组传进来数组接收(根据语法简单理解)
{}
void test(int arr[10])//ok,同上
{}
void test(int arr[100])//不建议,但是没错!
{}
void test(int *arr)//ok,数组首元素地址传进来用指针变量接收
{}
void test2(int *arr[20])//ok,指针数组传进来,指针数组接收
{}
void test2(int *arr[20])//ok
{}
void test2(int **arr)//传过去是首元素地址,首元素存放的是一个指针,用二级指针接收没问题
{}
int main()
{
 int arr[10] = {0};
 int *arr2[20] = {0};
 
 test(arr);
 test2(arr2);
}

2.二维数组传参

下列代码是否ok?

void test(int arr[3][5])//ok
{}
void test(int arr[][])//err
{}

void test(int arr[][5])//ok
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。

void test(int *arr)//err,数组地址传过来不能用普通指针变量接收
{}
void test(int* arr[5])//err,地址传过来不能放在一个数组中
{}
void test(int (*arr)[5])//二维数组首元素地址(第一行地址)传过来是一个数组的地址,用数组指针接收
{}
void test(int **arr)//err,二维数组首元素地址传过来是一个数组的地址,要用数组指针接收
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}

3.一级指针传参

一级指针传参,一级指针接收

#include <stdio.h>
void print(int *p, int sz)
{
 int i = 0;
 for(i=0; i<sz; i++)
 {
 printf("%d\n", *(p+i));
 }
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9};
 int *p = arr;
 int sz = sizeof(arr)/sizeof(arr[0]);
 //一级指针p,传给函数
 print(p, sz);
 return 0;
}

4.二级指针传参

二级指针传参,二级指针接收

#include <stdio.h>
void test(int** ptr)
{
 printf("num = %d\n", **ptr); //二级指针解引用
}
int main()
{
 int n = 10;
 int*p = &n;
 int **pp = &p;
 test(pp);
 test(&p);
 return 0;
}

五、函数指针

我们来看一段代码

#include <stdio.h>
void test()
{
 printf("hehe\n");
}
int main()
{
 printf("%p\n", test);
 printf("%p\n", &test);
 return 0;
}

在这里插入图片描述

此函数是输出两个地址,那么我们想要把这两个地址存储起来,应该怎么办呢?

void (*p1) () = test;
void (*p1) () = &test;
// (函数的返回类型) (* 指针变量名) (函数的参数)
// 该函数指针的类型为: void (*) ()

这两段代码都是一样的,都可以存储函数的地址,test 和 &test 都是函数的地址。

我们再来看一段代码

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 3;
	int b = 5;
	int (*p) (int, int) = Add;
	int ret1 = Add(a,b);
	int ret2 = (*p)(a, b);
	int ret3 = p(a, b);

	printf("%d\n",ret1);
	printf("%d\n", ret2);
	printf("%d\n", ret3);

	return 0;
}

在这里插入图片描述
既然test 和 &test 都是函数的地址,把 Add() 赋给指针变量 p ,那么 p 和 Add()就是等价的,这三种使用方法都是可以的,但是在定义时的 * 不可省略。

六、函数指针数组

指针数组是创建一个数组,里面元素全都是指针。
函数指针数组,就是创建一个数组,里面元素全是函数指针。

那函数指针的数组如何定义呢?

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
//哪个正确?

第一个正确。parr1 先和 [] 结合,说明 parr1是数组,是 int ( * ) ( ) 类型的函数指针。

我们知道函数指针数组的定义了,那么该如何使用呢?我们来看一段代码

#include <stdio.h>
int add(int a, int b)
{
 return a + b;
}
int sub(int a, int b)
{
 return a - b;
}
int mul(int a, int b)
{
 return a*b;
}
int div(int a, int b)
{
 return a / b;
}
int main()
{
 int x, y;
 int input = 1;
    int ret = 0;
    do
   {
        printf( "*************************\n" );
        printf( " 1:add           2:sub \n" );
        printf( " 3:mul           4:div \n" );
        printf( "*************************\n" );
        printf( "请选择:" );
        scanf( "%d", &input);
        switch (input)
    { 
       	case 1:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = add(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 2:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = sub(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 3:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = mul(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 4:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = div(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 0:
                printf("退出程序\n");
 breark;
        default:
              printf( "选择错误\n" );
              break;
       }
 } while (input);
    
    return 0;
}

在这里插入图片描述
可见我们使用循环与 switch 实现一个函数选择功能。但是这段代码非常冗余,重复的地方非常多,不够简便,接下来我们进行优化

#include <stdio.h>

int add(int a, int b)
{
    return a + b;
}
int sub(int a, int b)
{
    return a - b;
}
int mul(int a, int b)
{
    return a * b;
}
int div(int a, int b)
{
    return a / b;
}
int main()
{
    int x, y;
    int input = 1;
    int ret = 0;
    int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
while (input)
{
    printf("*************************\n");
    printf(" 1:add           2:sub \n");
    printf(" 3:mul           4:div \n");
    printf("*************************\n");
    printf("请选择:");
    scanf("%d", &input);
    if ((input <= 4 && input >= 1))
    {
        printf("输入操作数:");
        scanf("%d %d", &x, &y);
        ret = (*p[input])(x, y);
    }
    else
        printf("输入有误\n");
    printf("ret = %d\n", ret);
}
return 0;
}

在这里插入图片描述
可见,效果是一样的,大大节省了代码,这是函数指针数组的功能:转移表。

七、指向函数指针数组的指针

指向函数指针数组的指针是一个 指针。
指针指向一个数组 ,数组的元素都是函数指针。

那么我们该如何定义呢?

void test(const char* str)
{
 printf("%s\n", str);
}
int main()
{
 //函数指针pfun
 void (*pfun)(const char*) = test;
 
 //函数指针的数组pfunArr
 void (*pfunArr[5])(const char* str);
 pfunArr[0] = test;
 
 //指向函数指针数组pfunArr的指针ppfunArr
 void (*(*ppfunArr)[5])(const char*) = &pfunArr;
 
 return 0;
}

八、回调函数

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

我们来看一下库函数 qsort 函数
在这里插入图片描述
下面我们写一段代码

#include <stdio.h>
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
  return (*( int *)p1 - *(int *) p2);
}
int main()
{
    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    int i = 0;
    
    qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
    for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
   {
       printf( "%d ", arr[i]);
   }
    printf("\n");
    return 0;
}

在这里插入图片描述
实现了排序功能,事实上,qsort 函数不仅可以对整形排序,也可以对字符排序。
数据的比较方式需要我们自己定义。

现在我们来模拟一下 qsort 函数的实现

#include <stdio.h>

//升序

void swap(char* buf1, char* buf2,int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char temp = 0;
		temp = *buf1;
		*buf1 = *buf2;
		*buf2 = temp;
		buf1++;
		buf2++;
	}
}

int cmp(const void* e1,const void* e2)
{
	return *((int*)e1) - *((int*)e2);
}

void my_bubble_sort (void* base,int num,int width,int (*cmp) (const void* e1,const void* e2))
{
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if ((cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0))
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}

void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ",arr[i]);
	}
	
}


void test()
{
	int arr[] = { 10,5,6,1,3,1,6,0,7,9 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	my_bubble_sort(arr,sz,sizeof(arr[0]),cmp);
	print(arr,sz);
}

int main()
{
	test();

	return 0;
}

在这里插入图片描述

注:本次学习就暂时结束啦,文章中有错误、不足之处欢迎大佬指正,让我们共同学习,共同进步,欲戴王冠,必承其重,加油!

【C语言进阶】玩转指针——指针的高阶玩法! 文章目录前言一、字符指针二、指针数组1.引入库2.读入数据总结 前言 指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念: 1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间。 2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。 3. 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。 4. 指针的运算 这个章节,我们继续探讨指针的高级主题 一、字符指针 在指针的类型中我们知道有一种指针类型为字符指针 char* 。 思考以下. 阅读详情

相关推荐

代码详解——Transformer

对于一个序列,在 time_step 为 t 的时刻,我们的解码输出应该只能依赖于 t 时刻之前的输出,而不能依赖 t 之后的输出。当 Decoder 的输入矩阵和 Mask 矩阵输入矩阵包含 “ I have a cat” (0, 1, 2, 3, 4) 五个单词的表示向量,Mask 是一个 5×5 的矩阵。然后判断是否传入的mask, 如果有mask (mask参数值不为None),则把mask为0的位置,将对应位置的attn的值设为无穷小的负数。可以发现得到的输出和输入的K,Q,V的大小相同。

zyw2002的博客 7211

C++随手笔记(四)char字符、字符数组、字符串、字符串指针、string类型、const char * 、char const * 与 char * const

1、字符与字符串 字符型常量是用单引号包含起来的一个字符,如‘a’, ‘b’; 字符串常量是用一对双引号包含起来的一堆字符,如"a"、”Hello word!" 等; ‘a’与“a”的区别: 字符 ‘a’ 在内存中占1字节, “a” 在内存中占2字节。“a” 的最后一个字符为’\0‘ ’\0‘ 是一个转义字符,标记字符串内容结束 变量定义与赋值: char c; c = 'a'; //正确 c = "a"; //错误,不能把一个字符串赋值给一个字符型变量 char cc[] = "a"; //正确

m0_50990607的博客 2763

如何用 OBProxy 实现 OceanBase 的最佳路由策略

本文基于业务中一类常见的路由问题展开,对OceanBase的路由功能及对应配置项做详细介绍,指导用户在业务实践中如何正确的配置路由策略及快速排查路由问题

OceanBase分布式数据库的博客 1682

C语言----指针

注意 避免访问未初始化的指针 可能会修改其他位置的内存的代码 左值和右值 左值指用于识别和定位一个存储位置的标识符。(注意:左值同时还必须是可以改变的) #include <stdio.h> int main() { int a = 5; ++(a++); return 0; } 这里(a++)是先将变量a的值(5)作为整...

weixin_39558227的博客 580

解决倍福ADS通信错误1861和6:一次完整的TwinCAT3路由配置与故障排查实录

本文详细解析了在Linux系统上解决倍福ADS通信错误1861和6的完整流程,包括TwinCAT3路由配置与故障排查。通过实例演示了如何正确配置AMS NetID、使用adstool工具添加静态路由,以及高级网络抓包分析技巧,帮助工程师快速定位和解决通信问题。

weixin_30875157的博客 520

告别ZotFile!Zotero 7+百度网盘文献同步全攻略(含软链接避坑指南)

本文针对Zotero 7用户,提供了告别ZotFile插件后,利用百度网盘实现文献附件同步的完整解决方案。详细讲解了如何通过设置Zotero链接目录与创建软链接,将附件库迁移至云盘,并附有详细的避坑指南,确保数据安全与多设备同步的稳定性。

mmm90的博客 586

C语言指针和指针变量-学习笔记(九)

一、基本概念 1、地址 (1)内存(内部存储器):由存储单元组成,其特点是存储单元线性连续。存储单元的最小单位是字节。 (2)地址:内存区的每一个字节有一个编号, “地址” 。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。 (3)变量的地址:变量在内存中总占用几个连续的字节,开始字节的地址,就是变量的地址。 2、变量的访问方式 (1)直接访问:按变量i地址存取变量值的方式(按变量名访问变量); 如: printf("%d\n",i); scanf("%d",&i);

微末观星 1070

如何实现C++动态调用动态链接库(DLL或SO)

使用函数指针来定义函数的类型,以便在动态库中找到的函数能够正确地调用。函数指针的类型必须与函数的签名(参数类型和返回类型)匹配。// 函数指针#ifdef _MSC_VER // Windows环境下的导出声明__declspec。

weixin_48438353的博客 7984

C语言-指针进阶

指针进阶

weixin_53908084的博客 728

避坑指南:Zabbix poller进程100%占用的5种常见原因及解决方案

本文深入剖析了Zabbix poller进程占用率高达100%的五大常见原因,包括监控项与进程数不匹配、不可达主机资源浪费、数据库性能瓶颈、监控项配置不合理以及版本缺陷。文章提供了从系统化诊断到针对性解决方案的完整指南,并重点介绍了如何通过优化zabbix_server.conf中的StartPollers等关键参数进行有效调优,帮助运维人员从根本上解决这一经典性能问题。

7z8x2c4v6b9的博客 250

今日头条面试——iOS开发面试题

layoutIfNeeded和setNeedsLayout的区别。* Pod update和pod install的区别。* 10、autoreleasepool的使用场景。* 如何hook一个对象的方法,而不影响其它对象。* 数组cop后里面的元素会复制一份新的吗。* 介绍项目,主要介绍自己强项一点的地方。* assing可以使用在对象中吗。* isEquel和hash的关系。* 4、谈谈runtime的理解。* 8、谈谈runloop的理解。* copy和strong的区别。

FPGA/MATLAB学习教程/源码/项目合作开发 594

C语言指针进阶训练

对C语言指针的进一步学习,通过常见的进阶练习,能够帮助学者更加深刻的学习指针的实际操作与用法。

Hello_shuo的博客 2212

微信小程序 - 解决报错 operateAudio:fail audioInstance is not set(wx.getRecorderManager苹果ios真机调试运行,播放音频播放出错解决)

微信小程序,苹果ios系统,真机测试报错,播放音频报错operateAudio:fail audioInstance not exist,errCode: -1,小程序生成音频播放报错errMsg,真机调试报错开发者工具没问题,wx.getRecorderManager,音频实例,InnerAudioContext,各种方法都试了不行,只有ios苹果手机有这个问题,IOS 出现 operateAudio,在iOS无法播放的解决方案,更新到最新版本了还是不行,链接没有特殊字符,微信小程序全局音频设置,手机上播

🏆 前端领域优质创作者 1万+

【C语言进阶】指针的进阶【上篇】

本文讲解了指针的进阶中的字符指针,数组指针,指针数组,数组传参和指针传参等一系列的知识,图文并茂,让你可以看懂并且理解

你我皆为凡人的博客 1545

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

c语言指针进阶部分

我们在创建数组的时候,会给我们分配一块内存,同时这个数组有地址,地址就是首字符的地址,让我们去往里面去放东西,这一次我们放的是 hello world ,这个时候,我们又创建了一个数组,又会为我们分配一块内存,同时这个数组也有地址,让我们去往里面去放东西,这一次我们放的也是 hello world ,所以这两个数组里面的内容是一致的,但是地址不一样,我们比较的是数组名,数组名就是首元素的地址,就是比较地址。在这里我们打印的是函数的地址,我们可以看到加不加&取地址符号结果都是一样的,这是不是和数组差不多啊。

2402_84602321的博客 1869

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

C语言——进阶指针理解

函数调用约定定义了函数在调用过程中参数传递、堆栈管理和返回值处理的规则。它确保了调用方(Caller)和被调用方(Callee)对函数调用的底层细节达成一致,避免因规则不匹配导致的程序崩溃或数据损坏。

Dfuen的博客 960

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

【C语言进阶】C语言指针进阶实战:优化与难题解析

在C语言的浩瀚宇宙中,指针无疑是那颗最为璀璨而神秘的星辰。它既是连接数据与操作的桥梁,也是让许多初学者望而生畏的迷宫。一旦掌握了指针的精髓,C语言的世界便仿佛被彻底点亮,编程的效率和灵活性得以质的飞跃。然而,仅仅停留在指针的基本使用上,远不足以探索其全部魅力与力量。指针的进阶应用,尤其是如何通过指针优化程序性能、解决复杂难题,是每一位C语言开发者必须攀登的高峰

EterNity_TiMe_的计算机学习之路 3190

c语言进阶 指针

指针数组是一种特殊的数组,其核心特点在于每个数组元素都是一个指针变量,这些指针可以指向不同的数据类型。&arr的存储类型&arr(*p)表示 p 是指针(*p)[5]表示 p 指向包含 5 个元素的数组表示数组元素为 int// 正确:p是指向包含5个int的数组指针// 正确:p1指向首元素(类型int*)// 错误:类型不匹配(int* 与 int (*)[5])// 错误:必须指定数组大小int* p1[5];// 指针数组:包含5个int*的数组。

ztztz111的博客 1728

【C语言】指针进阶【万字详细版】

进阶的指针有指针数组、数组指针、函数指针、函数指针数组、指向函数指针数组的指针等等,让我们来探讨指针的高级主题。

小明的博客 2366

【C语言】指针进阶(1)

数组指针是指针?还是数组?答案是:指针。整形指针——指向整形变量的指针,存放整形变量地址的指针变量。字符指针——指向字符变量的指针,存放字符变量地址的指针变量。数组指针——指向数组的指针,存放的是数组的地址。下面代码哪个是数组指针?int (*p)[10]是数组指针。

m0_73648729的博客 980

C语言——指针从初阶到进阶

本文讨论了关于指针的相关问题,欢迎大家阅读指针

2501_90507065的博客 6155

C语言进阶之路-数组与指针

本文细讲了打怪路上的数组和指针的特点和消灭方法,各位只需认真学习,即可消灭它们。祝各位都可爬上C语巅峰,斩尽拦路小妖。

志不在此,打卡留念 2374

C语言进阶——指针进阶

指针就是地址,而凡是存储在内存中的值都会有属于自己的地址,指针指向地址,这样我们就能通过指针间接操作变量。我们在指针初阶中介绍了指针的基本概念:如指针大小、野指针问题、指针间的关系运算等,在我们的指针进阶中,将会对指针进行进一步剖析,见识更深的指针!🎊🎊......

北 海的博客 2588

【C语言】指针的进阶

]的优先级 高于 * ,所以 要加上()来保证p 先和 * 结合。

加油!!! 1097

C语言进阶之指针的进阶

指针的进阶及面试题讲解

立志做一个高产且细节博主 976
上一篇: 数据的存储
下一篇: 字符函数和字符串函数
Snakra
博客等级 码龄5年 240粉丝 18原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值