• 关于

    指针

    的搜索结果

回答

指针的本意是:在一个变量中保存另一个变量的地址,以提供将“地址”变量化的能力。如果没有指针,将无法用一个变量引用另一个变量(只能把变量的值拷贝一份赋给另一个变量)。C语言中提供了完善的指针操作,包括为指针赋值、内存分配(malloc)、取变量地址、让指针可以参与运算等,这使得C程序员能够任意操作可用内存。Java(Javascript)中也有指针,只不过与C相比,Java对程序员使用指针有着严格的限制,仅允许赋值操作,而且不是任意值,只能是通过new创建的对象引用或其他引用变量的值。不过Java一般不说指针,而是用引用(reference)来称呼指向对象的指针,不过,Java中仍然可以找到一些指针存在的影子,例如,当一个对象为null时调用方法会导致null pointer异常,即所谓的空指针错误,可见Java内部使用的确实是指针。很多基本的数据结构,例如链表、树、图等,都必须用指针来保存前驱或后继节点的地址,否则这些数据结构无法实现。如果一个语言不提供指针,虽然在理论上它也具备完整的计算能力,但很多在其他语言中非常简单的问题都将变得极其复杂(本来想举个例子的,但一时想不起了,不过这个结论肯定是正确的)。所以这个作者说的是对的,只是你需要理解指针的本质,不要错误地认为只有像C语言那样的指针才叫指针,真正的指针的概念请看我开头的那句。

a123456678 2019-12-02 02:41:19 0 浏览量 回答数 0

问题

“指针”是成熟的编程语言必须具有的概念吗?

a123456678 2019-12-01 19:50:57 898 浏览量 回答数 1

回答

1.从编译器的角度来解释,编译器不允许const指针赋值给非const指针,因此可知const指针不能隐式转换为非const指针。2.由1知,const指针q加上任意常数n,即q+n仍然为const指针类型(因为const指针允许本身发生移动或者改变,即q=q+n是允许的),而q[n]=*(q+n),因此一个const指针访问的整个数组都是不可修改的只读的值。

杨冬芳 2019-12-02 02:25:32 0 浏览量 回答数 0

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

回答

引用比指针会更安全。比如定义一个引用变量和一个指针变量:引用变量必须要这样:int b = 1;int &a = b;引用必须要指向一个已经存在的变量。而定义指针的时候: a可以直接定义,没有规定一定要初始化,所以可能出现: 也就是野指针的情况,造成内存泄露。所以指针定义之后一定要初始化一下,一般都是初始化为NULL空指针,在访问指针的时候也应该判断它是否是空指针,这样代码鲁棒性也就会更强啦

a123456678 2019-12-02 01:59:50 0 浏览量 回答数 0

回答

将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理,使用格式不对,dynamic_cast (expression)该运算符把expression转换成type-id类型的对象。Type-id 必须是类的指针、类的引用或者void*;如果 type-id 是类指针类型,那么expression也必须是一个指针,如果 type-id 是一个引用,那么 expression 也必须是一个引用。

xumaojun 2019-12-02 01:59:43 0 浏览量 回答数 0

回答

将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理,使用格式不对,dynamic_cast (expression)该运算符把expression转换成type-id类型的对象。Type-id 必须是类的指针、类的引用或者void*;如果 type-id 是类指针类型,那么expression也必须是一个指针,如果 type-id 是一个引用,那么 expression 也必须是一个引用。

nothingfinal 2019-12-02 01:59:43 0 浏览量 回答数 0

问题

迷你书下载 精彩片段: 恶名昭著的指针究竟是什么:报错

kun坤 2020-06-09 15:10:04 4 浏览量 回答数 1

回答

void即“无类型”,void *则为“无类型指针”,可以指向任何数据类型。由于void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值,因此还可以用void指针来作为函数形参,这样函数就可以接受任意数据类型的指针作为参数。例如:void memcpy( void dest, const void *src, size_t len );void memset( void buffer, int c, size_t num);具体可在google中搜索 “void 指针”查询用途和示例。

a123456678 2019-12-02 02:00:27 0 浏览量 回答数 0

回答

你这句话是错误的,你传的是数组名,数组名不等价于地址。 编译器用数组名标记数组的属性,比如具有确定数量的元素。而你说的地址,也就是指针,只是一个标量值。 只有当数组名在表达式中使用时,编译器才会为它产生一个指针常量。而只有以下两种情况,才不被当做指针常量: sizeof(数组名):返回数组长度(所占的字节数,不是数组元素个数),而不是指向数组的指针的长度。&数组名:产生一个指向数组的指针,而不是一个指向某个指针常量的指针。以上内容来源:《C和指针》P141~142

a123456678 2019-12-02 02:41:22 0 浏览量 回答数 0

回答

第一段程序你结构体指针没初始化, b根本就是的野指针嘛, 你程序能运行时因为野指针恰好没影响到程序的运行;第二段函数中返回结构体指针, main函数若是处理该给指针就属于越界操作.你通过malloc创建结构体, 会在内存中段空间就开辟了这段内存. 只要你没有free 这段内存就会一直存在, 因此在mian中访问该指针是合法的.

a123456678 2019-12-02 02:39:11 0 浏览量 回答数 0

问题

C语言指针作为参数的问题?报错

爱吃鱼的程序员 2020-06-22 22:50:18 1 浏览量 回答数 1

回答

要理解二级指针的用法,必须先从根本上理解它的含义。其实含义很简单,二级指针即指针的指针。比如说,内存地址A中存放了一个地址,那么A就是一个指针;内存地址B中恰好存放了内存地址A,那么B就是一个指针的指针,即二级指针。 根据这个思路,说两种最常见的二级指针用法。 1.动态申请二维数组。 C++中动态申请一维数组,一般形式为 T *arr_t = new T[N];也就是说,在堆上分配出N*sizeof(T)的空间,并让arr_t指向这块空间的起始地址。同理,动态申请二维数组的一般形式为: T *arr_t = new T[N];for (int i = 0; i < N; ++i) { arr_t[i] = new T[M];}这里的arr_t指向的是一个指针数组的起始位置。也就是说,先在堆上分配一块空间,这块空间里面存的全都是指针。然后,在for循环内,对于每个指针,都要在堆上重新开辟一块大小为M*sizeof(T)的空间,这块空间才真正存储我们想要的数据,然后让指针指向这块空间的起始地址。而那块叫做“arr_t”的内存里,实际上存储的是指针数组的起始位置。 2.用于返回参数。 参考一个MSDN上的例子,假设有一个接口叫IDrawable,同时,有一个叫做Shape的类实现了这个接口。再假设有个图形库里面有这样一个函数: HRESULT CreateShape(IDrawable** ppShape);这个函数用于创建一个Shape对象。现在问题来了,创建好的Shape对象总要返回吧?而这个函数又恰好需要返回一个表示成功或失败的、类型为HRESULT的代码,那么创建好的Shape让谁返回呢?显然,需要通过参数ppShape来返回。这个函数的使用场景如下: IDrawable *pShape; HRESULT hr = CreateShape(&pShape);if (SUCCEEDED(hr)){ // Use the Shape object. }else{ // An error occurred. }显然,pShape的作用就是指向堆上的某块空间,这个空间里存着一个Shape类型的对象。也就是说,我们之所以要把pShape传给函数CreateShape,就是想改变pShape的当前值(当前值可能为NULL,或者指向其他某个不确定的内存位置),让pShape真正指向堆上的Shape对象。于是,问题转化成“怎样真正改变传入函数的参数值”的问题。显然,在C++里,传引用或者传指针都能达到这个效果。这里用传指针的方法,如下图所示:

a123456678 2019-12-02 02:42:04 0 浏览量 回答数 0

回答

要理解二级指针的用法,必须先从根本上理解它的含义。其实含义很简单,二级指针即指针的指针。比如说,内存地址A中存放了一个地址,那么A就是一个指针;内存地址B中恰好存放了内存地址A,那么B就是一个指针的指针,即二级指针。 根据这个思路,说两种最常见的二级指针用法。 1.动态申请二维数组。 C++中动态申请一维数组,一般形式为 T *arr_t = new T[N];也就是说,在堆上分配出N*sizeof(T)的空间,并让arr_t指向这块空间的起始地址。同理,动态申请二维数组的一般形式为: T *arr_t = new T[N];for (int i = 0; i < N; ++i) { arr_t[i] = new T[M];}这里的arr_t指向的是一个指针数组的起始位置。也就是说,先在堆上分配一块空间,这块空间里面存的全都是指针。然后,在for循环内,对于每个指针,都要在堆上重新开辟一块大小为M*sizeof(T)的空间,这块空间才真正存储我们想要的数据,然后让指针指向这块空间的起始地址。而那块叫做“arr_t”的内存里,实际上存储的是指针数组的起始位置。 2.用于返回参数。 参考一个MSDN上的例子,假设有一个接口叫IDrawable,同时,有一个叫做Shape的类实现了这个接口。再假设有个图形库里面有这样一个函数: HRESULT CreateShape(IDrawable** ppShape);这个函数用于创建一个Shape对象。现在问题来了,创建好的Shape对象总要返回吧?而这个函数又恰好需要返回一个表示成功或失败的、类型为HRESULT的代码,那么创建好的Shape让谁返回呢?显然,需要通过参数ppShape来返回。这个函数的使用场景如下: IDrawable *pShape; HRESULT hr = CreateShape(&pShape);if (SUCCEEDED(hr)){ // Use the Shape object. }else{ // An error occurred. }显然,pShape的作用就是指向堆上的某块空间,这个空间里存着一个Shape类型的对象。也就是说,我们之所以要把pShape传给函数CreateShape,就是想改变pShape的当前值(当前值可能为NULL,或者指向其他某个不确定的内存位置),让pShape真正指向堆上的Shape对象。于是,问题转化成“怎样真正改变传入函数的参数值”的问题。显然,在C++里,传引用或者传指针都能达到这个效果。这里用传指针的方法,如下图所示:

a123456678 2019-12-02 02:41:33 0 浏览量 回答数 0

回答

不知道你对指针与指针,折腾啥异或操作,能说说道理吗。指针的与,或,经常用,不过那是指针和整型的玩。段错误,指针越界,跨平台不安全性。。。

杨冬芳 2019-12-02 02:59:49 0 浏览量 回答数 0

回答

快排的一趟称为一次划分,原因是一趟排序后,数组以基准元素X为界,左边的元素都小于等于X,右边的元素都大于等于X。 要做到这点:先刨去21,再设俩指针,一个指向最左边,一个指向最右边。左边指针的往右走,找一个大于等于21的元素,右边的指针往左走,找一个小于等于21的元素,然后俩指针的值交换。继续循环上面的过程。直到俩指针相遇或擦肩而过。把21交换到俩指针相遇的地方就可以了。 第一次交换25和9,然后俩指针相遇,把21和界限处的17交换,得到: 结果:17 9 5 21 25 23 30

小旋风柴进 2019-12-02 01:18:50 0 浏览量 回答数 0

回答

KMP 算法是一种字符串的模式匹配算法,参看严蔚敏数据结构一书,里面讲的很清楚。 基本的字符串匹配算法是将被匹配的字符串S和模式串T 逐个字符进行比较。例如:S中有10个字符,T中有5个字符。S串初始的匹配位置为3.则从S中的第3个字符与T中的第一个字符匹配,若相同则S的第4个字符与T中的第2个字符匹配。直到匹配成功或者出现失配字符。当出现失配情况下,移动标识S中当前进行比较的字符指针,会退到第4个字符处。然后,重复这一过程。简单说,基本的字符匹配算法是通过移动被匹配的字符串S,进行比较字符的指针位置来完成字符匹配的。 而KMP算法刚好相反,在整个匹配过程中S中当前比较字符的指针并不发生回退现象,当出现S中的字符与T中的字符失配的时候。通过改变T的当前比较字符位置的指针来确定当前S中的字符该与T中哪个字符进行比较。简单说,通过模式字符串T的当前比较字符的指针的回退来完成字符匹配。 当理解了KMP算法通过改变T的当前比较字符位置的指针来完成匹配时,接下来要理清的是模式字符串T中的字符指针在失配的情况下是如何移动的。 以严蔚敏数据结构一书中KMP为例,对于模式字符串T,KMP维护了一个对应于T中每个字符弱发生失配情况下,指针回退到哪一位置的数组。当被匹配串S与模式串T发生失配的情况下,T读取数组中相应记录的位置,讲指针回退。如果回退后仍然失配则S的当前比较字符位置指针+1,T串指针回到第一个字符处。 由此可见获取数组中存储的数据是KMP算法的关键,书中的公式看起来有点抽象。数组中的存储指针的位置是根据,模式串T与自身的匹配过程获取的。 实际上是说,模式串T的第一个字符,如果出现失配则不会回退;当前比较位置的字符向前N-1位的子串恰好与T中从第一个字符起止N-1个字符形成的子串相等,且N小于当前位置,满足这些条件的N的最大值即为T当前位置指针回退的位置,然后迭代此过程,直到T本身匹配或回退到第一个字符位置仍不匹配,则当前位置的对应的回退位置指针指向T中的第一个字符所在位置。 讲的还不是很清楚,主要是对比一下基本的字符匹配算法和KMP的不同。一个是通过移动被匹配字符串比较字符的指针来实现匹配,一个是移动模式字符串的当前比较字符的位置指针来实现匹配。对于匹配串字符回退位置这个计算书中已经很清楚,根据算法单步调试一次自然就理解了。

boxti 2019-12-02 01:26:08 0 浏览量 回答数 0

问题

从一个指针里检索数据问题

蛮大人123 2019-12-01 19:54:36 871 浏览量 回答数 1

回答

C/C++ 规定可以将任何类型的指针转化为void指针,void指针转换为任何类型的指针,所以我们可以将type 转化为void ,然后将void转化为char进行memcpy。若是入参定义为char,对于编译器不能隐身转化的类型需要在调用的地方进行指针类型强转为char。memcpy要做的是bit-wise的拷贝,所以需要逐位的去拷贝。char类型只占一个byte的空间,所以选择char来实现。参数列表使用void*是为了表达该参数可以是指向任意类型的指针。

a123456678 2019-12-02 02:01:57 0 浏览量 回答数 0

问题

字符数组的指针概念的理解,望讨论。

a123456678 2019-12-01 19:48:29 969 浏览量 回答数 1

回答

你给的例子里面确实没有修改mArrayCount这个局部变量啊。mArrayCount是一个指针,指向一个可变长度的数组。在block里面,并没有修改这个指针,而是修改了这个指针指向的数组。换句话说,mArrayCount是一个整数,保存的是一块内存区域的地址,在block里,并没有改变这个地址,而是读取出这个地址,然后去操作这块地址空间的内容。这是允许的,因为声明block的时候实际上是把当时的临时变量又复制了一份,在block里即使修改了这些复制的变量,也不影响外面的原始变量。即所谓的闭包。但是当变量是一个指针的时候,block里只是复制了一份这个指针,两个指针指向同一个地址。所以,在block里面对指针指向内容做的修改,在block外面也一样生效。

a123456678 2019-12-02 03:12:50 0 浏览量 回答数 0

回答

如果p是个一级指针 *p 则可以取出如果p是个二级指针则 **p //此时括号非必须还有需要看上下文 如果是函数指针的话,此时括号是需要的比如 int (p) (int x); / 声明一个函数指针 */p=function; / 将function函数的首地址赋给指针p /

a123456678 2019-12-02 02:39:22 0 浏览量 回答数 0

回答

c_str()返回的是const char*指针,所以不能用该指针改变其指向的对象。例如你的string对象是1234,你希望修改成2234,那你是不能用string函数的c_str返回值来修改它的。不是string对象在调用c_str后会被析构,而是string对象在其作用期结束的时候被析构(无论是否调用c_str)。但如果你用c_str获得了一个指针,而这个指针的作用期比string对象长,那么在string被析构后这个指针还存在,而指针指向的位置已经无效了。这就会出问题。大概是这样: const char func(){ string s = "1234"; return s.c_str(); } 这个函数是不正确的,因为在func返回的时候,s会被析构。所以你返回的const char指针,指向了一个无效区域。(注意,这个无效区域可能暂时没有被新数据覆盖,所以你在函数返回后立刻访问这个指针对象,不见得会出错,但这个区域迟早会被覆盖的。)

a123456678 2019-12-02 02:01:45 0 浏览量 回答数 0

回答

<p>你的代码没错。你所指的报错,是不会出现的。*avgr就是指向了“开源中国”字符串的首地址。以下代码为证。</p> prgv 是 字符指针数组。 数组中的每一个指针,指向一个给定的字符串。比如,prgv[ 1 ] 指向字符串"hello"。avgr 是一个指向字符指针的指针,保存着一个字符(串)指针的地址。这个地址,可以从 *avgr 得到。*avgr =prgv[ 0 ]; 代码没有错误。执行这行代码之后,字符串 "开源中国" 的地址,即此串的首地址,就赋予 *avgr。  #include<stdio.h> int main(){ char *s ="开源中国"; char *prgv[]= {"开源中国","hello","world",NULL}; //prgv 是字符指针数组。 char **avgr; //argr 是一个指向字符指针的指针 *avgr =prgv[ 0 ]; /*没错,*avgr 的类型是字符指针,prgv[ 0 ]的类型也是字符指针型。 执行此行代码,使得 *avgr 指向 prgv[ 0 ] 所指的字符串"开源中国"。*/ printf("%s\n", *avgr); //输出 prgv[ 0 ]所指的字符串 "开源中国" return 0; } 输出:开源中国 <div class="ref"> 引用来自“tcxu”的评论 你的代码没错。你所指的报错,是不会出现的。*avgr就是指向了“开源中国”字符串的首地址。以下代码为证。 prgv 是 字符指针数组。 数组中的每一个指针,指向一个给定的字符串。比如,prgv[ 1 ] 指向字符串"hello"。avgr 是一个指向字符指针的指针,保存着一个字符(串)指针的地址。这个地址,可以从 *avgr 得到。*avgr =prgv[ 0 ]; 代码没有错误。执行这行代码之后,字符串 "开源中国" 的地址,即此串的首地址,就赋予 *avgr。  #include<stdio.h> int main(){ char *s ="开源中国"; char *prgv[]= {"开源中国","hello","world",NULL}; //prgv 是字符指针数组。 char **avgr; //argr 是一个指向字符指针的指针 *avgr =prgv[ 0 ]; /*没错,*avgr 的类型是字符指针,prgv[ 0 ]的类型也是字符指针型。 执行此行代码,使得 *avgr 指向 prgv[ 0 ] 所指的字符串"开源中国"。*/ printf("%s\n", *avgr); //输出 prgv[ 0 ]所指的字符串 "开源中国" return 0; } 输出:开源中国 在我这里,你我的两套代码都没报错。我的环境是:Dev-C++。

爱吃鱼的程序员 2020-06-06 09:46:10 0 浏览量 回答数 0

回答

数组是数组,指针是指针,但是在大多数情况下,数组名称会转换为指针。经常使用的术语是它们衰减到指针。 这是一个数组: int a[7]; a 包含七个整数的空间,您可以通过赋值在其中一个中放置一个值,如下所示: a[3] = 9; 这是一个指针: int *p; p不含整数的空格,但可以指向整数的空格。例如,我们可以将其设置为指向数组中的一个位置a,例如第一个位置: p = &a[0]; 令人困惑的是,您也可以这样写: p = a; 这并没有数组的内容复制a到指针p(无论这将意味着)。而是将数组名称a转换为指向其第一个元素的指针。因此该分配与上一个分配相同。 现在,您可以p以类似于数组的方式使用: p[3] = 17; 这样做的原因是C中的数组解引用运算符[ ]是根据指针定义的。x[y]方法:先从指针x,步y以后有什么指针指向元素向前,然后采取一切是存在的。使用指针算术语法,x[y]也可以写成*(x+y)。 为了使其与普通数组(例如our)一起使用a,必须首先将ain中的名称a[3]转换为指针(指向中的第一个元素a)。然后,我们将3个元素向前移动,并采用其中的任何内容。换句话说:将元素放在数组中的位置3。(这是数组中的第四个元素,因为第一个元素的编号为0。) 因此,总而言之,在大多数情况下,C程序中的数组名称都将转换为指针。一个例外是当我们sizeof在数组上使用运算符时。如果a在这种情况下被转换为指针,sizeof a则将给出指针的大小,而不是实际数组的大小,这将毫无用处,因此在这种情况下a意味着数组本身。 问题来源于stack overflow

保持可爱mmm 2020-01-16 16:49:53 0 浏览量 回答数 0

回答

C语言里int a[10]的话,a是一个指针指向a[0]的指针C语言里int a2的话,a[0]是一个指向a0的指针,a[1]是一个指向a1的指针在你的代码里,int a2 *a指向a0这个inta指向a[0] = {1, 2, 3}这个array。只不过在C里面,array的地址就是array第一个元素的地址,所以在这里a = a[0] = a0a是*int[3]的指针,对其进行+1操作,内存指针往后移动sizeof(int[3])个位置*a是int指针,对其进行+1操作,内存指针往后移动sizeof(int)个位置你可以运行下面代码测试下 #include<stdio.h> int main(){ int a[2][3] = { {1, 2, 3}, {4, 5, 6} }; printf("%p\n", a); printf("%p\n", a+1); printf("%p\n", (*a+1)); return 0; }

西秦说云 2019-12-02 02:34:42 0 浏览量 回答数 0

回答

参考答案: 我们可以使用两个指针而不是一个指针。第一个指针从列表的开头向前移动 n+1 步,而第二个指针将从列表的开头出发。现在,这两个指针被 n 个结点分开。我们通过同时移动两个指针向前来保持这个恒定的间隔,直到第一个指针到达最后一个结点。此时第二个指针将指向从最后一个结点数起的第 n 个结点。我们重新链接第二个指针所引用的结点的 next 指针指向该结点的下下个结点。 参考代码: { ListNode dummy = new ListNode(0); dummy.next = head; ListNode first = dummy; ListNode second = dummy; // Advances first pointer so that the gap between first and second is n nodes apart for (int i = 1; i <= n + 1; i++) { first = first.next; } // Move first to the end, maintaining the gap while (first != null) { first = first.next; second = second.next; } second.next = second.next.next; return dummy.next; } 复杂度分析: 时间复杂度:O(L),该算法对含有 L 个结点的列表进行了一次遍历。因此时间复杂度为 O(L)。 空间复杂度:O(1),我们只用了常量级的额外空间。

Runt 2020-04-14 18:27:34 0 浏览量 回答数 0

回答

const 类型 * 变量名:可以改变指针的指向,不能改变指针指向的内容.类型 * const 变量名:可以改变指针指向的内容,不能改变指针的指向。

a123456678 2019-12-02 03:13:16 0 浏览量 回答数 0

问题

【算法】五分钟算法小知识:双指针技巧总结

游客ih62co2qqq5ww 2020-05-08 14:25:40 19 浏览量 回答数 1

回答

内置函数operator+可以将指针类型作为其操作数,因此将数组传递s给它会导致数组到指针的转换,然后int*返回指针。这意味着您可能会+s单独使用以获取指针。(在这种情况下,它是多余的;没有operator+它,它也会衰减到指针,然后分配给p。) (强调我的) 内置的一元加运算符返回其操作数的值。它不是空操作的唯一情况是操作数具有整数类型或无作用域枚举类型(通过整数提升来更改),例如,将char转换为int或如果操作数受左值到右值的约束,数组到指针或函数到指针的转换

保持可爱mmm 2020-02-09 13:16:36 0 浏览量 回答数 0

回答

因为楼主基类的display函数不是虚函数C++重要性质:1、如果你以一个"基类之指针"指向一个"派生类之对象",那么经由该指针你只能调用该基类所定义的函数2、如果你以一个“派生类之指针”指向一个“基类之对象”,你必须先做明显的转型操作(explicit cast),这种作法很危险。B b = (B)&A_obj;3、如果基类和派生类都定义了“相同名称之函数”,那么通过对象指针调用成员函数时,到底调用了那个函数,必须视该指针的原始类型(B)而定,而不是视指针实际所指的对象的类型而定,这与第1点其实意义相通

a123456678 2019-12-02 02:01:46 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站